Update slots to support assigning to specific items
This commit is contained in:
@@ -17,6 +17,17 @@ namespace Interactions
|
||||
Forbidden
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a sprite renderer to an optional item assignment
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class SlotRendererMapping
|
||||
{
|
||||
public SpriteRenderer renderer;
|
||||
[Tooltip("Optional: If set, this renderer slot is dedicated to this specific item. Leave null for flexible slots.")]
|
||||
public PickupItemData assignedItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saveable data for ItemSlot state
|
||||
/// </summary>
|
||||
@@ -41,7 +52,7 @@ namespace Interactions
|
||||
|
||||
// Multi-slot item tracking
|
||||
private List<PickupItemData> slottedItemsData = new List<PickupItemData>();
|
||||
public SpriteRenderer[] slottedItemRenderers; // Array of renderers for multiple items
|
||||
public SlotRendererMapping[] slottedItemRenderers; // Array of renderers with optional item assignments
|
||||
private List<GameObject> slottedItemObjects = new List<GameObject>();
|
||||
private List<bool> slottedItemCorrectness = new List<bool>(); // Track which items are correct
|
||||
|
||||
@@ -63,12 +74,24 @@ namespace Interactions
|
||||
/// <summary>
|
||||
/// Number of items currently slotted (correct or incorrect)
|
||||
/// </summary>
|
||||
public int CurrentSlottedCount => slottedItemObjects.Count;
|
||||
public int CurrentSlottedCount => slottedItemObjects.Count(obj => obj != null);
|
||||
|
||||
/// <summary>
|
||||
/// Number of CORRECT items currently slotted
|
||||
/// </summary>
|
||||
public int CurrentCorrectCount => slottedItemCorrectness.Count(correct => correct);
|
||||
public int CurrentCorrectCount
|
||||
{
|
||||
get
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i < slottedItemCorrectness.Count; i++)
|
||||
{
|
||||
if (i < slottedItemObjects.Count && slottedItemObjects[i] != null && slottedItemCorrectness[i])
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of items required to complete this slot
|
||||
@@ -288,16 +311,44 @@ namespace Interactions
|
||||
// For multi-slots: only allow swap if not complete yet (allows fixing mistakes)
|
||||
if (!IsMultiSlot || !IsComplete)
|
||||
{
|
||||
// LIFO swap - swap with the last item
|
||||
int lastIndex = CurrentSlottedCount - 1;
|
||||
var itemToReturn = slottedItemObjects[lastIndex];
|
||||
var itemDataToReturn = slottedItemsData[lastIndex];
|
||||
// Determine target index for the new item
|
||||
int targetIndex = GetTargetSlotIndexForItem(heldItemData);
|
||||
|
||||
int indexToSwap;
|
||||
if (targetIndex >= 0 && targetIndex < slottedItemObjects.Count && slottedItemObjects[targetIndex] != null)
|
||||
{
|
||||
// Swap with the item in the assigned slot
|
||||
indexToSwap = targetIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// LIFO swap - swap with the last non-null item
|
||||
indexToSwap = -1;
|
||||
for (int i = slottedItemObjects.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (slottedItemObjects[i] != null)
|
||||
{
|
||||
indexToSwap = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (indexToSwap < 0)
|
||||
{
|
||||
// No items to swap (shouldn't happen, but fallback)
|
||||
SlotItem(heldItemObj, heldItemData);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var itemToReturn = slottedItemObjects[indexToSwap];
|
||||
var itemDataToReturn = slottedItemsData[indexToSwap];
|
||||
|
||||
// Step 1: Give old item to follower
|
||||
FollowerController.TryPickupItem(itemToReturn, itemDataToReturn, dropItem: false);
|
||||
|
||||
// Step 2: Remove old item from slot
|
||||
RemoveItemAtIndex(lastIndex);
|
||||
RemoveItemAtIndex(indexToSwap);
|
||||
|
||||
// Step 3: Slot the new item
|
||||
SlotItem(heldItemObj, heldItemData);
|
||||
@@ -317,15 +368,28 @@ namespace Interactions
|
||||
// Pickup from slot (empty hands) - LIFO removal
|
||||
if (heldItemData == null)
|
||||
{
|
||||
int lastIndex = CurrentSlottedCount - 1;
|
||||
var itemToPickup = slottedItemObjects[lastIndex];
|
||||
var itemDataToPickup = slottedItemsData[lastIndex];
|
||||
// Find last non-null item
|
||||
int lastIndex = -1;
|
||||
for (int i = slottedItemObjects.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (slottedItemObjects[i] != null)
|
||||
{
|
||||
lastIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to give item to follower
|
||||
FollowerController.TryPickupItem(itemToPickup, itemDataToPickup, dropItem: false);
|
||||
|
||||
// Remove from slot
|
||||
RemoveItemAtIndex(lastIndex);
|
||||
if (lastIndex >= 0)
|
||||
{
|
||||
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;
|
||||
@@ -343,7 +407,16 @@ namespace Interactions
|
||||
/// </summary>
|
||||
private void ClearSlot()
|
||||
{
|
||||
var previousData = slottedItemsData.Count > 0 ? slottedItemsData[0] : null;
|
||||
// Find first non-null data for event
|
||||
PickupItemData previousData = null;
|
||||
foreach (var data in slottedItemsData)
|
||||
{
|
||||
if (data != null)
|
||||
{
|
||||
previousData = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all pickup's OwningSlot references
|
||||
foreach (var itemObj in slottedItemObjects)
|
||||
@@ -375,7 +448,7 @@ namespace Interactions
|
||||
/// </summary>
|
||||
private void RemoveItemAtIndex(int index)
|
||||
{
|
||||
if (index < 0 || index >= CurrentSlottedCount)
|
||||
if (index < 0 || index >= slottedItemObjects.Count)
|
||||
return;
|
||||
|
||||
var itemObj = slottedItemObjects[index];
|
||||
@@ -391,9 +464,10 @@ namespace Interactions
|
||||
}
|
||||
}
|
||||
|
||||
slottedItemObjects.RemoveAt(index);
|
||||
slottedItemsData.RemoveAt(index);
|
||||
slottedItemCorrectness.RemoveAt(index); // Also remove correctness tracking
|
||||
// Set to null instead of removing to maintain sparse storage
|
||||
slottedItemObjects[index] = null;
|
||||
slottedItemsData[index] = null;
|
||||
slottedItemCorrectness[index] = false;
|
||||
|
||||
if (CurrentSlottedCount == 0)
|
||||
{
|
||||
@@ -412,6 +486,74 @@ namespace Interactions
|
||||
|
||||
#region Visual Updates
|
||||
|
||||
/// <summary>
|
||||
/// Determines the best slot index for an item based on assignments and availability.
|
||||
/// Returns -1 if no mappings are configured (uses append behavior).
|
||||
/// </summary>
|
||||
private int GetTargetSlotIndexForItem(PickupItemData itemData)
|
||||
{
|
||||
if (slottedItemRenderers == null || slottedItemRenderers.Length == 0)
|
||||
return -1;
|
||||
|
||||
// Check if any renderer has assignments configured
|
||||
bool hasAnyAssignments = false;
|
||||
for (int i = 0; i < slottedItemRenderers.Length; i++)
|
||||
{
|
||||
if (slottedItemRenderers[i]?.assignedItem != null)
|
||||
{
|
||||
hasAnyAssignments = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no assignments configured, use append behavior (backward compatible)
|
||||
if (!hasAnyAssignments)
|
||||
return -1;
|
||||
|
||||
// Step 1: Check if this item has a dedicated assigned slot
|
||||
for (int i = 0; i < slottedItemRenderers.Length; i++)
|
||||
{
|
||||
var mapping = slottedItemRenderers[i];
|
||||
if (mapping?.assignedItem != null && PickupItemData.AreEquivalent(mapping.assignedItem, itemData))
|
||||
{
|
||||
// This is the assigned slot for this item
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Item is not assigned to a specific slot - find best available slot
|
||||
// Ensure lists are large enough to check
|
||||
while (slottedItemObjects.Count < slottedItemRenderers.Length)
|
||||
{
|
||||
slottedItemObjects.Add(null);
|
||||
slottedItemsData.Add(null);
|
||||
slottedItemCorrectness.Add(false);
|
||||
}
|
||||
|
||||
// Prefer unassigned empty slots first
|
||||
for (int i = 0; i < slottedItemRenderers.Length; i++)
|
||||
{
|
||||
var mapping = slottedItemRenderers[i];
|
||||
if (mapping?.assignedItem == null && slottedItemObjects[i] == null)
|
||||
{
|
||||
return i; // Empty unassigned slot
|
||||
}
|
||||
}
|
||||
|
||||
// Then try assigned but empty slots
|
||||
for (int i = 0; i < slottedItemRenderers.Length; i++)
|
||||
{
|
||||
var mapping = slottedItemRenderers[i];
|
||||
if (mapping?.assignedItem != null && slottedItemObjects[i] == null)
|
||||
{
|
||||
return i; // Empty assigned slot
|
||||
}
|
||||
}
|
||||
|
||||
// All slots full - return -1 to trigger swap logic
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the sprite and scale for all slotted items.
|
||||
/// </summary>
|
||||
@@ -423,10 +565,12 @@ namespace Interactions
|
||||
// Update each renderer based on slotted items
|
||||
for (int i = 0; i < slottedItemRenderers.Length; i++)
|
||||
{
|
||||
var slotRenderer = slottedItemRenderers[i];
|
||||
if (slotRenderer == null)
|
||||
var mapping = slottedItemRenderers[i];
|
||||
if (mapping == null || mapping.renderer == null)
|
||||
continue;
|
||||
|
||||
var slotRenderer = mapping.renderer;
|
||||
|
||||
// If we have an item at this index, show it
|
||||
if (i < slottedItemsData.Count && slottedItemsData[i] != null)
|
||||
{
|
||||
@@ -645,7 +789,7 @@ namespace Interactions
|
||||
|
||||
/// <summary>
|
||||
/// Public API for slotting items during gameplay.
|
||||
/// Adds item to the slot (multi-slot support).
|
||||
/// Adds item to the slot (multi-slot support with positional awareness).
|
||||
/// Caller is responsible for managing follower's held item state.
|
||||
/// </summary>
|
||||
public void SlotItem(GameObject itemToSlot, PickupItemData itemToSlotData)
|
||||
@@ -661,10 +805,31 @@ namespace Interactions
|
||||
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
|
||||
// Determine target slot index
|
||||
int targetIndex = GetTargetSlotIndexForItem(itemToSlotData);
|
||||
|
||||
if (targetIndex >= 0)
|
||||
{
|
||||
// Positional slotting - ensure lists are large enough
|
||||
while (slottedItemObjects.Count <= targetIndex)
|
||||
{
|
||||
slottedItemObjects.Add(null);
|
||||
slottedItemsData.Add(null);
|
||||
slottedItemCorrectness.Add(false);
|
||||
}
|
||||
|
||||
// Place at specific index
|
||||
slottedItemObjects[targetIndex] = itemToSlot;
|
||||
slottedItemsData[targetIndex] = itemToSlotData;
|
||||
slottedItemCorrectness[targetIndex] = isCorrectItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Append behavior (backward compatible or when all slots full)
|
||||
slottedItemObjects.Add(itemToSlot);
|
||||
slottedItemsData.Add(itemToSlotData);
|
||||
slottedItemCorrectness.Add(isCorrectItem);
|
||||
}
|
||||
|
||||
// Deactivate item and set pickup state
|
||||
itemToSlot.SetActive(false);
|
||||
|
||||
Reference in New Issue
Block a user