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