using Input; using UnityEngine; using System; using System.Linq; using Bootstrap; // added for Action using Core; // register with ItemManager namespace Interactions { /// /// Saveable data for Pickup state /// [System.Serializable] public class PickupSaveData { public bool isPickedUp; public bool wasHeldByFollower; // Track if held by follower for bilateral restoration public Vector3 worldPosition; public Quaternion worldRotation; public bool isActive; } public class Pickup : SaveableInteractable { public PickupItemData itemData; public SpriteRenderer iconRenderer; // Track if the item has been picked up public bool IsPickedUp { get; internal set; } // Event: invoked when the item was picked up successfully public event Action OnItemPickedUp; // Event: invoked when this item is successfully combined with another public event Action OnItemsCombined; /// /// Unity Awake callback. Sets up icon and applies item data. /// protected override void Awake() { base.Awake(); // Register with save system if (iconRenderer == null) iconRenderer = GetComponent(); ApplyItemData(); } /// /// Register with ItemManager on Start /// protected override void Start() { base.Start(); // Register with save system // Always register with ItemManager, even if picked up // This allows the save/load system to find held items when restoring state BootCompletionService.RegisterInitAction(() => { ItemManager.Instance?.RegisterPickup(this); }); } /// /// Unity OnDestroy callback. Unregisters from ItemManager. /// protected override void OnDestroy() { base.OnDestroy(); // Unregister from save system // Unregister from ItemManager ItemManager.Instance?.UnregisterPickup(this); } #if UNITY_EDITOR /// /// Unity OnValidate callback. Ensures icon and data are up to date in editor. /// void OnValidate() { if (iconRenderer == null) iconRenderer = GetComponent(); ApplyItemData(); } #endif /// /// Applies the item data to the pickup (icon, name, etc). /// public void ApplyItemData() { if (itemData != null) { if (iconRenderer != null && itemData.mapSprite != null) { iconRenderer.sprite = itemData.mapSprite; } gameObject.name = itemData.itemName; } } /// /// Override: Called when character arrives at the interaction point. /// Handles item pickup and combination logic. /// protected override void OnCharacterArrived() { Logging.Debug("[Pickup] OnCharacterArrived"); var combinationResult = _followerController.TryCombineItems(this, out var combinationResultItem); if (combinationResultItem != null) { CompleteInteraction(true); // Fire the combination event when items are successfully combined if (combinationResult == FollowerController.CombinationResult.Successful) { var resultPickup = combinationResultItem.GetComponent(); if (resultPickup != null && resultPickup.itemData != null) { // Get the combined item data var resultItemData = resultPickup.itemData; var heldItem = _followerController.GetHeldPickupObject(); if (heldItem != null) { var heldPickup = heldItem.GetComponent(); if (heldPickup != null && heldPickup.itemData != null) { // Trigger the combination event OnItemsCombined?.Invoke(itemData, heldPickup.itemData, resultItemData); } } } } return; } _followerController?.TryPickupItem(gameObject, itemData); var step = GetComponent(); if (step != null && !step.IsStepUnlocked()) { CompleteInteraction(false); return; } bool wasPickedUp = (combinationResult == FollowerController.CombinationResult.NotApplicable || combinationResult == FollowerController.CombinationResult.Unsuccessful); CompleteInteraction(wasPickedUp); // Update pickup state and invoke event when the item was picked up successfully if (wasPickedUp) { IsPickedUp = true; OnItemPickedUp?.Invoke(itemData); } } #region Save/Load Implementation protected override object GetSerializableState() { // Check if this pickup is currently held by the follower bool isHeldByFollower = IsPickedUp && !gameObject.activeSelf && transform.parent != null; return new PickupSaveData { isPickedUp = this.IsPickedUp, wasHeldByFollower = isHeldByFollower, worldPosition = transform.position, worldRotation = transform.rotation, isActive = gameObject.activeSelf }; } protected override void ApplySerializableState(string serializedData) { PickupSaveData data = JsonUtility.FromJson(serializedData); if (data == null) { Debug.LogWarning($"[Pickup] Failed to deserialize save data for {gameObject.name}"); return; } // Restore picked up state IsPickedUp = data.isPickedUp; if (IsPickedUp) { // Hide the pickup if it was already picked up gameObject.SetActive(false); // If this was held by the follower, try bilateral restoration if (data.wasHeldByFollower) { // Try to give this pickup to the follower // This might succeed or fail depending on timing var follower = FollowerController.FindInstance(); if (follower != null) { follower.TryClaimHeldItem(this); } } } else { // Restore position for items that haven't been picked up (they may have moved) transform.position = data.worldPosition; transform.rotation = data.worldRotation; gameObject.SetActive(data.isActive); } // Note: We do NOT fire OnItemPickedUp event during restoration // This prevents duplicate logic execution } /// /// Resets the pickup state when the item is dropped back into the world. /// Called by FollowerController when swapping items. /// public void ResetPickupState() { IsPickedUp = false; gameObject.SetActive(true); // Re-register with ItemManager if not already registered if (ItemManager.Instance != null && !ItemManager.Instance.GetAllPickups().Contains(this)) { ItemManager.Instance.RegisterPickup(this); } } #endregion } }