First go around with save load system
This commit is contained in:
@@ -16,6 +16,19 @@ namespace Interactions
|
||||
Forbidden
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saveable data for ItemSlot state
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public class ItemSlotSaveData
|
||||
{
|
||||
public PickupSaveData pickupData; // Base pickup state
|
||||
public ItemSlotState slotState; // Current slot validation state
|
||||
public string slottedItemSaveId; // Save ID of slotted item (if any)
|
||||
public string slottedItemDataAssetPath; // Asset path to PickupItemData
|
||||
}
|
||||
|
||||
// TODO: Remove this ridiculous inheritance from Pickup if possible
|
||||
/// <summary>
|
||||
/// Interaction requirement that allows slotting, swapping, or picking up items in a slot.
|
||||
/// </summary>
|
||||
@@ -178,7 +191,130 @@ namespace Interactions
|
||||
}
|
||||
}
|
||||
|
||||
public void SlotItem(GameObject itemToSlot, PickupItemData itemToSlotData, bool clearFollowerHeldItem = true)
|
||||
// Register with ItemManager when enabled
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start(); // This calls Pickup.Start() which registers with save system
|
||||
|
||||
// Additionally register as ItemSlot
|
||||
ItemManager.Instance?.RegisterItemSlot(this);
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy(); // Unregister from save system and pickup manager
|
||||
|
||||
// Unregister from slot manager
|
||||
ItemManager.Instance?.UnregisterItemSlot(this);
|
||||
}
|
||||
|
||||
#region Save/Load Implementation
|
||||
|
||||
protected override object GetSerializableState()
|
||||
{
|
||||
// Get base pickup state
|
||||
PickupSaveData baseData = base.GetSerializableState() as PickupSaveData;
|
||||
|
||||
// Get slotted item save ID if there's a slotted item
|
||||
string slottedSaveId = "";
|
||||
string slottedAssetPath = "";
|
||||
|
||||
if (_currentlySlottedItemObject != null)
|
||||
{
|
||||
var slottedPickup = _currentlySlottedItemObject.GetComponent<Pickup>();
|
||||
if (slottedPickup is SaveableInteractable saveablePickup)
|
||||
{
|
||||
slottedSaveId = saveablePickup.GetSaveId();
|
||||
}
|
||||
|
||||
if (_currentlySlottedItemData != null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
slottedAssetPath = UnityEditor.AssetDatabase.GetAssetPath(_currentlySlottedItemData);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return new ItemSlotSaveData
|
||||
{
|
||||
pickupData = baseData,
|
||||
slotState = _currentState,
|
||||
slottedItemSaveId = slottedSaveId,
|
||||
slottedItemDataAssetPath = slottedAssetPath
|
||||
};
|
||||
}
|
||||
|
||||
protected override void ApplySerializableState(string serializedData)
|
||||
{
|
||||
ItemSlotSaveData data = JsonUtility.FromJson<ItemSlotSaveData>(serializedData);
|
||||
if (data == null)
|
||||
{
|
||||
Debug.LogWarning($"[ItemSlot] Failed to deserialize save data for {gameObject.name}");
|
||||
return;
|
||||
}
|
||||
|
||||
// First restore base pickup state
|
||||
if (data.pickupData != null)
|
||||
{
|
||||
string pickupJson = JsonUtility.ToJson(data.pickupData);
|
||||
base.ApplySerializableState(pickupJson);
|
||||
}
|
||||
|
||||
// Restore slot state
|
||||
_currentState = data.slotState;
|
||||
|
||||
// Restore slotted item if there was one
|
||||
if (!string.IsNullOrEmpty(data.slottedItemSaveId))
|
||||
{
|
||||
RestoreSlottedItem(data.slottedItemSaveId, data.slottedItemDataAssetPath);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restore a slotted item from save data.
|
||||
/// This is called during load restoration and should NOT trigger events.
|
||||
/// </summary>
|
||||
private void RestoreSlottedItem(string slottedItemSaveId, string slottedItemDataAssetPath)
|
||||
{
|
||||
// Try to find the item in the scene by its save ID via ItemManager
|
||||
GameObject slottedObject = ItemManager.Instance?.FindPickupBySaveId(slottedItemSaveId);
|
||||
|
||||
if (slottedObject == null)
|
||||
{
|
||||
Debug.LogWarning($"[ItemSlot] Could not find slotted item with save ID: {slottedItemSaveId}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the item data
|
||||
PickupItemData slottedData = null;
|
||||
#if UNITY_EDITOR
|
||||
if (!string.IsNullOrEmpty(slottedItemDataAssetPath))
|
||||
{
|
||||
slottedData = UnityEditor.AssetDatabase.LoadAssetAtPath<PickupItemData>(slottedItemDataAssetPath);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (slottedData == null)
|
||||
{
|
||||
var pickup = slottedObject.GetComponent<Pickup>();
|
||||
if (pickup != null)
|
||||
{
|
||||
slottedData = pickup.itemData;
|
||||
}
|
||||
}
|
||||
|
||||
// Silently slot the item (no events, no interaction completion)
|
||||
ApplySlottedItemState(slottedObject, slottedData, triggerEvents: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Core logic for slotting an item. Can be used both for normal slotting and silent restoration.
|
||||
/// </summary>
|
||||
/// <param name="itemToSlot">The item GameObject to slot (or null to clear)</param>
|
||||
/// <param name="itemToSlotData">The PickupItemData for the item</param>
|
||||
/// <param name="triggerEvents">Whether to fire events and complete interaction</param>
|
||||
/// <param name="clearFollowerHeldItem">Whether to clear the follower's held item</param>
|
||||
private void ApplySlottedItemState(GameObject itemToSlot, PickupItemData itemToSlotData, bool triggerEvents, bool clearFollowerHeldItem = true)
|
||||
{
|
||||
// Cache the previous item data before clearing, needed for OnItemSlotRemoved event
|
||||
var previousItemData = _currentlySlottedItemData;
|
||||
@@ -188,11 +324,10 @@ namespace Interactions
|
||||
{
|
||||
_currentlySlottedItemObject = null;
|
||||
_currentlySlottedItemData = null;
|
||||
// Clear state when no item is slotted
|
||||
_currentState = ItemSlotState.None;
|
||||
|
||||
// Fire native event for slot clearing
|
||||
if (wasSlotCleared)
|
||||
// Fire native event for slot clearing (only if triggering events)
|
||||
if (wasSlotCleared && triggerEvents)
|
||||
{
|
||||
OnItemSlotRemoved?.Invoke(previousItemData);
|
||||
}
|
||||
@@ -205,55 +340,53 @@ namespace Interactions
|
||||
_currentlySlottedItemData = itemToSlotData;
|
||||
}
|
||||
|
||||
if (clearFollowerHeldItem)
|
||||
if (clearFollowerHeldItem && _followerController != null)
|
||||
{
|
||||
_followerController.ClearHeldItem();
|
||||
}
|
||||
UpdateSlottedSprite();
|
||||
|
||||
// Once an item is slotted, we know it is not forbidden, so we can skip that check, but now check if it was
|
||||
// the correct item we're looking for
|
||||
var config = _interactionSettings?.GetSlotItemConfig(itemData);
|
||||
var allowed = config?.allowedItems ?? new List<PickupItemData>();
|
||||
if (itemToSlotData != null && PickupItemData.ListContainsEquivalent(allowed, itemToSlotData))
|
||||
// Only validate and trigger events if requested
|
||||
if (triggerEvents)
|
||||
{
|
||||
if (itemToSlot != null)
|
||||
// Once an item is slotted, we know it is not forbidden, so we can skip that check, but now check if it was
|
||||
// the correct item we're looking for
|
||||
var config = _interactionSettings?.GetSlotItemConfig(itemData);
|
||||
var allowed = config?.allowedItems ?? new List<PickupItemData>();
|
||||
if (itemToSlotData != null && PickupItemData.ListContainsEquivalent(allowed, itemToSlotData))
|
||||
{
|
||||
DebugUIMessage.Show("You correctly slotted " + itemToSlotData.itemName + " into: " + itemData.itemName, Color.green);
|
||||
onCorrectItemSlotted?.Invoke();
|
||||
OnCorrectItemSlotted?.Invoke(itemData, _currentlySlottedItemData);
|
||||
_currentState = ItemSlotState.Correct;
|
||||
if (itemToSlot != null)
|
||||
{
|
||||
DebugUIMessage.Show("You correctly slotted " + itemToSlotData.itemName + " into: " + itemData.itemName, Color.green);
|
||||
onCorrectItemSlotted?.Invoke();
|
||||
OnCorrectItemSlotted?.Invoke(itemData, _currentlySlottedItemData);
|
||||
_currentState = ItemSlotState.Correct;
|
||||
}
|
||||
|
||||
CompleteInteraction(true);
|
||||
}
|
||||
|
||||
CompleteInteraction(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (itemToSlot != null)
|
||||
else
|
||||
{
|
||||
DebugUIMessage.Show("I'm not sure this works.", Color.yellow);
|
||||
onIncorrectItemSlotted?.Invoke();
|
||||
OnIncorrectItemSlotted?.Invoke(itemData, _currentlySlottedItemData);
|
||||
_currentState = ItemSlotState.Incorrect;
|
||||
if (itemToSlot != null)
|
||||
{
|
||||
DebugUIMessage.Show("I'm not sure this works.", Color.yellow);
|
||||
onIncorrectItemSlotted?.Invoke();
|
||||
OnIncorrectItemSlotted?.Invoke(itemData, _currentlySlottedItemData);
|
||||
_currentState = ItemSlotState.Incorrect;
|
||||
}
|
||||
CompleteInteraction(false);
|
||||
}
|
||||
CompleteInteraction(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Register with ItemManager when enabled
|
||||
void Start()
|
||||
/// <summary>
|
||||
/// Public API for slotting items during gameplay.
|
||||
/// </summary>
|
||||
public void SlotItem(GameObject itemToSlot, PickupItemData itemToSlotData, bool clearFollowerHeldItem = true)
|
||||
{
|
||||
// Note: Base Pickup class also calls RegisterPickup in its Start
|
||||
// This additionally registers as ItemSlot
|
||||
ItemManager.Instance?.RegisterPickup(this);
|
||||
ItemManager.Instance?.RegisterItemSlot(this);
|
||||
ApplySlottedItemState(itemToSlot, itemToSlotData, triggerEvents: true, clearFollowerHeldItem);
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
// Unregister from both pickup and slot managers
|
||||
ItemManager.Instance?.UnregisterPickup(this);
|
||||
ItemManager.Instance?.UnregisterItemSlot(this);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user