using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using System; // for Action using Core; // register with ItemManager namespace Interactions { // New enum describing possible states for the slotted item public enum ItemSlotState { None, Correct, Incorrect, Forbidden } /// /// Interaction requirement that allows slotting, swapping, or picking up items in a slot. /// [RequireComponent(typeof(Interactable))] public class ItemSlot : Pickup { // Tracks the current state of the slotted item private ItemSlotState _currentState = ItemSlotState.None; /// /// Read-only access to the current slotted item state. /// public ItemSlotState CurrentSlottedState => _currentState; public UnityEvent onItemSlotted; public UnityEvent onItemSlotRemoved; // Native C# event alternative for code-only subscribers public event Action OnItemSlotRemoved; public UnityEvent onCorrectItemSlotted; // Native C# event alternative to the UnityEvent for code-only subscribers public event Action OnCorrectItemSlotted; public UnityEvent onIncorrectItemSlotted; // Native C# event alternative for code-only subscribers public event Action OnIncorrectItemSlotted; public UnityEvent onForbiddenItemSlotted; // Native C# event alternative for code-only subscribers public event Action OnForbiddenItemSlotted; private PickupItemData _currentlySlottedItemData; public SpriteRenderer slottedItemRenderer; private GameObject _currentlySlottedItemObject = null; public GameObject GetSlottedObject() { return _currentlySlottedItemObject; } public void SetSlottedObject(GameObject obj) { _currentlySlottedItemObject = obj; if (_currentlySlottedItemObject != null) { _currentlySlottedItemObject.SetActive(false); } } protected override void OnCharacterArrived() { Debug.Log("[ItemSlot] OnCharacterArrived"); var heldItemData = FollowerController.CurrentlyHeldItemData; var heldItemObj = FollowerController.GetHeldPickupObject(); var config = GameManager.Instance.GetSlotItemConfig(itemData); var forbidden = config?.forbiddenItems ?? new List(); // Held item, slot empty -> try to slot item if (heldItemData != null && _currentlySlottedItemObject == null) { // First check for forbidden items at the very start so we don't continue unnecessarily if (PickupItemData.ListContainsEquivalent(forbidden, heldItemData)) { DebugUIMessage.Show("Can't place that here.", Color.red); onForbiddenItemSlotted?.Invoke(); OnForbiddenItemSlotted?.Invoke(itemData, heldItemData); _currentState = ItemSlotState.Forbidden; Interactable.BroadcastInteractionComplete(false); return; } SlotItem(heldItemObj, heldItemData, true); return; } // Either pickup or swap items if ((heldItemData == null && _currentlySlottedItemObject != null) || (heldItemData != null && _currentlySlottedItemObject != null)) { FollowerController.TryPickupItem(_currentlySlottedItemObject, _currentlySlottedItemData, false); onItemSlotRemoved?.Invoke(); OnItemSlotRemoved?.Invoke(_currentlySlottedItemData); _currentState = ItemSlotState.None; SlotItem(heldItemObj, heldItemData, _currentlySlottedItemObject == null); return; } // No held item, slot empty -> show warning if (heldItemData == null && _currentlySlottedItemObject == null) { DebugUIMessage.Show("This requires an item.", Color.red); return; } } /// /// Updates the sprite and scale for the currently slotted item. /// private void UpdateSlottedSprite() { if (slottedItemRenderer != null && _currentlySlottedItemData != null && _currentlySlottedItemData.mapSprite != null) { slottedItemRenderer.sprite = _currentlySlottedItemData.mapSprite; // Scale sprite to desired height, preserve aspect ratio, compensate for parent scale float desiredHeight = GameManager.Instance.HeldIconDisplayHeight; var sprite = _currentlySlottedItemData.mapSprite; float spriteHeight = sprite.bounds.size.y; float spriteWidth = sprite.bounds.size.x; Vector3 parentScale = slottedItemRenderer.transform.parent != null ? slottedItemRenderer.transform.parent.localScale : Vector3.one; if (spriteHeight > 0f) { float uniformScale = desiredHeight / spriteHeight; float scale = uniformScale / Mathf.Max(parentScale.x, parentScale.y); slottedItemRenderer.transform.localScale = new Vector3(scale, scale, 1f); } } else if (slottedItemRenderer != null) { slottedItemRenderer.sprite = null; } } public void SlotItem(GameObject itemToSlot, PickupItemData itemToSlotData, bool clearFollowerHeldItem = true) { // Cache the previous item data before clearing, needed for OnItemSlotRemoved event var previousItemData = _currentlySlottedItemData; bool wasSlotCleared = _currentlySlottedItemObject != null && itemToSlot == null; if (itemToSlot == null) { _currentlySlottedItemObject = null; _currentlySlottedItemData = null; // Clear state when no item is slotted _currentState = ItemSlotState.None; // Fire native event for slot clearing if (wasSlotCleared) { OnItemSlotRemoved?.Invoke(previousItemData); } } else { itemToSlot.SetActive(false); itemToSlot.transform.SetParent(null); SetSlottedObject(itemToSlot); _currentlySlottedItemData = itemToSlotData; } if (clearFollowerHeldItem) { 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 = GameManager.Instance.GetSlotItemConfig(itemData); var allowed = config?.allowedItems ?? new List(); if (itemToSlotData != null && PickupItemData.ListContainsEquivalent(allowed, itemToSlotData)) { if (itemToSlot != null) { DebugUIMessage.Show("You correctly slotted " + itemToSlotData.itemName + " into: " + itemData.itemName, Color.green); onCorrectItemSlotted?.Invoke(); OnCorrectItemSlotted?.Invoke(itemData, _currentlySlottedItemData); _currentState = ItemSlotState.Correct; } Interactable.BroadcastInteractionComplete(true); } else { if (itemToSlot != null) { DebugUIMessage.Show("I'm not sure this works.", Color.yellow); onIncorrectItemSlotted?.Invoke(); OnIncorrectItemSlotted?.Invoke(itemData, _currentlySlottedItemData); _currentState = ItemSlotState.Incorrect; } Interactable.BroadcastInteractionComplete(false); } } // Register with ItemManager when enabled void Start() { ItemManager.Instance?.RegisterItemSlot(this); } void OnDestroy() { ItemManager.Instance?.UnregisterItemSlot(this); } } }