diff --git a/Assets/Scripts/Interactions/CombineWithBehavior.cs b/Assets/Scripts/Interactions/CombineWithBehavior.cs index bc3039b6..84ac49bc 100644 --- a/Assets/Scripts/Interactions/CombineWithBehavior.cs +++ b/Assets/Scripts/Interactions/CombineWithBehavior.cs @@ -13,7 +13,7 @@ public class CombineWithBehavior : InteractionRequirementBase /// True if the combination was successful, false otherwise. public override bool TryInteract(FollowerController follower) { - var heldItem = follower.currentlyHeldItem; + var heldItem = follower.CurrentlyHeldItem; var pickup = GetComponent(); if (heldItem == null) { diff --git a/Assets/Scripts/Interactions/RequiresItemBehavior.cs b/Assets/Scripts/Interactions/RequiresItemBehavior.cs index 401c7db0..2368f3e4 100644 --- a/Assets/Scripts/Interactions/RequiresItemBehavior.cs +++ b/Assets/Scripts/Interactions/RequiresItemBehavior.cs @@ -16,7 +16,7 @@ public class RequiresItemBehavior : InteractionRequirementBase /// True if the interaction was successful, false otherwise. public override bool TryInteract(FollowerController follower) { - var heldItem = follower.currentlyHeldItem; + var heldItem = follower.CurrentlyHeldItem; if (heldItem == requiredItem) { OnSuccess?.Invoke(); diff --git a/Assets/Scripts/Interactions/SlotItemBehavior.cs b/Assets/Scripts/Interactions/SlotItemBehavior.cs index 3cb421a0..afaf88ce 100644 --- a/Assets/Scripts/Interactions/SlotItemBehavior.cs +++ b/Assets/Scripts/Interactions/SlotItemBehavior.cs @@ -26,7 +26,7 @@ public class SlotItemBehavior : InteractionRequirementBase /// True if the interaction was successful, false otherwise. public override bool TryInteract(FollowerController follower) { - var heldItem = follower.currentlyHeldItem; + var heldItem = follower.CurrentlyHeldItem; var pickup = GetComponent(); var slotItem = pickup != null ? pickup.itemData : null; var config = GameManager.Instance.GetSlotItemConfig(slotItem); diff --git a/Assets/Scripts/Movement/FollowerController.cs b/Assets/Scripts/Movement/FollowerController.cs index 73d77dfa..6d13d06e 100644 --- a/Assets/Scripts/Movement/FollowerController.cs +++ b/Assets/Scripts/Movement/FollowerController.cs @@ -1,6 +1,7 @@ using UnityEngine; using Pathfinding; using UnityEngine.SceneManagement; +using Utils; /// /// Controls the follower character, including following the player, handling pickups, and managing held items. @@ -18,38 +19,28 @@ public class FollowerController : MonoBehaviour /// public float manualMoveSmooth = 8f; - private Transform playerTransform; - private AIPath playerAIPath; - private AIPath aiPath; - private Vector3 targetPoint; - private float timer; - private bool isManualFollowing = true; - private Vector3 lastMoveDir = Vector3.right; - private float currentSpeed = 0f; - - private Animator animator; - private Transform artTransform; - - [Header("Held Item")] - /// - /// The item currently held by the follower. - /// - public PickupItemData currentlyHeldItem; + private Transform _playerTransform; + private AIPath _playerAIPath; + private AIPath _aiPath; + private Vector3 _targetPoint; + private float _timer; + private bool _isManualFollowing = true; + private Vector3 _lastMoveDir = Vector3.right; + private float _currentSpeed = 0f; + private Animator _animator; + private Transform _artTransform; + + private PickupItemData _currentlyHeldItem; + public PickupItemData CurrentlyHeldItem => _currentlyHeldItem; /// /// Renderer for the held item icon. /// public SpriteRenderer heldObjectRenderer; - /// - /// Desired height for held item icon. - /// - public float heldIconDisplayHeight = 2.0f; - private bool isReturningToPlayer = false; - - // Speed fields for follower - private float playerMaxSpeed = 5f; - private float followerMaxSpeed = 6f; - private float defaultFollowerMaxSpeed = 6f; + private bool _isReturningToPlayer = false; + private float _playerMaxSpeed = 5f; + private float _followerMaxSpeed = 6f; + private float _defaultFollowerMaxSpeed = 6f; // Pickup events public delegate void FollowerPickupHandler(); @@ -61,14 +52,14 @@ public class FollowerController : MonoBehaviour /// Event fired when the follower returns to the player after a pickup. /// public event FollowerPickupHandler OnPickupReturned; - private Coroutine pickupCoroutine; + private Coroutine _pickupCoroutine; - private bool lastInteractionSuccess = true; + private bool _lastInteractionSuccess = true; /// /// Cache for the currently picked-up GameObject (hidden while held). /// - private GameObject cachedPickupObject = null; + private GameObject _cachedPickupObject = null; /// /// Set to true if the follower just combined items. @@ -77,16 +68,16 @@ public class FollowerController : MonoBehaviour void Awake() { - aiPath = GetComponent(); + _aiPath = GetComponent(); // Find art prefab and animator - artTransform = transform.Find("CharacterArt"); - if (artTransform != null) + _artTransform = transform.Find("CharacterArt"); + if (_artTransform != null) { - animator = artTransform.GetComponent(); + _animator = _artTransform.GetComponent(); } else { - animator = GetComponentInChildren(); // fallback + _animator = GetComponentInChildren(); // fallback } } @@ -108,102 +99,102 @@ public class FollowerController : MonoBehaviour void UpdateFollowTarget() { - if (playerTransform == null) + if (_playerTransform == null) { FindPlayerReference(); - if (playerTransform == null) + if (_playerTransform == null) return; } - if (isManualFollowing) + if (_isManualFollowing) { - Vector3 playerPos = playerTransform.position; + Vector3 playerPos = _playerTransform.position; Vector3 moveDir = Vector3.zero; - if (playerAIPath != null && playerAIPath.velocity.magnitude > 0.01f) + if (_playerAIPath != null && _playerAIPath.velocity.magnitude > 0.01f) { - moveDir = playerAIPath.velocity.normalized; - lastMoveDir = moveDir; + moveDir = _playerAIPath.velocity.normalized; + _lastMoveDir = moveDir; } else { - moveDir = lastMoveDir; + moveDir = _lastMoveDir; } // Use GameSettings for followDistance - targetPoint = playerPos - moveDir * GameManager.Instance.FollowDistance; - targetPoint.z = 0; - if (aiPath != null) + _targetPoint = playerPos - moveDir * GameManager.Instance.FollowDistance; + _targetPoint.z = 0; + if (_aiPath != null) { - aiPath.enabled = false; + _aiPath.enabled = false; } } } void Update() { - if (playerTransform == null) + if (_playerTransform == null) { FindPlayerReference(); - if (playerTransform == null) + if (_playerTransform == null) return; } - timer += Time.deltaTime; - if (timer >= GameManager.Instance.FollowUpdateInterval) + _timer += Time.deltaTime; + if (_timer >= GameManager.Instance.FollowUpdateInterval) { - timer = 0f; + _timer = 0f; UpdateFollowTarget(); } - if (isManualFollowing) + if (_isManualFollowing) { Vector2 current2D = new Vector2(transform.position.x, transform.position.y); - Vector2 target2D = new Vector2(targetPoint.x, targetPoint.y); + Vector2 target2D = new Vector2(_targetPoint.x, _targetPoint.y); float dist = Vector2.Distance(current2D, target2D); - float minSpeed = followerMaxSpeed * 0.3f; + float minSpeed = _followerMaxSpeed * 0.3f; float lerpFactor = GameManager.Instance.ManualMoveSmooth * Time.deltaTime; float targetSpeed = 0f; if (dist > GameManager.Instance.StopThreshold) { if (dist > GameManager.Instance.ThresholdFar) { - targetSpeed = followerMaxSpeed; + targetSpeed = _followerMaxSpeed; } else if (dist > GameManager.Instance.ThresholdNear && dist <= GameManager.Instance.ThresholdFar) { - targetSpeed = followerMaxSpeed; + targetSpeed = _followerMaxSpeed; } else if (dist > GameManager.Instance.StopThreshold && dist <= GameManager.Instance.ThresholdNear) { targetSpeed = minSpeed; } - currentSpeed = Mathf.Lerp(currentSpeed, targetSpeed, lerpFactor); + _currentSpeed = Mathf.Lerp(_currentSpeed, targetSpeed, lerpFactor); if (dist > GameManager.Instance.StopThreshold && dist <= GameManager.Instance.ThresholdNear) { - currentSpeed = Mathf.Max(currentSpeed, minSpeed); + _currentSpeed = Mathf.Max(_currentSpeed, minSpeed); } - Vector3 dir = (targetPoint - transform.position).normalized; - transform.position += dir * currentSpeed * Time.deltaTime; + Vector3 dir = (_targetPoint - transform.position).normalized; + transform.position += dir * _currentSpeed * Time.deltaTime; } else { - currentSpeed = 0f; + _currentSpeed = 0f; } } - if (isReturningToPlayer && aiPath != null && aiPath.enabled && playerTransform != null) + if (_isReturningToPlayer && _aiPath != null && _aiPath.enabled && _playerTransform != null) { - aiPath.destination = playerTransform.position; + _aiPath.destination = _playerTransform.position; } - if (animator != null) + if (_animator != null) { float normalizedSpeed = 0f; - if (isManualFollowing) + if (_isManualFollowing) { - normalizedSpeed = currentSpeed / followerMaxSpeed; + normalizedSpeed = _currentSpeed / _followerMaxSpeed; } - else if (aiPath != null) + else if (_aiPath != null) { - normalizedSpeed = aiPath.velocity.magnitude / followerMaxSpeed; + normalizedSpeed = _aiPath.velocity.magnitude / _followerMaxSpeed; } - animator.SetFloat("Speed", Mathf.Clamp01(normalizedSpeed)); + _animator.SetFloat("Speed", Mathf.Clamp01(normalizedSpeed)); } } @@ -212,19 +203,19 @@ public class FollowerController : MonoBehaviour GameObject playerObj = GameObject.FindGameObjectWithTag("Player"); if (playerObj != null) { - playerTransform = playerObj.transform; - playerAIPath = playerObj.GetComponent(); - if (playerAIPath != null) + _playerTransform = playerObj.transform; + _playerAIPath = playerObj.GetComponent(); + if (_playerAIPath != null) { - playerMaxSpeed = playerAIPath.maxSpeed; - defaultFollowerMaxSpeed = playerMaxSpeed; - followerMaxSpeed = playerMaxSpeed * GameManager.Instance.FollowerSpeedMultiplier; + _playerMaxSpeed = _playerAIPath.maxSpeed; + _defaultFollowerMaxSpeed = _playerMaxSpeed; + _followerMaxSpeed = _playerMaxSpeed * GameManager.Instance.FollowerSpeedMultiplier; } } else { - playerTransform = null; - playerAIPath = null; + _playerTransform = null; + _playerAIPath = null; } } @@ -235,12 +226,12 @@ public class FollowerController : MonoBehaviour /// The world position to move to. public void GoToPoint(Vector2 worldPosition) { - isManualFollowing = false; - if (aiPath != null) + _isManualFollowing = false; + if (_aiPath != null) { - aiPath.enabled = true; - aiPath.maxSpeed = followerMaxSpeed; - aiPath.destination = new Vector3(worldPosition.x, worldPosition.y, 0); + _aiPath.enabled = true; + _aiPath.maxSpeed = _followerMaxSpeed; + _aiPath.destination = new Vector3(worldPosition.x, worldPosition.y, 0); } } @@ -252,36 +243,26 @@ public class FollowerController : MonoBehaviour /// The transform of the player. public void GoToPointAndReturn(Vector2 itemPosition, Transform playerTransform) { - if (pickupCoroutine != null) - StopCoroutine(pickupCoroutine); - if (aiPath != null) - aiPath.maxSpeed = followerMaxSpeed; - pickupCoroutine = StartCoroutine(PickupSequence(itemPosition, playerTransform)); + if (_pickupCoroutine != null) + StopCoroutine(_pickupCoroutine); + if (_aiPath != null) + _aiPath.maxSpeed = _followerMaxSpeed; + _pickupCoroutine = StartCoroutine(PickupSequence(itemPosition, playerTransform)); } /// - /// Set the item held by the follower. + /// Set the item held by the follower, copying all visual properties from the Pickup's SpriteRenderer. /// /// The item data to set. - public void SetHeldItem(PickupItemData itemData) + /// The SpriteRenderer from the Pickup to copy appearance from. + public void SetHeldItem(PickupItemData itemData, SpriteRenderer pickupRenderer = null) { - currentlyHeldItem = itemData; + _currentlyHeldItem = itemData; if (heldObjectRenderer != null) { - if (currentlyHeldItem != null && currentlyHeldItem.mapSprite != null) + if (_currentlyHeldItem != null && pickupRenderer != null) { - heldObjectRenderer.sprite = currentlyHeldItem.mapSprite; - heldObjectRenderer.enabled = true; - // Scale held icon to fixed height, preserve aspect ratio - var sprite = currentlyHeldItem.mapSprite; - float spriteHeight = sprite.bounds.size.y; - float spriteWidth = sprite.bounds.size.x; - if (spriteHeight > 0f) - { - float scaleY = heldIconDisplayHeight / spriteHeight; - float scaleX = scaleY * (spriteWidth / spriteHeight); - heldObjectRenderer.transform.localScale = new Vector3(scaleX, scaleY, 1f); - } + AppleHillsUtils.CopySpriteRendererProperties(pickupRenderer, heldObjectRenderer); } else { @@ -297,18 +278,18 @@ public class FollowerController : MonoBehaviour /// True if the last interaction was successful, false otherwise. public void SetInteractionResult(bool success) { - lastInteractionSuccess = success; + _lastInteractionSuccess = success; } private System.Collections.IEnumerator PickupSequence(Vector2 itemPosition, Transform playerTransform) { - isManualFollowing = false; - isReturningToPlayer = false; - if (aiPath != null) + _isManualFollowing = false; + _isReturningToPlayer = false; + if (_aiPath != null) { - aiPath.enabled = true; - aiPath.maxSpeed = followerMaxSpeed; - aiPath.destination = new Vector3(itemPosition.x, itemPosition.y, 0); + _aiPath.enabled = true; + _aiPath.maxSpeed = _followerMaxSpeed; + _aiPath.destination = new Vector3(itemPosition.x, itemPosition.y, 0); } // Wait until follower reaches item (2D distance) while (Vector2.Distance(new Vector2(transform.position.x, transform.position.y), new Vector2(itemPosition.x, itemPosition.y)) > GameManager.Instance.StopThreshold) @@ -317,7 +298,7 @@ public class FollowerController : MonoBehaviour } OnPickupArrived?.Invoke(); // Only perform pickup/swap logic if interaction succeeded - if (lastInteractionSuccess && heldObjectRenderer != null) + if (_lastInteractionSuccess && heldObjectRenderer != null) { Collider2D[] hits = Physics2D.OverlapCircleAll(itemPosition, 0.2f); foreach (var hit in hits) @@ -334,56 +315,56 @@ public class FollowerController : MonoBehaviour if (justCombined) { // Combination: just destroy the pickup, don't spawn anything - if (cachedPickupObject != null) + if (_cachedPickupObject != null) { - Destroy(cachedPickupObject); - cachedPickupObject = null; + Destroy(_cachedPickupObject); + _cachedPickupObject = null; } GameObject.Destroy(pickup.gameObject); justCombined = false; break; } // Swap logic: if holding an item, drop it here - if (currentlyHeldItem != null && cachedPickupObject != null) + if (_currentlyHeldItem != null && _cachedPickupObject != null) { // Drop the cached object at the pickup's position - cachedPickupObject.transform.position = pickup.transform.position; - cachedPickupObject.transform.SetParent(null); - cachedPickupObject.SetActive(true); - cachedPickupObject = null; + _cachedPickupObject.transform.position = pickup.transform.position; + _cachedPickupObject.transform.SetParent(null); + _cachedPickupObject.SetActive(true); + _cachedPickupObject = null; } - SetHeldItem(pickup.itemData); + SetHeldItem(pickup.itemData, pickup.iconRenderer); // Cache and hide the picked up object - cachedPickupObject = pickup.gameObject; - cachedPickupObject.SetActive(false); - cachedPickupObject.transform.SetParent(this.transform); + _cachedPickupObject = pickup.gameObject; + _cachedPickupObject.SetActive(false); + _cachedPickupObject.transform.SetParent(this.transform); break; } } } // Wait briefly, then return to player yield return new WaitForSeconds(0.2f); - if (aiPath != null && playerTransform != null) + if (_aiPath != null && playerTransform != null) { - aiPath.maxSpeed = followerMaxSpeed; - aiPath.destination = playerTransform.position; + _aiPath.maxSpeed = _followerMaxSpeed; + _aiPath.destination = playerTransform.position; } - isReturningToPlayer = true; + _isReturningToPlayer = true; // Wait until follower returns to player (2D distance) while (playerTransform != null && Vector2.Distance(new Vector2(transform.position.x, transform.position.y), new Vector2(playerTransform.position.x, playerTransform.position.y)) > GameManager.Instance.StopThreshold) { yield return null; } - isReturningToPlayer = false; + _isReturningToPlayer = false; OnPickupReturned?.Invoke(); // Reset follower speed to normal after pickup - followerMaxSpeed = defaultFollowerMaxSpeed; - if (aiPath != null) - aiPath.maxSpeed = followerMaxSpeed; - isManualFollowing = true; - if (aiPath != null) - aiPath.enabled = false; - pickupCoroutine = null; + _followerMaxSpeed = _defaultFollowerMaxSpeed; + if (_aiPath != null) + _aiPath.maxSpeed = _followerMaxSpeed; + _isManualFollowing = true; + if (_aiPath != null) + _aiPath.enabled = false; + _pickupCoroutine = null; } void OnDrawGizmos() @@ -391,9 +372,9 @@ public class FollowerController : MonoBehaviour if (debugDrawTarget && Application.isPlaying) { Gizmos.color = Color.cyan; - Gizmos.DrawSphere(targetPoint, 0.2f); + Gizmos.DrawSphere(_targetPoint, 0.2f); Gizmos.color = Color.yellow; - Gizmos.DrawLine(transform.position, targetPoint); + Gizmos.DrawLine(transform.position, _targetPoint); } } } diff --git a/Assets/Scripts/Utils.meta b/Assets/Scripts/Utils.meta new file mode 100644 index 00000000..0aa6bdce --- /dev/null +++ b/Assets/Scripts/Utils.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4c0a166b16da465792751b490744b095 +timeCreated: 1757337202 \ No newline at end of file diff --git a/Assets/Scripts/Utils/AppleHillsUtils.cs b/Assets/Scripts/Utils/AppleHillsUtils.cs new file mode 100644 index 00000000..2fa3f527 --- /dev/null +++ b/Assets/Scripts/Utils/AppleHillsUtils.cs @@ -0,0 +1,32 @@ +using UnityEngine; + +namespace Utils +{ + /// + /// Utility methods for working with SpriteRenderers. + /// + public static class AppleHillsUtils + { + /// + /// Copies all relevant visual properties from one SpriteRenderer to another. + /// + /// The source SpriteRenderer. + /// The target SpriteRenderer. + public static void CopySpriteRendererProperties(SpriteRenderer from, SpriteRenderer to) + { + if (from == null || to == null) return; + to.sprite = from.sprite; + to.color = from.color; + to.flipX = from.flipX; + to.flipY = from.flipY; + to.material = from.material; + to.drawMode = from.drawMode; + to.size = from.size; + to.maskInteraction = from.maskInteraction; + to.sortingLayerID = from.sortingLayerID; + to.sortingOrder = from.sortingOrder; + to.enabled = true; + to.transform.localScale = from.transform.localScale; + } + } +} diff --git a/Assets/Scripts/Utils/AppleHillsUtils.cs.meta b/Assets/Scripts/Utils/AppleHillsUtils.cs.meta new file mode 100644 index 00000000..094ab807 --- /dev/null +++ b/Assets/Scripts/Utils/AppleHillsUtils.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1bf54e2eb908492588a62995b99bd74a +timeCreated: 1757337202 \ No newline at end of file