2025-09-06 21:01:54 +02:00
|
|
|
|
using Input;
|
|
|
|
|
|
using UnityEngine;
|
2025-11-02 12:48:48 +01:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Linq; // added for Action<T>
|
2025-09-29 09:34:15 +00:00
|
|
|
|
using Core; // register with ItemManager
|
2025-09-01 16:14:21 +02:00
|
|
|
|
|
2025-09-11 13:00:26 +02:00
|
|
|
|
namespace Interactions
|
2025-09-01 16:14:21 +02:00
|
|
|
|
{
|
2025-11-02 12:48:48 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Saveable data for Pickup state
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[System.Serializable]
|
|
|
|
|
|
public class PickupSaveData
|
|
|
|
|
|
{
|
|
|
|
|
|
public bool isPickedUp;
|
|
|
|
|
|
public Vector3 worldPosition;
|
|
|
|
|
|
public Quaternion worldRotation;
|
|
|
|
|
|
public bool isActive;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class Pickup : SaveableInteractable
|
2025-09-01 16:14:21 +02:00
|
|
|
|
{
|
2025-09-11 13:00:26 +02:00
|
|
|
|
public PickupItemData itemData;
|
|
|
|
|
|
public SpriteRenderer iconRenderer;
|
|
|
|
|
|
|
2025-09-29 09:34:15 +00:00
|
|
|
|
// Track if the item has been picked up
|
2025-11-02 12:48:48 +01:00
|
|
|
|
public bool IsPickedUp { get; internal set; }
|
2025-09-29 09:34:15 +00:00
|
|
|
|
|
|
|
|
|
|
// Event: invoked when the item was picked up successfully
|
|
|
|
|
|
public event Action<PickupItemData> OnItemPickedUp;
|
|
|
|
|
|
|
|
|
|
|
|
// Event: invoked when this item is successfully combined with another
|
|
|
|
|
|
public event Action<PickupItemData, PickupItemData, PickupItemData> OnItemsCombined;
|
|
|
|
|
|
|
2025-09-11 13:00:26 +02:00
|
|
|
|
/// <summary>
|
2025-10-31 13:50:08 +01:00
|
|
|
|
/// Unity Awake callback. Sets up icon and applies item data.
|
2025-09-11 13:00:26 +02:00
|
|
|
|
/// </summary>
|
2025-11-02 12:48:48 +01:00
|
|
|
|
protected override void Awake()
|
2025-09-01 16:14:21 +02:00
|
|
|
|
{
|
2025-11-02 12:48:48 +01:00
|
|
|
|
base.Awake(); // Register with save system
|
|
|
|
|
|
|
2025-09-11 13:00:26 +02:00
|
|
|
|
if (iconRenderer == null)
|
|
|
|
|
|
iconRenderer = GetComponent<SpriteRenderer>();
|
|
|
|
|
|
|
|
|
|
|
|
ApplyItemData();
|
2025-09-01 16:14:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-29 09:34:15 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Register with ItemManager on Start
|
|
|
|
|
|
/// </summary>
|
2025-11-02 12:48:48 +01:00
|
|
|
|
protected override void Start()
|
2025-09-29 09:34:15 +00:00
|
|
|
|
{
|
2025-11-02 12:48:48 +01:00
|
|
|
|
base.Start(); // Register with save system
|
|
|
|
|
|
|
|
|
|
|
|
// Don't register with ItemManager if already picked up (restored from save)
|
|
|
|
|
|
if (!IsPickedUp)
|
|
|
|
|
|
{
|
|
|
|
|
|
ItemManager.Instance?.RegisterPickup(this);
|
|
|
|
|
|
}
|
2025-09-29 09:34:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-11 13:00:26 +02:00
|
|
|
|
/// <summary>
|
2025-10-31 13:50:08 +01:00
|
|
|
|
/// Unity OnDestroy callback. Unregisters from ItemManager.
|
2025-09-11 13:00:26 +02:00
|
|
|
|
/// </summary>
|
2025-11-02 12:48:48 +01:00
|
|
|
|
protected override void OnDestroy()
|
2025-09-01 16:14:21 +02:00
|
|
|
|
{
|
2025-11-02 12:48:48 +01:00
|
|
|
|
base.OnDestroy(); // Unregister from save system
|
|
|
|
|
|
|
2025-09-29 09:34:15 +00:00
|
|
|
|
// Unregister from ItemManager
|
|
|
|
|
|
ItemManager.Instance?.UnregisterPickup(this);
|
2025-09-01 16:14:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if UNITY_EDITOR
|
2025-09-11 13:00:26 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Unity OnValidate callback. Ensures icon and data are up to date in editor.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
void OnValidate()
|
2025-09-08 08:45:13 +02:00
|
|
|
|
{
|
2025-09-11 13:00:26 +02:00
|
|
|
|
if (iconRenderer == null)
|
|
|
|
|
|
iconRenderer = GetComponent<SpriteRenderer>();
|
|
|
|
|
|
ApplyItemData();
|
2025-09-08 08:45:13 +02:00
|
|
|
|
}
|
2025-09-11 13:00:26 +02:00
|
|
|
|
#endif
|
|
|
|
|
|
|
2025-10-31 13:50:08 +01:00
|
|
|
|
|
2025-09-11 13:00:26 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Applies the item data to the pickup (icon, name, etc).
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void ApplyItemData()
|
2025-09-08 08:45:13 +02:00
|
|
|
|
{
|
2025-09-11 13:00:26 +02:00
|
|
|
|
if (itemData != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (iconRenderer != null && itemData.mapSprite != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
iconRenderer.sprite = itemData.mapSprite;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
gameObject.name = itemData.itemName;
|
|
|
|
|
|
}
|
2025-09-08 08:45:13 +02:00
|
|
|
|
}
|
2025-09-11 13:00:26 +02:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2025-10-31 13:50:08 +01:00
|
|
|
|
/// Override: Called when character arrives at the interaction point.
|
|
|
|
|
|
/// Handles item pickup and combination logic.
|
2025-09-11 13:00:26 +02:00
|
|
|
|
/// </summary>
|
2025-10-31 13:50:08 +01:00
|
|
|
|
protected override void OnCharacterArrived()
|
2025-09-01 16:14:21 +02:00
|
|
|
|
{
|
2025-10-14 15:53:58 +02:00
|
|
|
|
Logging.Debug("[Pickup] OnCharacterArrived");
|
2025-09-29 09:34:15 +00:00
|
|
|
|
|
2025-10-31 13:50:08 +01:00
|
|
|
|
var combinationResult = _followerController.TryCombineItems(this, out var combinationResultItem);
|
2025-09-11 13:00:26 +02:00
|
|
|
|
if (combinationResultItem != null)
|
2025-09-04 11:12:19 +02:00
|
|
|
|
{
|
2025-10-31 13:50:08 +01:00
|
|
|
|
CompleteInteraction(true);
|
2025-09-29 09:34:15 +00:00
|
|
|
|
|
|
|
|
|
|
// Fire the combination event when items are successfully combined
|
|
|
|
|
|
if (combinationResult == FollowerController.CombinationResult.Successful)
|
|
|
|
|
|
{
|
|
|
|
|
|
var resultPickup = combinationResultItem.GetComponent<Pickup>();
|
|
|
|
|
|
if (resultPickup != null && resultPickup.itemData != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Get the combined item data
|
|
|
|
|
|
var resultItemData = resultPickup.itemData;
|
2025-10-31 13:50:08 +01:00
|
|
|
|
var heldItem = _followerController.GetHeldPickupObject();
|
2025-09-29 09:34:15 +00:00
|
|
|
|
|
|
|
|
|
|
if (heldItem != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var heldPickup = heldItem.GetComponent<Pickup>();
|
|
|
|
|
|
if (heldPickup != null && heldPickup.itemData != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Trigger the combination event
|
|
|
|
|
|
OnItemsCombined?.Invoke(itemData, heldPickup.itemData, resultItemData);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-11 13:00:26 +02:00
|
|
|
|
return;
|
2025-09-04 11:12:19 +02:00
|
|
|
|
}
|
2025-09-11 13:00:26 +02:00
|
|
|
|
|
2025-10-31 13:50:08 +01:00
|
|
|
|
_followerController?.TryPickupItem(gameObject, itemData);
|
2025-10-02 07:16:39 +02:00
|
|
|
|
|
|
|
|
|
|
var step = GetComponent<PuzzleS.ObjectiveStepBehaviour>();
|
|
|
|
|
|
if (step != null && !step.IsStepUnlocked())
|
|
|
|
|
|
{
|
2025-10-31 13:50:08 +01:00
|
|
|
|
CompleteInteraction(false);
|
2025-10-02 07:16:39 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool wasPickedUp = (combinationResult == FollowerController.CombinationResult.NotApplicable
|
|
|
|
|
|
|| combinationResult == FollowerController.CombinationResult.Unsuccessful);
|
2025-10-31 13:50:08 +01:00
|
|
|
|
CompleteInteraction(wasPickedUp);
|
2025-09-29 09:34:15 +00:00
|
|
|
|
|
|
|
|
|
|
// Update pickup state and invoke event when the item was picked up successfully
|
|
|
|
|
|
if (wasPickedUp)
|
|
|
|
|
|
{
|
2025-11-02 12:48:48 +01:00
|
|
|
|
IsPickedUp = true;
|
2025-09-29 09:34:15 +00:00
|
|
|
|
OnItemPickedUp?.Invoke(itemData);
|
|
|
|
|
|
}
|
2025-09-01 16:14:21 +02:00
|
|
|
|
}
|
2025-11-02 12:48:48 +01:00
|
|
|
|
|
|
|
|
|
|
#region Save/Load Implementation
|
|
|
|
|
|
|
|
|
|
|
|
protected override object GetSerializableState()
|
|
|
|
|
|
{
|
|
|
|
|
|
return new PickupSaveData
|
|
|
|
|
|
{
|
|
|
|
|
|
isPickedUp = this.IsPickedUp,
|
|
|
|
|
|
worldPosition = transform.position,
|
|
|
|
|
|
worldRotation = transform.rotation,
|
|
|
|
|
|
isActive = gameObject.activeSelf
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void ApplySerializableState(string serializedData)
|
|
|
|
|
|
{
|
|
|
|
|
|
PickupSaveData data = JsonUtility.FromJson<PickupSaveData>(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);
|
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Resets the pickup state when the item is dropped back into the world.
|
|
|
|
|
|
/// Called by FollowerController when swapping items.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
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
|
2025-09-01 16:14:21 +02:00
|
|
|
|
}
|
2025-09-11 13:00:26 +02:00
|
|
|
|
}
|