chunk 1: core gameplay scripts scenes runtime assets
This commit is contained in:
240
Assets/Scripts/SpecialMoveCardSystem/PlayerPocket.cs
Normal file
240
Assets/Scripts/SpecialMoveCardSystem/PlayerPocket.cs
Normal file
@@ -0,0 +1,240 @@
|
||||
using UnityEngine;
|
||||
#if ENABLE_INPUT_SYSTEM
|
||||
using UnityEngine.InputSystem;
|
||||
#endif
|
||||
using System;
|
||||
|
||||
namespace SpecialMoveCards
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds up to two cards, exposes UseCard(slot), integrates with Animation via SpecialAnimationBridge.
|
||||
/// </summary>
|
||||
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<SpecialAnimationBridge>();
|
||||
|
||||
// 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: "<Keyboard>/1");
|
||||
}
|
||||
if (useSlot2Action == null)
|
||||
{
|
||||
useSlot2Action = new InputAction(name: "UseCardSlot2", binding: "<Keyboard>/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
|
||||
|
||||
/// <summary>
|
||||
/// Try adding a card to the first empty slot. Returns true if added.
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses a card at slot index (0 or 1). Consumes immediately and plays mapped animation.
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a snapshot of slots (length 2). Do not mutate the returned array.
|
||||
/// </summary>
|
||||
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<UIController_Pocket>();
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user