using UnityEngine; #if ENABLE_INPUT_SYSTEM using UnityEngine.InputSystem; #endif using System; namespace SpecialMoveCards { /// /// Holds up to two cards, exposes UseCard(slot), integrates with Animation via SpecialAnimationBridge. /// public class PlayerPocket : MonoBehaviour { [Header("Animation")] public SpecialAnimationBridge animationBridge; [Header("UI")] public UIController_Pocket pocketUI; [Tooltip("Tag used to find the Pocket UI at runtime if not assigned in inspector.")] [SerializeField] private string pocketUITag = "Pocket UI"; private Coroutine resolveUICoroutine; [Header("Input (Optional)")] #if ENABLE_INPUT_SYSTEM public InputAction useSlot1Action; // bind to keyboard 1 public InputAction useSlot2Action; // bind to keyboard 2 [Tooltip("Input System action that triggers using the next available card in the pocket (left-most).")] public InputActionReference specialMoveAction; // expected to be bound to e.g. gamepad X / keyboard E #endif [Tooltip("Allow using cards while holding only 1.")] public bool allowUseWithSingleCard = true; private CardData[] slots = new CardData[2]; public int Count { get { return (slots[0] != null ? 1 : 0) + (slots[1] != null ? 1 : 0); } } private void Awake() { // Skip if card system is disabled globally if (CardSpawner.CARD_SYSTEM_DISABLED) { Debug.Log("[PlayerPocket] Card System is DISABLED - disabling component"); enabled = false; return; } if (animationBridge == null) animationBridge = GetComponent(); // Try to auto-resolve UI early (player is often spawned from prefab) if (pocketUI == null) TryResolvePocketUIOnce(); } private void OnEnable() { #if ENABLE_INPUT_SYSTEM // Create defaults if not wired in Inspector if (useSlot1Action == null) { useSlot1Action = new InputAction(name: "UseCardSlot1", binding: "/1"); } if (useSlot2Action == null) { useSlot2Action = new InputAction(name: "UseCardSlot2", binding: "/2"); } if (useSlot1Action != null) { useSlot1Action.SafeEnable(); useSlot1Action.performed += OnUseSlot1; } if (useSlot2Action != null) { useSlot2Action.SafeEnable(); useSlot2Action.performed += OnUseSlot2; } // Hook SpecialMove input if provided if (specialMoveAction != null && specialMoveAction.action != null) { specialMoveAction.action.SafeEnable(); specialMoveAction.action.performed += OnSpecialMovePerformed; } #endif PushUI(); } private void OnDisable() { #if ENABLE_INPUT_SYSTEM if (useSlot1Action != null) useSlot1Action.performed -= OnUseSlot1; if (useSlot2Action != null) useSlot2Action.performed -= OnUseSlot2; if (useSlot1Action != null) useSlot1Action.Disable(); if (useSlot2Action != null) useSlot2Action.Disable(); if (specialMoveAction != null && specialMoveAction.action != null) { specialMoveAction.action.performed -= OnSpecialMovePerformed; specialMoveAction.action.Disable(); } #endif if (resolveUICoroutine != null) { StopCoroutine(resolveUICoroutine); resolveUICoroutine = null; } } private void Update() { // Legacy fallback if (Input.GetKeyDown(KeyCode.Alpha1)) UseCard(0); if (Input.GetKeyDown(KeyCode.Alpha2)) UseCard(1); } #if ENABLE_INPUT_SYSTEM private void OnUseSlot1(InputAction.CallbackContext ctx) { if (ctx.performed) UseCard(0); } private void OnUseSlot2(InputAction.CallbackContext ctx) { if (ctx.performed) UseCard(1); } private void OnSpecialMovePerformed(InputAction.CallbackContext ctx) { if (!ctx.performed) return; // Always consume the left-most available card (sequential use) // UseCard will compact and raise events/UI. if (slots[0] != null) { UseCard(0); return; } if (slots[1] != null) { UseCard(1); return; } // no card -> ignore } #endif /// /// Try adding a card to the first empty slot. Returns true if added. /// public bool TryAddCard(CardData data) { if (data == null) return false; if (slots[0] == null) { slots[0] = data; OnChanged(); GameEvents.RaiseCardPicked(data); return true; } if (slots[1] == null) { slots[1] = data; OnChanged(); GameEvents.RaiseCardPicked(data); return true; } return false; // full } /// /// Uses a card at slot index (0 or 1). Consumes immediately and plays mapped animation. /// public void UseCard(int slotIndex) { if (slotIndex < 0 || slotIndex > 1) return; var data = slots[slotIndex]; if (data == null) { // If only one card and user pressed empty slot, optionally redirect to occupied slot if (allowUseWithSingleCard && Count == 1) { int other = slotIndex == 0 ? 1 : 0; if (slots[other] != null) { UseCard(other); } } return; } // Consume first slots[slotIndex] = null; CompactLeft(); OnChanged(); GameEvents.RaiseCardUsed(data, slotIndex); // Play animation if (animationBridge) { animationBridge.PlaySpecialMove(data.animationTriggerName); } // Optional particles could be spawned by listening to GameEvents.CardUsed } /// /// Get a snapshot of slots (length 2). Do not mutate the returned array. /// public CardData[] GetSnapshot() { var snap = new CardData[2]; snap[0] = slots[0]; snap[1] = slots[1]; return snap; } private void CompactLeft() { // Keep items packed to the left: [A,null] => [A,null], [null,B] => [B,null] if (slots[0] == null && slots[1] != null) { slots[0] = slots[1]; slots[1] = null; } } private void OnChanged() { PushUI(); GameEvents.RaisePocketChanged(GetSnapshot(), Count); } private void PushUI() { if (pocketUI == null) { // Attempt to resolve again at first use; if still missing, start a short retry loop TryResolvePocketUIOnce(); if (pocketUI == null && resolveUICoroutine == null) resolveUICoroutine = StartCoroutine(ResolvePocketUIRoutine()); } if (pocketUI) pocketUI.UpdateUI(GetSnapshot()); } private void TryResolvePocketUIOnce() { if (string.IsNullOrEmpty(pocketUITag)) return; var go = GameObject.FindWithTag(pocketUITag); if (go == null) return; var ui = go.GetComponent(); if (ui != null) pocketUI = ui; } private System.Collections.IEnumerator ResolvePocketUIRoutine() { // Try for a short window (e.g., next 1 second) in case UI spawns after the player float timeout = 1.0f; float t = 0f; while (pocketUI == null && t < timeout) { TryResolvePocketUIOnce(); if (pocketUI != null) break; t += Time.deltaTime; yield return null; } resolveUICoroutine = null; if (pocketUI) pocketUI.UpdateUI(GetSnapshot()); else Debug.LogWarning("PlayerPocket: Could not find Pocket UI by tag '" + pocketUITag + "' within 1s. Make sure the UI exists and is tagged."); } } }