using UnityEngine; using System; using System.Linq; using Core; namespace Interactions { /// /// Saveable data for Pickup state /// [Serializable] public class PickupSaveData { public bool isPickedUp; public bool wasHeldByFollower; public bool wasInSlot; // NEW: Was this pickup in a slot? public string slotSaveId; // NEW: Which slot held this pickup? public Vector3 worldPosition; public Quaternion worldRotation; public bool isActive; } public class Pickup : SaveableInteractable { public PickupItemData itemData; public SpriteRenderer iconRenderer; public bool IsPickedUp { get; internal set; } // Track which slot owns this pickup (for bilateral restoration) internal ItemSlot OwningSlot { get; set; } public event Action OnItemPickedUp; public event Action OnItemsCombined; internal override void OnManagedAwake() { base.OnManagedAwake(); // Register with save system if (iconRenderer == null) iconRenderer = GetComponent(); ApplyItemData(); } // Always register with ItemManager, even if picked up // This allows the save/load system to find held items when restoring state internal override void OnManagedStart() { base.OnManagedStart(); ItemManager.Instance?.RegisterPickup(this); } internal override void OnManagedDestroy() { // 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; } } #region Interaction Logic /// /// Main interaction logic: Try combination, then try pickup. /// protected override bool DoInteraction() { Logging.Debug("[Pickup] DoInteraction"); // IMPORTANT: Capture held item data BEFORE combination // TryCombineItems destroys the original items, so we need this data for the event var heldItemObject = FollowerController?.GetHeldPickupObject(); var heldItemData = heldItemObject?.GetComponent()?.itemData; // Try combination first var combinationResult = FollowerController.TryCombineItems(this, out var resultItem); if (combinationResult == FollowerController.CombinationResult.Successful) { // Mark this pickup as picked up (consumed in combination) to prevent restoration IsPickedUp = true; // Combination succeeded - original items destroyed, result picked up by TryCombineItems FireCombinationEvent(resultItem, heldItemData); return true; } // No combination (or unsuccessful) - do regular pickup FollowerController?.TryPickupItem(gameObject, itemData); IsPickedUp = true; OnItemPickedUp?.Invoke(itemData); return true; } /// /// Helper method to fire the combination event with correct item data. /// /// The spawned result item /// The ORIGINAL held item data (before destruction) private void FireCombinationEvent(GameObject resultItem, PickupItemData originalHeldItemData) { var resultPickup = resultItem?.GetComponent(); // Verify we have all required data if (resultPickup?.itemData != null && originalHeldItemData != null && itemData != null) { OnItemsCombined?.Invoke(itemData, originalHeldItemData, resultPickup.itemData); } } #endregion #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; // Check if this pickup is in a slot bool isInSlot = OwningSlot != null; string slotId = isInSlot && OwningSlot is SaveableInteractable saveableSlot ? saveableSlot.SaveId : ""; return new PickupSaveData { isPickedUp = this.IsPickedUp, wasHeldByFollower = isHeldByFollower, wasInSlot = isInSlot, slotSaveId = slotId, worldPosition = transform.position, worldRotation = transform.rotation, isActive = gameObject.activeSelf }; } protected override void ApplySerializableState(string serializedData) { PickupSaveData data = JsonUtility.FromJson(serializedData); if (data == null) { Logging.Warning($"[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); } } // If this was in a slot, try bilateral restoration with the slot else if (data.wasInSlot && !string.IsNullOrEmpty(data.slotSaveId)) { // Try to give this pickup to the slot var slot = FindSlotBySaveId(data.slotSaveId); if (slot != null) { slot.TryClaimSlottedItem(this); } else { Logging.Warning($"[Pickup] Could not find slot with SaveId: {data.slotSaveId}"); } } } 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 } /// /// Find an ItemSlot by its SaveId (for bilateral restoration). /// private ItemSlot FindSlotBySaveId(string slotSaveId) { if (string.IsNullOrEmpty(slotSaveId)) return null; // Get all ItemSlots from ItemManager var allSlots = ItemManager.Instance?.GetAllItemSlots(); if (allSlots == null) return null; foreach (var slot in allSlots) { if (slot is SaveableInteractable saveable && saveable.SaveId == slotSaveId) { return slot; } } return null; } /// /// 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 } }