2025-11-11 20:25:23 +01:00
|
|
|
|
using AppleHills.Data.CardSystem;
|
2025-11-12 14:39:38 +01:00
|
|
|
|
using Core;
|
2025-11-11 20:25:23 +01:00
|
|
|
|
using Core.SaveLoad;
|
2025-11-12 14:39:38 +01:00
|
|
|
|
using UI.DragAndDrop.Core;
|
2025-11-11 20:25:23 +01:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
|
|
namespace UI.CardSystem.StateMachine
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Main Card controller component.
|
|
|
|
|
|
/// Orchestrates the card state machine, context, and animator.
|
2025-11-12 14:39:38 +01:00
|
|
|
|
/// Inherits from DraggableObject to provide drag/drop capabilities for album placement.
|
2025-11-11 20:25:23 +01:00
|
|
|
|
/// This is the single entry point for working with cards.
|
|
|
|
|
|
/// </summary>
|
2025-11-12 14:39:38 +01:00
|
|
|
|
public class Card : DraggableObject
|
2025-11-11 20:25:23 +01:00
|
|
|
|
{
|
|
|
|
|
|
[Header("Components")]
|
|
|
|
|
|
[SerializeField] private CardContext context;
|
|
|
|
|
|
[SerializeField] private CardAnimator animator;
|
|
|
|
|
|
[SerializeField] private AppleMachine stateMachine;
|
|
|
|
|
|
|
|
|
|
|
|
[Header("Configuration")]
|
|
|
|
|
|
[SerializeField] private string initialState = "IdleState";
|
|
|
|
|
|
|
|
|
|
|
|
// Public accessors
|
|
|
|
|
|
public CardContext Context => context;
|
|
|
|
|
|
public CardAnimator Animator => animator;
|
|
|
|
|
|
public AppleMachine StateMachine => stateMachine;
|
|
|
|
|
|
public CardData CardData => context?.CardData;
|
|
|
|
|
|
|
2025-11-16 20:35:54 +01:00
|
|
|
|
// State inspection properties for booster flow
|
|
|
|
|
|
public bool IsIdle => GetCurrentStateName() == "IdleState";
|
|
|
|
|
|
public bool IsRevealing => !IsIdle && !IsComplete;
|
|
|
|
|
|
public bool IsComplete => context?.HasCompletedReveal ?? false;
|
|
|
|
|
|
|
|
|
|
|
|
// Event fired when this card is successfully placed into an AlbumCardSlot
|
|
|
|
|
|
public event System.Action<Card, AlbumCardSlot> OnPlacedInAlbumSlot;
|
|
|
|
|
|
|
2025-11-12 14:39:38 +01:00
|
|
|
|
protected override void Initialize()
|
2025-11-11 20:25:23 +01:00
|
|
|
|
{
|
2025-11-12 14:39:38 +01:00
|
|
|
|
base.Initialize(); // Call DraggableObject initialization
|
|
|
|
|
|
|
2025-11-11 20:25:23 +01:00
|
|
|
|
// Auto-find components if not assigned
|
|
|
|
|
|
if (context == null)
|
|
|
|
|
|
context = GetComponent<CardContext>();
|
|
|
|
|
|
|
|
|
|
|
|
if (animator == null)
|
|
|
|
|
|
animator = GetComponent<CardAnimator>();
|
|
|
|
|
|
|
|
|
|
|
|
if (stateMachine == null)
|
|
|
|
|
|
stateMachine = GetComponentInChildren<AppleMachine>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-12 14:39:38 +01:00
|
|
|
|
#region DraggableObject Hooks - Trigger State Transitions
|
|
|
|
|
|
|
|
|
|
|
|
protected override void OnDragStartedHook()
|
|
|
|
|
|
{
|
|
|
|
|
|
base.OnDragStartedHook();
|
|
|
|
|
|
|
|
|
|
|
|
// Transition to dragging state when drag begins
|
|
|
|
|
|
Logging.Debug($"[Card] Drag started on {CardData?.Name}, transitioning to DraggingState");
|
|
|
|
|
|
ChangeState("DraggingState");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void OnDragEndedHook()
|
|
|
|
|
|
{
|
|
|
|
|
|
base.OnDragEndedHook();
|
|
|
|
|
|
|
|
|
|
|
|
// Check if we dropped in a valid album slot
|
|
|
|
|
|
if (CurrentSlot is AlbumCardSlot albumSlot)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logging.Debug($"[Card] Dropped in album slot, transitioning to PlacedInSlotState");
|
|
|
|
|
|
|
|
|
|
|
|
// Set the parent slot on PlacedInSlotState
|
|
|
|
|
|
var placedState = GetStateComponent<States.CardPlacedInSlotState>("PlacedInSlotState");
|
|
|
|
|
|
if (placedState != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
placedState.SetParentSlot(albumSlot);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ChangeState("PlacedInSlotState");
|
2025-11-16 20:35:54 +01:00
|
|
|
|
|
|
|
|
|
|
// Notify listeners (AlbumViewPage) that this pending card was placed
|
|
|
|
|
|
OnPlacedInAlbumSlot?.Invoke(this, albumSlot);
|
2025-11-12 14:39:38 +01:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Logging.Debug($"[Card] Dropped outside valid slot, returning to RevealedState");
|
|
|
|
|
|
ChangeState("RevealedState");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2025-11-11 20:25:23 +01:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Setup the card with data and optional initial state
|
|
|
|
|
|
/// </summary>
|
2025-11-16 20:35:54 +01:00
|
|
|
|
public void SetupCard(CardData data, string startState = null)
|
2025-11-11 20:25:23 +01:00
|
|
|
|
{
|
|
|
|
|
|
if (context != null)
|
|
|
|
|
|
{
|
2025-11-16 20:35:54 +01:00
|
|
|
|
context.SetupCard(data);
|
2025-11-11 20:25:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Start the state machine with specified or default state
|
|
|
|
|
|
string targetState = startState ?? initialState;
|
|
|
|
|
|
if (stateMachine != null && !string.IsNullOrEmpty(targetState))
|
|
|
|
|
|
{
|
|
|
|
|
|
stateMachine.ChangeState(targetState);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Setup for booster reveal flow (starts at IdleState, will flip on click)
|
2025-11-12 14:39:38 +01:00
|
|
|
|
/// Dragging is DISABLED for booster cards
|
2025-11-16 20:35:54 +01:00
|
|
|
|
/// States will query CardSystemManager for collection state as needed
|
2025-11-11 20:25:23 +01:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void SetupForBoosterReveal(CardData data, bool isNew)
|
|
|
|
|
|
{
|
2025-11-16 20:35:54 +01:00
|
|
|
|
SetupCard(data, "IdleState");
|
2025-11-12 14:39:38 +01:00
|
|
|
|
SetDraggingEnabled(false); // Booster cards cannot be dragged
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Setup for album placement flow (starts at RevealedState, can be dragged)
|
|
|
|
|
|
/// Dragging is ENABLED for album placement cards
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void SetupForAlbumPlacement(CardData data)
|
|
|
|
|
|
{
|
2025-11-16 20:35:54 +01:00
|
|
|
|
SetupCard(data, "RevealedState");
|
2025-11-12 14:39:38 +01:00
|
|
|
|
SetDraggingEnabled(true); // Album placement cards can be dragged
|
2025-11-11 20:25:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Setup for album placement (starts at PlacedInSlotState)
|
2025-11-12 14:39:38 +01:00
|
|
|
|
/// Dragging is DISABLED once placed in slot
|
2025-11-11 20:25:23 +01:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void SetupForAlbumSlot(CardData data, AlbumCardSlot slot)
|
|
|
|
|
|
{
|
2025-11-16 20:35:54 +01:00
|
|
|
|
SetupCard(data, "PlacedInSlotState");
|
2025-11-12 14:39:38 +01:00
|
|
|
|
SetDraggingEnabled(false); // Cards in slots cannot be dragged out
|
2025-11-11 20:25:23 +01:00
|
|
|
|
|
|
|
|
|
|
// Set the parent slot on the PlacedInSlotState
|
|
|
|
|
|
var placedState = GetStateComponent<States.CardPlacedInSlotState>("PlacedInSlotState");
|
|
|
|
|
|
if (placedState != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
placedState.SetParentSlot(slot);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Transition to a specific state
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void ChangeState(string stateName)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (stateMachine != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
stateMachine.ChangeState(stateName);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Get a specific state component by name
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public T GetStateComponent<T>(string stateName) where T : AppleState
|
|
|
|
|
|
{
|
|
|
|
|
|
if (stateMachine == null) return null;
|
|
|
|
|
|
|
|
|
|
|
|
Transform stateTransform = stateMachine.transform.Find(stateName);
|
|
|
|
|
|
if (stateTransform != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return stateTransform.GetComponent<T>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Get the current active state name
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public string GetCurrentStateName()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (stateMachine?.currentState != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return stateMachine.currentState.name;
|
|
|
|
|
|
}
|
|
|
|
|
|
return "None";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|