Rework of base interactables and managed behaviors
This commit is contained in:
committed by
Michal Pikulski
parent
00e1746ac4
commit
f88bd0e2c9
@@ -5,8 +5,8 @@ using UnityEngine.SceneManagement;
|
||||
using Utils;
|
||||
using AppleHills.Core.Settings;
|
||||
using Core;
|
||||
using Core.Lifecycle;
|
||||
using Core.SaveLoad;
|
||||
using Bootstrap;
|
||||
using UnityEngine.Events;
|
||||
|
||||
/// <summary>
|
||||
@@ -24,7 +24,7 @@ public class FollowerSaveData
|
||||
/// <summary>
|
||||
/// Controls the follower character, including following the player, handling pickups, and managing held items.
|
||||
/// </summary>
|
||||
public class FollowerController : MonoBehaviour, ISaveParticipant
|
||||
public class FollowerController : ManagedBehaviour, ISaveParticipant
|
||||
{
|
||||
private static readonly int CombineTrigger = Animator.StringToHash("Combine");
|
||||
|
||||
@@ -103,7 +103,9 @@ public class FollowerController : MonoBehaviour, ISaveParticipant
|
||||
private bool _hasRestoredHeldItem; // Track if held item restoration completed
|
||||
private string _expectedHeldItemSaveId; // Expected saveId during restoration
|
||||
|
||||
void Awake()
|
||||
public override int ManagedAwakePriority => 110; // Follower after player
|
||||
|
||||
protected override void OnManagedAwake()
|
||||
{
|
||||
_aiPath = GetComponent<AIPath>();
|
||||
// Find art prefab and animator
|
||||
@@ -123,13 +125,7 @@ public class FollowerController : MonoBehaviour, ISaveParticipant
|
||||
_settings = GameManager.GetSettingsObject<IPlayerFollowerSettings>();
|
||||
_interactionSettings = GameManager.GetSettingsObject<IInteractionSettings>();
|
||||
|
||||
// Register for post-boot initialization
|
||||
BootCompletionService.RegisterInitAction(InitializePostBoot);
|
||||
}
|
||||
|
||||
private void InitializePostBoot()
|
||||
{
|
||||
// Register with save system after boot
|
||||
// Register with save system
|
||||
if (SaveLoadManager.Instance != null)
|
||||
{
|
||||
SaveLoadManager.Instance.RegisterParticipant(this);
|
||||
@@ -140,7 +136,7 @@ public class FollowerController : MonoBehaviour, ISaveParticipant
|
||||
Logging.Warning("[FollowerController] SaveLoadManager not available for registration");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
@@ -583,19 +579,31 @@ public class FollowerController : MonoBehaviour, ISaveParticipant
|
||||
#endregion StationaryAnimations
|
||||
|
||||
#region ItemInteractions
|
||||
|
||||
// TODO: Move TryCombineItems to ItemManager/InteractionHelpers
|
||||
// This is currently interaction logic living in a movement controller.
|
||||
// Pros of moving: Separates game logic from character logic, easier to test
|
||||
// Cons: More coordination needed, follower still needs animation callbacks
|
||||
|
||||
/// <summary>
|
||||
/// Try to pickup an item. If already holding something, optionally drop it first.
|
||||
/// </summary>
|
||||
/// <param name="itemObject">The GameObject to pick up (must have Pickup component)</param>
|
||||
/// <param name="itemData">The item data (redundant - can be extracted from GameObject)</param>
|
||||
/// <param name="dropItem">Whether to drop currently held item before picking up new one</param>
|
||||
public void TryPickupItem(GameObject itemObject, PickupItemData itemData, bool dropItem = true)
|
||||
{
|
||||
if (itemObject == null) return;
|
||||
|
||||
// Drop current item if holding something
|
||||
if (_currentlyHeldItemData != null && _cachedPickupObject != null && dropItem)
|
||||
{
|
||||
// Drop the currently held item at the current position
|
||||
DropHeldItemAt(transform.position);
|
||||
|
||||
}
|
||||
// Pick up the new item
|
||||
SetHeldItem(itemData, itemObject.GetComponent<SpriteRenderer>());
|
||||
_animator.SetBool("IsCarrying", true);
|
||||
_cachedPickupObject = itemObject;
|
||||
_cachedPickupObject.SetActive(false);
|
||||
|
||||
// Use helper to set held item (handles data extraction, caching, animator)
|
||||
SetHeldItemFromObject(itemObject);
|
||||
itemObject.SetActive(false);
|
||||
}
|
||||
|
||||
public enum CombinationResult
|
||||
@@ -609,41 +617,41 @@ public class FollowerController : MonoBehaviour, ISaveParticipant
|
||||
{
|
||||
_animator.ResetTrigger(CombineTrigger);
|
||||
newItem = null;
|
||||
|
||||
// Validation
|
||||
if (_cachedPickupObject == null)
|
||||
{
|
||||
return CombinationResult.NotApplicable;
|
||||
}
|
||||
|
||||
Pickup pickupB = _cachedPickupObject.GetComponent<Pickup>();
|
||||
if (pickupA == null || pickupB == null)
|
||||
{
|
||||
return CombinationResult.NotApplicable;
|
||||
}
|
||||
|
||||
// Use the InteractionSettings directly instead of GameManager
|
||||
// Find combination rule
|
||||
CombinationRule matchingRule = _interactionSettings.GetCombinationRule(pickupA.itemData, pickupB.itemData);
|
||||
|
||||
Vector3 spawnPos = pickupA.gameObject.transform.position;
|
||||
if (matchingRule != null && matchingRule.resultPrefab != null)
|
||||
{
|
||||
newItem = Instantiate(matchingRule.resultPrefab, spawnPos, Quaternion.identity);
|
||||
var resultPickup = newItem.GetComponent<Pickup>();
|
||||
PickupItemData itemData = resultPickup.itemData;
|
||||
|
||||
// Mark the base items as picked up before destroying them
|
||||
// (This ensures they save correctly if the game is saved during the combination animation)
|
||||
pickupA.IsPickedUp = true;
|
||||
pickupB.IsPickedUp = true;
|
||||
|
||||
Destroy(pickupA.gameObject);
|
||||
Destroy(pickupB.gameObject);
|
||||
TryPickupItem(newItem, itemData);
|
||||
PlayAnimationStationary("Combine", 10.0f);
|
||||
PulverIsCombining.Invoke();
|
||||
return CombinationResult.Successful;
|
||||
}
|
||||
if (matchingRule == null || matchingRule.resultPrefab == null)
|
||||
return CombinationResult.Unsuccessful;
|
||||
|
||||
// If no combination found, return Unsuccessful
|
||||
return CombinationResult.Unsuccessful;
|
||||
// Execute combination
|
||||
Vector3 spawnPos = pickupA.gameObject.transform.position;
|
||||
newItem = Instantiate(matchingRule.resultPrefab, spawnPos, Quaternion.identity);
|
||||
var resultPickup = newItem.GetComponent<Pickup>();
|
||||
|
||||
// Mark items as picked up before destroying (for save system)
|
||||
pickupA.IsPickedUp = true;
|
||||
pickupB.IsPickedUp = true;
|
||||
|
||||
Destroy(pickupA.gameObject);
|
||||
Destroy(pickupB.gameObject);
|
||||
|
||||
// Pickup the result (don't drop it!)
|
||||
TryPickupItem(newItem, resultPickup.itemData, dropItem: false);
|
||||
|
||||
// Visual feedback
|
||||
PlayAnimationStationary("Combine", 10.0f);
|
||||
PulverIsCombining.Invoke();
|
||||
|
||||
return CombinationResult.Successful;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -673,6 +681,10 @@ public class FollowerController : MonoBehaviour, ISaveParticipant
|
||||
return _cachedPickupObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set held item from a GameObject. Extracts Pickup component and sets up visuals.
|
||||
/// Centralizes held item state management including animator.
|
||||
/// </summary>
|
||||
public void SetHeldItemFromObject(GameObject obj)
|
||||
{
|
||||
if (obj == null)
|
||||
@@ -680,11 +692,13 @@ public class FollowerController : MonoBehaviour, ISaveParticipant
|
||||
ClearHeldItem();
|
||||
return;
|
||||
}
|
||||
|
||||
var pickup = obj.GetComponent<Pickup>();
|
||||
if (pickup != null)
|
||||
{
|
||||
SetHeldItem(pickup.itemData, pickup.iconRenderer);
|
||||
_cachedPickupObject = obj;
|
||||
_animator.SetBool("IsCarrying", true); // Centralized animator management
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -692,11 +706,15 @@ public class FollowerController : MonoBehaviour, ISaveParticipant
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the currently held item. Centralizes state cleanup including animator.
|
||||
/// </summary>
|
||||
public void ClearHeldItem()
|
||||
{
|
||||
_cachedPickupObject = null;
|
||||
_currentlyHeldItemData = null;
|
||||
_animator.SetBool("IsCarrying", false);
|
||||
_animator.SetBool("IsCarrying", false); // Centralized animator management
|
||||
|
||||
if (heldObjectRenderer != null)
|
||||
{
|
||||
heldObjectRenderer.sprite = null;
|
||||
@@ -704,29 +722,28 @@ public class FollowerController : MonoBehaviour, ISaveParticipant
|
||||
}
|
||||
}
|
||||
|
||||
public void DropItem(FollowerController follower, Vector3 position)
|
||||
/// <summary>
|
||||
/// Drop the currently held item at the specified position.
|
||||
/// </summary>
|
||||
public void DropHeldItemAt(Vector3 position)
|
||||
{
|
||||
var item = follower.GetHeldPickupObject();
|
||||
var item = GetHeldPickupObject();
|
||||
if (item == null) return;
|
||||
|
||||
// Place item in world
|
||||
item.transform.position = position;
|
||||
item.transform.SetParent(null);
|
||||
item.SetActive(true);
|
||||
|
||||
// Reset the pickup state so it can be picked up again and saves correctly
|
||||
// Reset pickup state so it can be picked up again
|
||||
var pickup = item.GetComponent<Pickup>();
|
||||
if (pickup != null)
|
||||
{
|
||||
pickup.ResetPickupState();
|
||||
}
|
||||
|
||||
follower.ClearHeldItem();
|
||||
_animator.SetBool("IsCarrying", false);
|
||||
// Optionally: fire event, update UI, etc.
|
||||
}
|
||||
|
||||
public void DropHeldItemAt(Vector3 position)
|
||||
{
|
||||
DropItem(this, position);
|
||||
// Clear held item state (includes animator)
|
||||
ClearHeldItem();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user