Working mulit-slot items
This commit is contained in:
@@ -262,7 +262,7 @@ MonoBehaviour:
|
|||||||
m_Calls: []
|
m_Calls: []
|
||||||
itemData: {fileID: 11400000, guid: aaf36cd26cf74334e9c7db6c1b03b3fb, type: 2}
|
itemData: {fileID: 11400000, guid: aaf36cd26cf74334e9c7db6c1b03b3fb, type: 2}
|
||||||
iconRenderer: {fileID: 6258593095132504700}
|
iconRenderer: {fileID: 6258593095132504700}
|
||||||
slottedItemRenderer: {fileID: 4110666412151536905}
|
slottedItemRenderers: []
|
||||||
onItemSlotted:
|
onItemSlotted:
|
||||||
m_PersistentCalls:
|
m_PersistentCalls:
|
||||||
m_Calls: []
|
m_Calls: []
|
||||||
|
|||||||
@@ -1170,7 +1170,7 @@ MonoBehaviour:
|
|||||||
m_Calls: []
|
m_Calls: []
|
||||||
itemData: {fileID: 11400000, guid: f97b9e24d6dceb145b56426c1152ebeb, type: 2}
|
itemData: {fileID: 11400000, guid: f97b9e24d6dceb145b56426c1152ebeb, type: 2}
|
||||||
iconRenderer: {fileID: 2343214996212089369}
|
iconRenderer: {fileID: 2343214996212089369}
|
||||||
slottedItemRenderer: {fileID: 7990414055343410434}
|
slottedItemRenderers: []
|
||||||
onItemSlotted:
|
onItemSlotted:
|
||||||
m_PersistentCalls:
|
m_PersistentCalls:
|
||||||
m_Calls: []
|
m_Calls: []
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ MonoBehaviour:
|
|||||||
m_Calls: []
|
m_Calls: []
|
||||||
itemData: {fileID: 11400000, guid: c68dea945fecbf44094359769db04f31, type: 2}
|
itemData: {fileID: 11400000, guid: c68dea945fecbf44094359769db04f31, type: 2}
|
||||||
iconRenderer: {fileID: 2825253017896168654}
|
iconRenderer: {fileID: 2825253017896168654}
|
||||||
slottedItemRenderer: {fileID: 3806274462998212361}
|
slottedItemRenderers: []
|
||||||
onItemSlotted:
|
onItemSlotted:
|
||||||
m_PersistentCalls:
|
m_PersistentCalls:
|
||||||
m_Calls: []
|
m_Calls: []
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ MonoBehaviour:
|
|||||||
m_Calls: []
|
m_Calls: []
|
||||||
itemData: {fileID: 11400000, guid: d28f5774afad9d14f823601707150700, type: 2}
|
itemData: {fileID: 11400000, guid: d28f5774afad9d14f823601707150700, type: 2}
|
||||||
iconRenderer: {fileID: 8875860401447896107}
|
iconRenderer: {fileID: 8875860401447896107}
|
||||||
slottedItemRenderer: {fileID: 6941190210788968874}
|
slottedItemRenderers: []
|
||||||
onItemSlotted:
|
onItemSlotted:
|
||||||
m_PersistentCalls:
|
m_PersistentCalls:
|
||||||
m_Calls: []
|
m_Calls: []
|
||||||
|
|||||||
@@ -465965,7 +465965,8 @@ MonoBehaviour:
|
|||||||
m_Calls: []
|
m_Calls: []
|
||||||
itemData: {fileID: 11400000, guid: d28f5774afad9d14f823601707150700, type: 2}
|
itemData: {fileID: 11400000, guid: d28f5774afad9d14f823601707150700, type: 2}
|
||||||
iconRenderer: {fileID: 1399567344}
|
iconRenderer: {fileID: 1399567344}
|
||||||
slottedItemRenderer: {fileID: 1707349194}
|
slottedItemRenderers:
|
||||||
|
- {fileID: 1707349194}
|
||||||
onItemSlotted:
|
onItemSlotted:
|
||||||
m_PersistentCalls:
|
m_PersistentCalls:
|
||||||
m_Calls: []
|
m_Calls: []
|
||||||
@@ -471861,6 +471862,14 @@ PrefabInstance:
|
|||||||
propertyPath: bushAnimator
|
propertyPath: bushAnimator
|
||||||
value:
|
value:
|
||||||
objectReference: {fileID: 1476225951}
|
objectReference: {fileID: 1476225951}
|
||||||
|
- target: {fileID: 3093816592344978065, guid: 3346526f3046f424196615241a307104, type: 3}
|
||||||
|
propertyPath: slottedItemRenderers.Array.size
|
||||||
|
value: 1
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 3093816592344978065, guid: 3346526f3046f424196615241a307104, type: 3}
|
||||||
|
propertyPath: 'slottedItemRenderers.Array.data[0]'
|
||||||
|
value:
|
||||||
|
objectReference: {fileID: 3708074769586677214}
|
||||||
- target: {fileID: 3093816592344978065, guid: 3346526f3046f424196615241a307104, type: 3}
|
- target: {fileID: 3093816592344978065, guid: 3346526f3046f424196615241a307104, type: 3}
|
||||||
propertyPath: onCorrectItemSlotted.m_PersistentCalls.m_Calls.Array.data[1].m_Target
|
propertyPath: onCorrectItemSlotted.m_PersistentCalls.m_Calls.Array.data[1].m_Target
|
||||||
value:
|
value:
|
||||||
@@ -471939,6 +471948,11 @@ MonoBehaviour:
|
|||||||
m_Script: {fileID: 11500000, guid: 95e46aacea5b42888ee7881894193c11, type: 3}
|
m_Script: {fileID: 11500000, guid: 95e46aacea5b42888ee7881894193c11, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: AppleHillsScripts::Core.SaveLoad.AppleState
|
m_EditorClassIdentifier: AppleHillsScripts::Core.SaveLoad.AppleState
|
||||||
|
--- !u!212 &3708074769586677214 stripped
|
||||||
|
SpriteRenderer:
|
||||||
|
m_CorrespondingSourceObject: {fileID: 7990414055343410434, guid: 3346526f3046f424196615241a307104, type: 3}
|
||||||
|
m_PrefabInstance: {fileID: 3708074769586677211}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
--- !u!1001 &3917799031583628180
|
--- !u!1001 &3917799031583628180
|
||||||
PrefabInstance:
|
PrefabInstance:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -472016,6 +472030,14 @@ PrefabInstance:
|
|||||||
serializedVersion: 3
|
serializedVersion: 3
|
||||||
m_TransformParent: {fileID: 1007550749}
|
m_TransformParent: {fileID: 1007550749}
|
||||||
m_Modifications:
|
m_Modifications:
|
||||||
|
- target: {fileID: 106497079666291966, guid: df01157608cce6447b7ccde0bfa290e1, type: 3}
|
||||||
|
propertyPath: slottedItemRenderers.Array.size
|
||||||
|
value: 1
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 106497079666291966, guid: df01157608cce6447b7ccde0bfa290e1, type: 3}
|
||||||
|
propertyPath: 'slottedItemRenderers.Array.data[0]'
|
||||||
|
value:
|
||||||
|
objectReference: {fileID: 3978117984697153446}
|
||||||
- target: {fileID: 106497079666291966, guid: df01157608cce6447b7ccde0bfa290e1, type: 3}
|
- target: {fileID: 106497079666291966, guid: df01157608cce6447b7ccde0bfa290e1, type: 3}
|
||||||
propertyPath: onCorrectItemSlotted.m_PersistentCalls.m_Calls.Array.data[1].m_Target
|
propertyPath: onCorrectItemSlotted.m_PersistentCalls.m_Calls.Array.data[1].m_Target
|
||||||
value:
|
value:
|
||||||
@@ -472081,6 +472103,11 @@ PrefabInstance:
|
|||||||
m_AddedGameObjects: []
|
m_AddedGameObjects: []
|
||||||
m_AddedComponents: []
|
m_AddedComponents: []
|
||||||
m_SourcePrefab: {fileID: 100100000, guid: df01157608cce6447b7ccde0bfa290e1, type: 3}
|
m_SourcePrefab: {fileID: 100100000, guid: df01157608cce6447b7ccde0bfa290e1, type: 3}
|
||||||
|
--- !u!212 &3978117984697153446 stripped
|
||||||
|
SpriteRenderer:
|
||||||
|
m_CorrespondingSourceObject: {fileID: 3806274462998212361, guid: df01157608cce6447b7ccde0bfa290e1, type: 3}
|
||||||
|
m_PrefabInstance: {fileID: 3978117984697153445}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
--- !u!1001 &4596770314561390347
|
--- !u!1001 &4596770314561390347
|
||||||
PrefabInstance:
|
PrefabInstance:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -472839,6 +472866,22 @@ PrefabInstance:
|
|||||||
propertyPath: playerToPlaceDistance
|
propertyPath: playerToPlaceDistance
|
||||||
value: 30
|
value: 30
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 4110666412151536905, guid: 3144c6bbac26fbd49a1608152821cc5f, type: 3}
|
||||||
|
propertyPath: m_Size.x
|
||||||
|
value: 5.75
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 4110666412151536905, guid: 3144c6bbac26fbd49a1608152821cc5f, type: 3}
|
||||||
|
propertyPath: m_Size.y
|
||||||
|
value: 2.78
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 4110666412151536905, guid: 3144c6bbac26fbd49a1608152821cc5f, type: 3}
|
||||||
|
propertyPath: m_Sprite
|
||||||
|
value:
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 4110666412151536905, guid: 3144c6bbac26fbd49a1608152821cc5f, type: 3}
|
||||||
|
propertyPath: m_WasSpriteAssigned
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 5375394469162727687, guid: 3144c6bbac26fbd49a1608152821cc5f, type: 3}
|
- target: {fileID: 5375394469162727687, guid: 3144c6bbac26fbd49a1608152821cc5f, type: 3}
|
||||||
propertyPath: m_LocalPosition.x
|
propertyPath: m_LocalPosition.x
|
||||||
value: 5.28
|
value: 5.28
|
||||||
@@ -472911,6 +472954,18 @@ PrefabInstance:
|
|||||||
propertyPath: m_Name
|
propertyPath: m_Name
|
||||||
value: LureSpotA_Slot
|
value: LureSpotA_Slot
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 8578055200319571631, guid: 3144c6bbac26fbd49a1608152821cc5f, type: 3}
|
||||||
|
propertyPath: iconRenderer
|
||||||
|
value:
|
||||||
|
objectReference: {fileID: 8013274907828598646}
|
||||||
|
- target: {fileID: 8578055200319571631, guid: 3144c6bbac26fbd49a1608152821cc5f, type: 3}
|
||||||
|
propertyPath: slottedItemRenderers.Array.size
|
||||||
|
value: 1
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 8578055200319571631, guid: 3144c6bbac26fbd49a1608152821cc5f, type: 3}
|
||||||
|
propertyPath: 'slottedItemRenderers.Array.data[0]'
|
||||||
|
value:
|
||||||
|
objectReference: {fileID: 8013274907828598645}
|
||||||
- target: {fileID: 8578055200319571631, guid: 3144c6bbac26fbd49a1608152821cc5f, type: 3}
|
- target: {fileID: 8578055200319571631, guid: 3144c6bbac26fbd49a1608152821cc5f, type: 3}
|
||||||
propertyPath: onCorrectItemSlotted.m_PersistentCalls.m_Calls.Array.size
|
propertyPath: onCorrectItemSlotted.m_PersistentCalls.m_Calls.Array.size
|
||||||
value: 2
|
value: 2
|
||||||
@@ -472965,6 +473020,16 @@ Transform:
|
|||||||
m_CorrespondingSourceObject: {fileID: 2045549771447434109, guid: 3144c6bbac26fbd49a1608152821cc5f, type: 3}
|
m_CorrespondingSourceObject: {fileID: 2045549771447434109, guid: 3144c6bbac26fbd49a1608152821cc5f, type: 3}
|
||||||
m_PrefabInstance: {fileID: 8013274907828598643}
|
m_PrefabInstance: {fileID: 8013274907828598643}
|
||||||
m_PrefabAsset: {fileID: 0}
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
--- !u!212 &8013274907828598645 stripped
|
||||||
|
SpriteRenderer:
|
||||||
|
m_CorrespondingSourceObject: {fileID: 4110666412151536905, guid: 3144c6bbac26fbd49a1608152821cc5f, type: 3}
|
||||||
|
m_PrefabInstance: {fileID: 8013274907828598643}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
--- !u!212 &8013274907828598646 stripped
|
||||||
|
SpriteRenderer:
|
||||||
|
m_CorrespondingSourceObject: {fileID: 6258593095132504700, guid: 3144c6bbac26fbd49a1608152821cc5f, type: 3}
|
||||||
|
m_PrefabInstance: {fileID: 8013274907828598643}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
--- !u!1001 &8058740013708592448
|
--- !u!1001 &8058740013708592448
|
||||||
PrefabInstance:
|
PrefabInstance:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
|||||||
@@ -23,5 +23,8 @@ namespace AppleHills.Core.Settings
|
|||||||
public PickupItemData slotItem; // The slot object (SO reference)
|
public PickupItemData slotItem; // The slot object (SO reference)
|
||||||
public List<PickupItemData> allowedItems;
|
public List<PickupItemData> allowedItems;
|
||||||
public List<PickupItemData> forbiddenItems; // Items that cannot be placed in this slot
|
public List<PickupItemData> forbiddenItems; // Items that cannot be placed in this slot
|
||||||
|
|
||||||
|
[Tooltip("Number of items required to complete this slot. If 0, requires ALL allowed items.")]
|
||||||
|
public int requiredItemCount; // 0 = require all allowed items (backward compatible)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq; // for Count() on List<bool>
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Events;
|
using UnityEngine.Events;
|
||||||
using System; // for Action<T>
|
using System; // for Action<T>
|
||||||
@@ -23,8 +24,9 @@ namespace Interactions
|
|||||||
public class ItemSlotSaveData
|
public class ItemSlotSaveData
|
||||||
{
|
{
|
||||||
public ItemSlotState slotState;
|
public ItemSlotState slotState;
|
||||||
public string slottedItemSaveId;
|
public List<string> slottedItemSaveIds = new List<string>(); // Changed to list for multi-slot support
|
||||||
public string slottedItemDataId; // ItemId of the PickupItemData (for verification)
|
public List<string> slottedItemDataIds = new List<string>(); // Changed to list for multi-slot support
|
||||||
|
public bool isLocked; // Track if slot is completed and locked
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -37,13 +39,17 @@ namespace Interactions
|
|||||||
public PickupItemData itemData;
|
public PickupItemData itemData;
|
||||||
public SpriteRenderer iconRenderer;
|
public SpriteRenderer iconRenderer;
|
||||||
|
|
||||||
// Slotted item tracking
|
// Multi-slot item tracking
|
||||||
private PickupItemData currentlySlottedItemData;
|
private List<PickupItemData> slottedItemsData = new List<PickupItemData>();
|
||||||
public SpriteRenderer slottedItemRenderer;
|
public SpriteRenderer[] slottedItemRenderers; // Array of renderers for multiple items
|
||||||
private GameObject currentlySlottedItemObject;
|
private List<GameObject> slottedItemObjects = new List<GameObject>();
|
||||||
|
private List<bool> slottedItemCorrectness = new List<bool>(); // Track which items are correct
|
||||||
|
|
||||||
// Tracks the current state of the slotted item
|
// Tracks the current state of the slotted item(s)
|
||||||
private ItemSlotState currentState = ItemSlotState.None;
|
private ItemSlotState currentState = ItemSlotState.None;
|
||||||
|
|
||||||
|
// Lock flag to prevent removal after successful completion
|
||||||
|
private bool isLockedAfterCompletion;
|
||||||
|
|
||||||
// Settings reference
|
// Settings reference
|
||||||
private IInteractionSettings interactionSettings;
|
private IInteractionSettings interactionSettings;
|
||||||
@@ -53,10 +59,53 @@ namespace Interactions
|
|||||||
/// Read-only access to the current slotted item state.
|
/// Read-only access to the current slotted item state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ItemSlotState CurrentSlottedState => currentState;
|
public ItemSlotState CurrentSlottedState => currentState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of items currently slotted (correct or incorrect)
|
||||||
|
/// </summary>
|
||||||
|
public int CurrentSlottedCount => slottedItemObjects.Count;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of CORRECT items currently slotted
|
||||||
|
/// </summary>
|
||||||
|
public int CurrentCorrectCount => slottedItemCorrectness.Count(correct => correct);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Number of items required to complete this slot
|
||||||
|
/// </summary>
|
||||||
|
public int RequiredItemCount
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var config = interactionSettings?.GetSlotItemConfig(itemData);
|
||||||
|
if (config != null)
|
||||||
|
{
|
||||||
|
// If requiredItemCount is set (> 0), use it; otherwise require all allowed items
|
||||||
|
return config.requiredItemCount > 0 ? config.requiredItemCount : (config.allowedItems?.Count ?? 0);
|
||||||
|
}
|
||||||
|
return 1; // Default to 1 for backward compatibility
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this slot has all required CORRECT items
|
||||||
|
/// </summary>
|
||||||
|
public bool IsComplete => CurrentCorrectCount >= RequiredItemCount && RequiredItemCount > 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this slot has space for more items
|
||||||
|
/// </summary>
|
||||||
|
public bool HasSpace => slottedItemRenderers != null && CurrentSlottedCount < slottedItemRenderers.Length;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this is a multi-slot (more than one renderer)
|
||||||
|
/// </summary>
|
||||||
|
private bool IsMultiSlot => slottedItemRenderers != null && slottedItemRenderers.Length > 1;
|
||||||
|
|
||||||
public UnityEvent onItemSlotted;
|
public UnityEvent onItemSlotted;
|
||||||
public UnityEvent onItemSlotRemoved;
|
public UnityEvent onItemSlotRemoved;
|
||||||
// Native C# event alternative for code-only subscribers
|
// Native C# event alternatives for code-only subscribers
|
||||||
|
public event Action<PickupItemData, PickupItemData> OnItemSlotted; // (slotData, slottedItemData)
|
||||||
public event Action<PickupItemData> OnItemSlotRemoved;
|
public event Action<PickupItemData> OnItemSlotRemoved;
|
||||||
|
|
||||||
public UnityEvent onCorrectItemSlotted;
|
public UnityEvent onCorrectItemSlotted;
|
||||||
@@ -69,17 +118,30 @@ namespace Interactions
|
|||||||
|
|
||||||
public UnityEvent onForbiddenItemSlotted;
|
public UnityEvent onForbiddenItemSlotted;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the first (or only) slotted object - for backward compatibility
|
||||||
|
/// </summary>
|
||||||
public GameObject GetSlottedObject()
|
public GameObject GetSlottedObject()
|
||||||
{
|
{
|
||||||
return currentlySlottedItemObject;
|
return slottedItemObjects.Count > 0 ? slottedItemObjects[0] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set a slotted object - for backward compatibility, replaces first item or adds
|
||||||
|
/// </summary>
|
||||||
public void SetSlottedObject(GameObject obj)
|
public void SetSlottedObject(GameObject obj)
|
||||||
{
|
{
|
||||||
currentlySlottedItemObject = obj;
|
if (obj != null)
|
||||||
if (currentlySlottedItemObject != null)
|
|
||||||
{
|
{
|
||||||
currentlySlottedItemObject.SetActive(false);
|
if (slottedItemObjects.Count == 0)
|
||||||
|
{
|
||||||
|
slottedItemObjects.Add(obj);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
slottedItemObjects[0] = obj;
|
||||||
|
}
|
||||||
|
obj.SetActive(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,12 +196,32 @@ namespace Interactions
|
|||||||
{
|
{
|
||||||
var heldItem = FollowerController?.CurrentlyHeldItemData;
|
var heldItem = FollowerController?.CurrentlyHeldItemData;
|
||||||
|
|
||||||
|
// Check if slot is locked after completion
|
||||||
|
if (isLockedAfterCompletion)
|
||||||
|
{
|
||||||
|
if (heldItem != null)
|
||||||
|
return (false, "This is already complete.");
|
||||||
|
else
|
||||||
|
return (false, "I can't remove these items.");
|
||||||
|
}
|
||||||
|
|
||||||
// Scenario: Nothing held + Empty slot = Error
|
// Scenario: Nothing held + Empty slot = Error
|
||||||
if (heldItem == null && currentlySlottedItemObject == null)
|
if (heldItem == null && CurrentSlottedCount == 0)
|
||||||
return (false, "This requires an item.");
|
return (false, "This requires an item.");
|
||||||
|
|
||||||
// Check forbidden items if trying to slot into empty slot
|
// If holding an item and slot is full but not complete, allow swap
|
||||||
if (heldItem != null && currentlySlottedItemObject == null)
|
if (heldItem != null && !HasSpace)
|
||||||
|
{
|
||||||
|
// Allow swap for fixing mistakes (single-slot or multi-slot not complete)
|
||||||
|
if (!IsMultiSlot || !IsComplete)
|
||||||
|
return (true, null); // Allow swap
|
||||||
|
|
||||||
|
// Multi-slot is complete - can't swap
|
||||||
|
return (false, "This slot is full.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check forbidden items if trying to slot
|
||||||
|
if (heldItem != null)
|
||||||
{
|
{
|
||||||
var config = interactionSettings?.GetSlotItemConfig(itemData);
|
var config = interactionSettings?.GetSlotItemConfig(itemData);
|
||||||
var forbidden = config?.forbiddenItems ?? new List<PickupItemData>();
|
var forbidden = config?.forbiddenItems ?? new List<PickupItemData>();
|
||||||
@@ -153,7 +235,7 @@ namespace Interactions
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Main interaction logic: Slot, pickup, swap, or combine items.
|
/// Main interaction logic: Slot, pickup, swap, or combine items.
|
||||||
/// Returns true only if correct item was slotted.
|
/// Returns true only if correct item was slotted AND slot is now complete.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected override bool DoInteraction()
|
protected override bool DoInteraction()
|
||||||
{
|
{
|
||||||
@@ -162,24 +244,33 @@ namespace Interactions
|
|||||||
var heldItemData = FollowerController.CurrentlyHeldItemData;
|
var heldItemData = FollowerController.CurrentlyHeldItemData;
|
||||||
var heldItemObj = FollowerController.GetHeldPickupObject();
|
var heldItemObj = FollowerController.GetHeldPickupObject();
|
||||||
|
|
||||||
// Scenario 1: Held item + Empty slot = Slot it
|
// Scenario 1: Held item + Has space = Slot it
|
||||||
if (heldItemData != null && currentlySlottedItemObject == null)
|
if (heldItemData != null && HasSpace)
|
||||||
{
|
{
|
||||||
SlotItem(heldItemObj, heldItemData);
|
SlotItem(heldItemObj, heldItemData);
|
||||||
FollowerController.ClearHeldItem(); // Clear follower's hand after slotting
|
FollowerController.ClearHeldItem(); // Clear follower's hand after slotting
|
||||||
return IsSlottedItemCorrect();
|
|
||||||
|
// Check if we completed the slot
|
||||||
|
if (IsComplete)
|
||||||
|
{
|
||||||
|
isLockedAfterCompletion = true;
|
||||||
|
currentState = ItemSlotState.Correct;
|
||||||
|
return true; // Completed!
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // Slotted but not complete yet
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scenario 2 & 3: Slot is full
|
// Scenario 2 & 3: Slot is full
|
||||||
if (currentlySlottedItemObject != null)
|
if (CurrentSlottedCount > 0)
|
||||||
{
|
{
|
||||||
// Try combination if both items present
|
// Try combination if both items present (only for single slots)
|
||||||
if (heldItemData != null)
|
if (heldItemData != null && !IsMultiSlot)
|
||||||
{
|
{
|
||||||
var slottedPickup = currentlySlottedItemObject.GetComponent<Pickup>();
|
var slottedPickup = slottedItemObjects[0].GetComponent<Pickup>();
|
||||||
if (slottedPickup != null)
|
if (slottedPickup != null)
|
||||||
{
|
{
|
||||||
var comboResult = FollowerController.TryCombineItems(slottedPickup, out var combinationResultItem);
|
var comboResult = FollowerController.TryCombineItems(slottedPickup, out _);
|
||||||
|
|
||||||
if (comboResult == FollowerController.CombinationResult.Successful)
|
if (comboResult == FollowerController.CombinationResult.Successful)
|
||||||
{
|
{
|
||||||
@@ -190,55 +281,88 @@ namespace Interactions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No combination or unsuccessful - perform swap
|
// Swap behavior when slot is full (single slots OR multi-slots that aren't complete)
|
||||||
// Step 1: Pickup from slot (follower now holds the old slotted item)
|
if (heldItemData != null && !HasSpace)
|
||||||
FollowerController.TryPickupItem(currentlySlottedItemObject, currentlySlottedItemData, dropItem: false);
|
|
||||||
ClearSlot();
|
|
||||||
|
|
||||||
// Step 2: If we had a held item, slot it (follower already holding picked up item, don't clear!)
|
|
||||||
if (heldItemData != null)
|
|
||||||
{
|
{
|
||||||
SlotItem(heldItemObj, heldItemData);
|
// For single slots: always allow swap
|
||||||
// Don't clear follower - they're holding the item they picked up from the slot
|
// For multi-slots: only allow swap if not complete yet (allows fixing mistakes)
|
||||||
return IsSlottedItemCorrect();
|
if (!IsMultiSlot || !IsComplete)
|
||||||
|
{
|
||||||
|
// LIFO swap - swap with the last item
|
||||||
|
int lastIndex = CurrentSlottedCount - 1;
|
||||||
|
var itemToReturn = slottedItemObjects[lastIndex];
|
||||||
|
var itemDataToReturn = slottedItemsData[lastIndex];
|
||||||
|
|
||||||
|
// Step 1: Give old item to follower
|
||||||
|
FollowerController.TryPickupItem(itemToReturn, itemDataToReturn, dropItem: false);
|
||||||
|
|
||||||
|
// Step 2: Remove old item from slot
|
||||||
|
RemoveItemAtIndex(lastIndex);
|
||||||
|
|
||||||
|
// Step 3: Slot the new item
|
||||||
|
SlotItem(heldItemObj, heldItemData);
|
||||||
|
|
||||||
|
// Check if we completed the slot with this swap
|
||||||
|
if (IsComplete)
|
||||||
|
{
|
||||||
|
isLockedAfterCompletion = true;
|
||||||
|
currentState = ItemSlotState.Correct;
|
||||||
|
return true; // Completed!
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // Swapped but not complete
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just picked up from slot - not a success
|
// Pickup from slot (empty hands) - LIFO removal
|
||||||
return false;
|
if (heldItemData == null)
|
||||||
|
{
|
||||||
|
int lastIndex = CurrentSlottedCount - 1;
|
||||||
|
var itemToPickup = slottedItemObjects[lastIndex];
|
||||||
|
var itemDataToPickup = slottedItemsData[lastIndex];
|
||||||
|
|
||||||
|
// Try to give item to follower
|
||||||
|
FollowerController.TryPickupItem(itemToPickup, itemDataToPickup, dropItem: false);
|
||||||
|
|
||||||
|
// Remove from slot
|
||||||
|
RemoveItemAtIndex(lastIndex);
|
||||||
|
|
||||||
|
// Just picked up from slot - not a success
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shouldn't reach here (validation prevents empty + no held)
|
// Shouldn't reach here (validation prevents empty + no held)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Helper: Check if the currently slotted item is correct.
|
|
||||||
/// </summary>
|
|
||||||
private bool IsSlottedItemCorrect()
|
|
||||||
{
|
|
||||||
return currentState == ItemSlotState.Correct;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper: Clear the slot and fire removal events.
|
/// Helper: Clear the slot and fire removal events.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void ClearSlot()
|
private void ClearSlot()
|
||||||
{
|
{
|
||||||
var previousData = currentlySlottedItemData;
|
var previousData = slottedItemsData.Count > 0 ? slottedItemsData[0] : null;
|
||||||
|
|
||||||
// Clear the pickup's OwningSlot reference
|
// Clear all pickup's OwningSlot references
|
||||||
if (currentlySlottedItemObject != null)
|
foreach (var itemObj in slottedItemObjects)
|
||||||
{
|
{
|
||||||
var pickup = currentlySlottedItemObject.GetComponent<Pickup>();
|
if (itemObj != null)
|
||||||
if (pickup != null)
|
|
||||||
{
|
{
|
||||||
pickup.OwningSlot = null;
|
var pickup = itemObj.GetComponent<Pickup>();
|
||||||
|
if (pickup != null)
|
||||||
|
{
|
||||||
|
pickup.OwningSlot = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentlySlottedItemObject = null;
|
slottedItemObjects.Clear();
|
||||||
currentlySlottedItemData = null;
|
slottedItemsData.Clear();
|
||||||
|
slottedItemCorrectness.Clear(); // Also clear correctness tracking
|
||||||
currentState = ItemSlotState.None;
|
currentState = ItemSlotState.None;
|
||||||
|
isLockedAfterCompletion = false;
|
||||||
UpdateSlottedSprite();
|
UpdateSlottedSprite();
|
||||||
|
|
||||||
// Fire removal events
|
// Fire removal events
|
||||||
@@ -246,35 +370,92 @@ namespace Interactions
|
|||||||
OnItemSlotRemoved?.Invoke(previousData);
|
OnItemSlotRemoved?.Invoke(previousData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper: Remove a specific item from the slot by index.
|
||||||
|
/// </summary>
|
||||||
|
private void RemoveItemAtIndex(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= CurrentSlottedCount)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var itemObj = slottedItemObjects[index];
|
||||||
|
var removedItemData = slottedItemsData[index];
|
||||||
|
|
||||||
|
// Clear the pickup's OwningSlot reference
|
||||||
|
if (itemObj != null)
|
||||||
|
{
|
||||||
|
var pickup = itemObj.GetComponent<Pickup>();
|
||||||
|
if (pickup != null)
|
||||||
|
{
|
||||||
|
pickup.OwningSlot = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
slottedItemObjects.RemoveAt(index);
|
||||||
|
slottedItemsData.RemoveAt(index);
|
||||||
|
slottedItemCorrectness.RemoveAt(index); // Also remove correctness tracking
|
||||||
|
|
||||||
|
if (CurrentSlottedCount == 0)
|
||||||
|
{
|
||||||
|
currentState = ItemSlotState.None;
|
||||||
|
isLockedAfterCompletion = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateSlottedSprite();
|
||||||
|
|
||||||
|
// Fire removal events
|
||||||
|
onItemSlotRemoved?.Invoke();
|
||||||
|
OnItemSlotRemoved?.Invoke(removedItemData);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Visual Updates
|
#region Visual Updates
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the sprite and scale for the currently slotted item.
|
/// Updates the sprite and scale for all slotted items.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateSlottedSprite()
|
private void UpdateSlottedSprite()
|
||||||
{
|
{
|
||||||
if (slottedItemRenderer != null && currentlySlottedItemData != null && currentlySlottedItemData.mapSprite != null)
|
if (slottedItemRenderers == null || slottedItemRenderers.Length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Update each renderer based on slotted items
|
||||||
|
for (int i = 0; i < slottedItemRenderers.Length; i++)
|
||||||
{
|
{
|
||||||
slottedItemRenderer.sprite = currentlySlottedItemData.mapSprite;
|
var slotRenderer = slottedItemRenderers[i];
|
||||||
// Scale sprite to desired height, preserve aspect ratio, compensate for parent scale
|
if (slotRenderer == null)
|
||||||
float desiredHeight = playerFollowerSettings?.HeldIconDisplayHeight ?? 2.0f;
|
continue;
|
||||||
var sprite = currentlySlottedItemData.mapSprite;
|
|
||||||
float spriteHeight = sprite.bounds.size.y;
|
// If we have an item at this index, show it
|
||||||
Vector3 parentScale = slottedItemRenderer.transform.parent != null
|
if (i < slottedItemsData.Count && slottedItemsData[i] != null)
|
||||||
? slottedItemRenderer.transform.parent.localScale
|
|
||||||
: Vector3.one;
|
|
||||||
if (spriteHeight > 0f)
|
|
||||||
{
|
{
|
||||||
float uniformScale = desiredHeight / spriteHeight;
|
var slottedData = slottedItemsData[i];
|
||||||
float scale = uniformScale / Mathf.Max(parentScale.x, parentScale.y);
|
if (slottedData.mapSprite != null)
|
||||||
slottedItemRenderer.transform.localScale = new Vector3(scale, scale, 1f);
|
{
|
||||||
|
slotRenderer.sprite = slottedData.mapSprite;
|
||||||
|
|
||||||
|
// Scale sprite to desired height, preserve aspect ratio, compensate for parent scale
|
||||||
|
float desiredHeight = playerFollowerSettings?.HeldIconDisplayHeight ?? 2.0f;
|
||||||
|
var sprite = slottedData.mapSprite;
|
||||||
|
float spriteHeight = sprite.bounds.size.y;
|
||||||
|
Vector3 parentScale = slotRenderer.transform.parent != null
|
||||||
|
? slotRenderer.transform.parent.localScale
|
||||||
|
: Vector3.one;
|
||||||
|
|
||||||
|
if (spriteHeight > 0f)
|
||||||
|
{
|
||||||
|
float uniformScale = desiredHeight / spriteHeight;
|
||||||
|
float scale = uniformScale / Mathf.Max(parentScale.x, parentScale.y);
|
||||||
|
slotRenderer.transform.localScale = new Vector3(scale, scale, 1f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Clear renderer if no item at this index
|
||||||
|
slotRenderer.sprite = null;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (slottedItemRenderer != null)
|
|
||||||
{
|
|
||||||
slottedItemRenderer.sprite = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,31 +478,47 @@ namespace Interactions
|
|||||||
|
|
||||||
protected override object GetSerializableState()
|
protected override object GetSerializableState()
|
||||||
{
|
{
|
||||||
// Get slotted item save ID if there's a slotted item
|
var saveData = new ItemSlotSaveData
|
||||||
string slottedSaveId = "";
|
|
||||||
string slottedDataId = "";
|
|
||||||
|
|
||||||
if (currentlySlottedItemObject != null)
|
|
||||||
{
|
{
|
||||||
var slottedPickup = currentlySlottedItemObject.GetComponent<Pickup>();
|
slotState = currentState,
|
||||||
if (slottedPickup is SaveableInteractable saveablePickup)
|
isLocked = isLockedAfterCompletion
|
||||||
|
};
|
||||||
|
|
||||||
|
// Save all slotted items
|
||||||
|
foreach (var itemObj in slottedItemObjects)
|
||||||
|
{
|
||||||
|
if (itemObj != null)
|
||||||
{
|
{
|
||||||
slottedSaveId = saveablePickup.SaveId;
|
var slottedPickup = itemObj.GetComponent<Pickup>();
|
||||||
|
if (slottedPickup is SaveableInteractable saveablePickup)
|
||||||
|
{
|
||||||
|
saveData.slottedItemSaveIds.Add(saveablePickup.SaveId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
saveData.slottedItemSaveIds.Add("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
saveData.slottedItemSaveIds.Add("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also save the itemData ID for verification
|
// Save all item data IDs for verification
|
||||||
if (currentlySlottedItemData != null)
|
foreach (var slottedData in slottedItemsData)
|
||||||
{
|
{
|
||||||
slottedDataId = currentlySlottedItemData.itemId;
|
if (slottedData != null)
|
||||||
|
{
|
||||||
|
saveData.slottedItemDataIds.Add(slottedData.itemId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
saveData.slottedItemDataIds.Add("");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ItemSlotSaveData
|
return saveData;
|
||||||
{
|
|
||||||
slotState = currentState,
|
|
||||||
slottedItemSaveId = slottedSaveId,
|
|
||||||
slottedItemDataId = slottedDataId
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void ApplySerializableState(string serializedData)
|
protected override void ApplySerializableState(string serializedData)
|
||||||
@@ -335,13 +532,26 @@ namespace Interactions
|
|||||||
|
|
||||||
// Restore slot state
|
// Restore slot state
|
||||||
currentState = data.slotState;
|
currentState = data.slotState;
|
||||||
|
isLockedAfterCompletion = data.isLocked;
|
||||||
|
|
||||||
// Restore slotted item if there was one
|
// Restore all slotted items if there were any
|
||||||
if (!string.IsNullOrEmpty(data.slottedItemSaveId))
|
if (data.slottedItemSaveIds != null && data.slottedItemSaveIds.Count > 0)
|
||||||
{
|
{
|
||||||
Logging.Debug($"[ItemSlot] Restoring slotted item: {data.slottedItemSaveId} (itemId: {data.slottedItemDataId})");
|
for (int i = 0; i < data.slottedItemSaveIds.Count; i++)
|
||||||
RestoreSlottedItem(data.slottedItemSaveId, data.slottedItemDataId);
|
{
|
||||||
|
string saveId = data.slottedItemSaveIds[i];
|
||||||
|
string dataId = i < data.slottedItemDataIds.Count ? data.slottedItemDataIds[i] : "";
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(saveId))
|
||||||
|
{
|
||||||
|
Logging.Debug($"[ItemSlot] Restoring slotted item {i}: {saveId} (itemId: {dataId})");
|
||||||
|
RestoreSlottedItem(saveId, dataId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update all renderers after restoration
|
||||||
|
UpdateSlottedSprite();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -411,118 +621,107 @@ namespace Interactions
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Silently slot the item (no events, no interaction completion)
|
// Add to slotted items list (no events, no interaction completion)
|
||||||
// Follower state is managed separately during save/load restoration
|
// Follower state is managed separately during save/load restoration
|
||||||
ApplySlottedItemState(slottedObject, slottedData, triggerEvents: false);
|
slottedItemObjects.Add(slottedObject);
|
||||||
|
slottedItemsData.Add(slottedData);
|
||||||
|
|
||||||
Logging.Debug($"[ItemSlot] Successfully restored slotted item: {slottedData.itemName} (itemId: {slottedData.itemId})");
|
// Determine if this item is correct for correctness tracking
|
||||||
}
|
var config = interactionSettings?.GetSlotItemConfig(itemData);
|
||||||
|
var allowed = config?.allowedItems ?? new List<PickupItemData>();
|
||||||
/// <summary>
|
bool isCorrectItem = PickupItemData.ListContainsEquivalent(allowed, slottedData);
|
||||||
/// Core logic for slotting an item. Can be used both for normal slotting and silent restoration.
|
slottedItemCorrectness.Add(isCorrectItem);
|
||||||
/// NOTE: Does NOT call CompleteInteraction - the template method handles that via DoInteraction return value.
|
|
||||||
/// NOTE: Does NOT manage follower state - caller is responsible for clearing follower's hand if needed.
|
// Deactivate the item and set pickup state
|
||||||
/// </summary>
|
slottedObject.SetActive(false);
|
||||||
/// <param name="itemToSlot">The item GameObject to slot (or null to clear)</param>
|
if (pickup != null)
|
||||||
/// <param name="itemToSlotData">The PickupItemData for the item</param>
|
|
||||||
/// <param name="triggerEvents">Whether to fire events</param>
|
|
||||||
private void ApplySlottedItemState(GameObject itemToSlot, PickupItemData itemToSlotData, bool triggerEvents)
|
|
||||||
{
|
|
||||||
if (itemToSlot == null)
|
|
||||||
{
|
{
|
||||||
// Clear slot - also clear the pickup's OwningSlot reference
|
pickup.IsPickedUp = true;
|
||||||
if (currentlySlottedItemObject != null)
|
pickup.OwningSlot = this;
|
||||||
{
|
|
||||||
var oldPickup = currentlySlottedItemObject.GetComponent<Pickup>();
|
|
||||||
if (oldPickup != null)
|
|
||||||
{
|
|
||||||
oldPickup.OwningSlot = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var previousData = currentlySlottedItemData;
|
|
||||||
currentlySlottedItemObject = null;
|
|
||||||
currentlySlottedItemData = null;
|
|
||||||
currentState = ItemSlotState.None;
|
|
||||||
|
|
||||||
// Fire native event for slot clearing (only if triggering events)
|
|
||||||
if (previousData != null && triggerEvents)
|
|
||||||
{
|
|
||||||
onItemSlotRemoved?.Invoke();
|
|
||||||
OnItemSlotRemoved?.Invoke(previousData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Slot the item
|
|
||||||
itemToSlot.SetActive(false);
|
|
||||||
itemToSlot.transform.SetParent(null);
|
|
||||||
SetSlottedObject(itemToSlot);
|
|
||||||
currentlySlottedItemData = itemToSlotData;
|
|
||||||
|
|
||||||
// Mark the pickup as picked up and track slot ownership for save/load
|
|
||||||
var pickup = itemToSlot.GetComponent<Pickup>();
|
|
||||||
if (pickup != null)
|
|
||||||
{
|
|
||||||
pickup.IsPickedUp = true;
|
|
||||||
pickup.OwningSlot = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if correct
|
|
||||||
var config = interactionSettings?.GetSlotItemConfig(itemData);
|
|
||||||
var allowed = config?.allowedItems ?? new List<PickupItemData>();
|
|
||||||
|
|
||||||
if (itemToSlotData != null && PickupItemData.ListContainsEquivalent(allowed, itemToSlotData))
|
|
||||||
{
|
|
||||||
currentState = ItemSlotState.Correct;
|
|
||||||
|
|
||||||
// Fire events if requested
|
|
||||||
if (triggerEvents)
|
|
||||||
{
|
|
||||||
DebugUIMessage.Show($"You correctly slotted {itemToSlotData.itemName} into: {itemData.itemName}", Color.green);
|
|
||||||
onCorrectItemSlotted?.Invoke();
|
|
||||||
OnCorrectItemSlotted?.Invoke(itemData, currentlySlottedItemData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currentState = ItemSlotState.Incorrect;
|
|
||||||
|
|
||||||
// Fire events if requested
|
|
||||||
if (triggerEvents)
|
|
||||||
{
|
|
||||||
DebugUIMessage.Show("I'm not sure this works.", Color.yellow);
|
|
||||||
onIncorrectItemSlotted?.Invoke();
|
|
||||||
OnIncorrectItemSlotted?.Invoke(itemData, currentlySlottedItemData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateSlottedSprite();
|
Logging.Debug($"[ItemSlot] Successfully restored slotted item: {slottedData.itemName} (itemId: {slottedData.itemId}, correct: {isCorrectItem})");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Public API for slotting items during gameplay.
|
/// Public API for slotting items during gameplay.
|
||||||
|
/// Adds item to the slot (multi-slot support).
|
||||||
/// Caller is responsible for managing follower's held item state.
|
/// Caller is responsible for managing follower's held item state.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SlotItem(GameObject itemToSlot, PickupItemData itemToSlotData)
|
public void SlotItem(GameObject itemToSlot, PickupItemData itemToSlotData)
|
||||||
{
|
{
|
||||||
ApplySlottedItemState(itemToSlot, itemToSlotData, triggerEvents: true);
|
if (itemToSlot == null || itemToSlotData == null)
|
||||||
|
{
|
||||||
|
Logging.Warning($"[ItemSlot] Attempted to slot null item or data");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if this item is correct (allowed)
|
||||||
|
var config = interactionSettings?.GetSlotItemConfig(itemData);
|
||||||
|
var allowed = config?.allowedItems ?? new List<PickupItemData>();
|
||||||
|
bool isCorrectItem = PickupItemData.ListContainsEquivalent(allowed, itemToSlotData);
|
||||||
|
|
||||||
|
// Add to lists
|
||||||
|
slottedItemObjects.Add(itemToSlot);
|
||||||
|
slottedItemsData.Add(itemToSlotData);
|
||||||
|
slottedItemCorrectness.Add(isCorrectItem); // Track correctness
|
||||||
|
|
||||||
|
// Deactivate item and set pickup state
|
||||||
|
itemToSlot.SetActive(false);
|
||||||
|
itemToSlot.transform.SetParent(null);
|
||||||
|
|
||||||
|
var pickup = itemToSlot.GetComponent<Pickup>();
|
||||||
|
if (pickup != null)
|
||||||
|
{
|
||||||
|
pickup.IsPickedUp = true;
|
||||||
|
pickup.OwningSlot = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update visuals
|
||||||
|
UpdateSlottedSprite();
|
||||||
|
|
||||||
|
// Fire events based on correctness
|
||||||
|
if (isCorrectItem)
|
||||||
|
{
|
||||||
|
DebugUIMessage.Show($"You slotted {itemToSlotData.itemName} into: {itemData.itemName}", Color.green);
|
||||||
|
|
||||||
|
// Fire generic slot event
|
||||||
|
onItemSlotted?.Invoke();
|
||||||
|
OnItemSlotted?.Invoke(itemData, itemToSlotData);
|
||||||
|
|
||||||
|
// Only fire correct completion event if ALL required CORRECT items are now slotted
|
||||||
|
if (IsComplete)
|
||||||
|
{
|
||||||
|
currentState = ItemSlotState.Correct;
|
||||||
|
DebugUIMessage.Show($"Completed: {itemData.itemName}", Color.green);
|
||||||
|
onCorrectItemSlotted?.Invoke();
|
||||||
|
OnCorrectItemSlotted?.Invoke(itemData, itemToSlotData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Incorrect item slotted
|
||||||
|
DebugUIMessage.Show($"Slotted {itemToSlotData.itemName}, but it might not be right...", Color.yellow);
|
||||||
|
onItemSlotted?.Invoke(); // Still fire generic event
|
||||||
|
OnItemSlotted?.Invoke(itemData, itemToSlotData);
|
||||||
|
onIncorrectItemSlotted?.Invoke();
|
||||||
|
OnIncorrectItemSlotted?.Invoke(itemData, itemToSlotData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bilateral restoration entry point: Pickup calls this to offer itself to the Slot.
|
/// Bilateral restoration entry point: Pickup calls this to offer itself to the Slot.
|
||||||
/// Returns true if claim was successful, false if slot already has an item or wrong pickup.
|
/// Returns true if claim was successful, false if slot is full or wrong pickup.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TryClaimSlottedItem(Pickup pickup)
|
public bool TryClaimSlottedItem(Pickup pickup)
|
||||||
{
|
{
|
||||||
if (pickup == null)
|
if (pickup == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// If slot already has an item, reject the claim
|
// If slot is full, reject the claim
|
||||||
if (currentlySlottedItemObject != null)
|
if (!HasSpace)
|
||||||
{
|
{
|
||||||
Logging.Warning($"[ItemSlot] Already has a slotted item, rejecting claim from {pickup.gameObject.name}");
|
Logging.Warning($"[ItemSlot] Slot is full, rejecting claim from {pickup.gameObject.name}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -530,10 +729,21 @@ namespace Interactions
|
|||||||
// Note: We don't have easy access to the expected SaveId here, so we just accept it
|
// Note: We don't have easy access to the expected SaveId here, so we just accept it
|
||||||
// The Pickup's bilateral restoration ensures it only claims the correct slot
|
// The Pickup's bilateral restoration ensures it only claims the correct slot
|
||||||
|
|
||||||
// Claim the pickup
|
// Add the item to lists
|
||||||
ApplySlottedItemState(pickup.gameObject, pickup.itemData, triggerEvents: false);
|
slottedItemObjects.Add(pickup.gameObject);
|
||||||
|
slottedItemsData.Add(pickup.itemData);
|
||||||
|
|
||||||
Logging.Debug($"[ItemSlot] Successfully claimed slotted item: {pickup.itemData?.itemName}");
|
// Determine correctness for tracking
|
||||||
|
var config = interactionSettings?.GetSlotItemConfig(itemData);
|
||||||
|
var allowed = config?.allowedItems ?? new List<PickupItemData>();
|
||||||
|
bool isCorrectItem = PickupItemData.ListContainsEquivalent(allowed, pickup.itemData);
|
||||||
|
slottedItemCorrectness.Add(isCorrectItem);
|
||||||
|
|
||||||
|
pickup.gameObject.SetActive(false);
|
||||||
|
pickup.IsPickedUp = true;
|
||||||
|
pickup.OwningSlot = this;
|
||||||
|
|
||||||
|
Logging.Debug($"[ItemSlot] Successfully claimed slotted item: {pickup.itemData?.itemName} (correct: {isCorrectItem})");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user