Stash work!

This commit is contained in:
Michal Pikulski
2025-11-17 17:10:24 +01:00
parent c6f635f871
commit b5364a2bbc
29 changed files with 665 additions and 2444 deletions

View File

@@ -1,22 +1,46 @@
using Core.SaveLoad;
using Core;
using Core.SaveLoad;
using UnityEngine;
namespace UI.CardSystem.StateMachine.States
{
/// <summary>
/// Dragging revealed state for pending cards after flip.
/// Shows card front without badges, handles placement or return to corner.
/// Shows card front without badges, handles transition to placement after drag release.
/// Queries AlbumViewPage for page flip status instead of tracking state internally.
/// </summary>
public class CardDraggingRevealedState : AppleState, ICardStateDragHandler
{
private CardContext _context;
private Vector3 _originalScale;
// Placement info passed from PendingFaceDownState
private AlbumCardSlot _targetSlot;
private bool _dragAlreadyEnded = false; // Track if drag ended before we entered this state (instant-release case)
private void Awake()
{
_context = GetComponentInParent<CardContext>();
}
/// <summary>
/// Set target slot from previous state
/// Called by PendingFaceDownState before transition
/// </summary>
public void SetTargetSlot(AlbumCardSlot targetSlot)
{
_targetSlot = targetSlot;
}
/// <summary>
/// Set flag indicating drag already ended before entering this state
/// Called by PendingFaceDownState for instant-release case
/// </summary>
public void SetDragAlreadyEnded(bool ended)
{
_dragAlreadyEnded = ended;
}
public override void OnEnterState()
{
if (_context == null) return;
@@ -27,6 +51,14 @@ namespace UI.CardSystem.StateMachine.States
}
_originalScale = _context.RootTransform.localScale;
_context.RootTransform.localScale = _originalScale * 1.15f;
// Check if drag already ended before we entered this state (instant-release case)
if (_dragAlreadyEnded)
{
Logging.Debug("[CardDraggingRevealedState] Drag ended before state entry - handling placement immediately");
_dragAlreadyEnded = false; // Clear flag
HandlePlacement();
}
}
/// <summary>
@@ -38,21 +70,94 @@ namespace UI.CardSystem.StateMachine.States
}
/// <summary>
/// Handle drag end - just let AlbumViewPage handle placement logic
/// Stay in this state until AlbumViewPage transitions us after tween
/// Handle drag end - query AlbumViewPage for page flip status and place accordingly
/// </summary>
public bool OnCardDragEnded(CardContext ctx)
{
// Don't do anything - AlbumViewPage will:
// 1. Wait for page flip to complete
// 2. Find the correct slot
// 3. Tween card to slot
// 4. Transition to PlacedInSlotState
// Return true to prevent default behavior
HandlePlacement();
return true;
}
/// <summary>
/// Handle card placement logic - called from OnCardDragEnded or OnEnterState (instant-release)
/// </summary>
private void HandlePlacement()
{
if (_targetSlot == null)
{
Logging.Warning("[CardDraggingRevealedState] No target slot set - cannot place card");
// Return to corner
_context.StateMachine.ChangeState("PendingFaceDownState");
return;
}
// Query AlbumViewPage for page flip status
var albumPage = _context.AlbumViewPage;
if (albumPage == null)
{
Logging.Warning("[CardDraggingRevealedState] AlbumViewPage not found - placing immediately");
TransitionToPlacement(_context);
return;
}
// Check if page is still flipping
if (albumPage.IsPageFlipping)
{
// Wait for flip to complete
Logging.Debug("[CardDraggingRevealedState] Page still flipping - waiting before placement");
StartCoroutine(WaitForPageFlipThenPlace(_context, albumPage));
}
else
{
// Flip already done - place immediately
Logging.Debug("[CardDraggingRevealedState] Page flip complete - placing card immediately");
TransitionToPlacement(_context);
}
}
private System.Collections.IEnumerator WaitForPageFlipThenPlace(CardContext ctx, AlbumViewPage albumPage)
{
// Wait until page flip completes (max 0.5 seconds timeout)
float timeout = 0.5f;
float elapsed = 0f;
while (albumPage.IsPageFlipping && elapsed < timeout)
{
yield return null;
elapsed += Time.deltaTime;
}
if (elapsed >= timeout)
{
Logging.Warning("[CardDraggingRevealedState] Page flip wait timed out");
}
else
{
Logging.Debug("[CardDraggingRevealedState] Page flip completed, placing card");
}
// Now place the card
TransitionToPlacement(ctx);
}
private void TransitionToPlacement(CardContext ctx)
{
// Pass target slot to PlacedInSlotState
var card = ctx.GetComponent<Card>();
if (card != null)
{
var placedState = card.GetStateComponent<CardPlacedInSlotState>("PlacedInSlotState");
if (placedState != null)
{
placedState.SetPlacementInfo(_targetSlot);
}
}
// Transition to PlacedInSlotState
// The state will handle animation and finalization in OnEnterState
ctx.StateMachine.ChangeState("PlacedInSlotState");
}
private void OnDisable()
{
if (_context?.RootTransform != null)

View File

@@ -6,7 +6,8 @@ namespace UI.CardSystem.StateMachine.States
{
/// <summary>
/// Card is in pending face-down state in corner, awaiting drag.
/// On drag start, triggers flip animation and transitions to revealed dragging.
/// On drag start, queries AlbumViewPage for card data and slot, triggers page navigation,
/// then flips and transitions to dragging revealed state.
/// </summary>
public class CardPendingFaceDownState : AppleState, ICardStateDragHandler
{
@@ -15,6 +16,8 @@ namespace UI.CardSystem.StateMachine.States
private CardContext _context;
private bool _isFlipping;
private AlbumCardSlot _targetSlot;
private bool _dragEndedDuringFlip = false; // Track if user released before card flip animation completed
private void Awake()
{
@@ -26,6 +29,8 @@ namespace UI.CardSystem.StateMachine.States
if (_context == null) return;
_isFlipping = false;
_targetSlot = null;
_dragEndedDuringFlip = false;
// Show card back, hide card front
if (cardBackVisual != null)
@@ -39,37 +44,67 @@ namespace UI.CardSystem.StateMachine.States
_context.CardDisplay.gameObject.SetActive(false);
_context.CardDisplay.transform.localRotation = Quaternion.Euler(0, 180, 0);
}
// Scale down for corner display
_context.RootTransform.localScale = _context.OriginalScale * 0.8f;
}
/// <summary>
/// Handle drag start - triggers flip animation and page navigation
/// Handle drag start - STATE ORCHESTRATES ITS OWN FLOW
/// </summary>
public bool OnCardDragStarted(CardContext context)
{
if (_isFlipping) return true; // Already handling
// IMPORTANT: Data must be assigned by event listeners (AlbumViewPage) BEFORE we flip
// The event system guarantees this because events are synchronous
if (context.CardData == null)
// Step 1: Find AlbumViewPage
AlbumViewPage albumPage = Object.FindFirstObjectByType<AlbumViewPage>();
if (albumPage == null)
{
Logging.Warning("[CardPendingFaceDownState] OnCardDragStarted called but no CardData assigned yet!");
return true; // Don't flip without data
Logging.Warning("[CardPendingFaceDownState] AlbumViewPage not found!");
return true;
}
// Start flip animation (data is now guaranteed to be assigned)
// Step 2: Ask AlbumViewPage what card to display and prompt it to rebuild
var cardData = albumPage.GetCardForPendingSlot();
if (cardData == null)
{
Logging.Warning("[CardPendingFaceDownState] No card data available from AlbumViewPage!");
return true;
}
// Step 3: Apply card data to context
context.SetupCard(cardData);
Logging.Debug($"[CardPendingFaceDownState] Assigned card data: {cardData.Name} ({cardData.Zone})");
// Step 4: Ask AlbumViewPage for target slot
_targetSlot = albumPage.GetTargetSlotForCard(cardData);
if (_targetSlot == null)
{
Logging.Warning($"[CardPendingFaceDownState] No slot found for card {cardData.DefinitionId}");
// Still flip and show card, but won't be able to place it
}
// Step 5: Request page navigation (no callback needed - AlbumViewPage tracks state)
albumPage.NavigateToCardPage(cardData, null);
// Step 6: Start card flip animation
StartFlipAnimation();
return true; // We handled it, prevent default DraggingState transition
}
/// <summary>
/// We don't handle drag end in face-down state
/// Handle drag end - if card flip animation still in progress, flag it for next state
/// </summary>
public bool OnCardDragEnded(CardContext context)
{
return false;
if (_isFlipping)
{
// Card flip animation still in progress - user released immediately
_dragEndedDuringFlip = true;
Logging.Debug("[CardPendingFaceDownState] Drag ended during card flip - will pass to next state");
return true; // We handled it
}
return false; // Already transitioned to DraggingRevealedState, let it handle
}
private void StartFlipAnimation()
@@ -103,6 +138,24 @@ namespace UI.CardSystem.StateMachine.States
private void OnFlipComplete()
{
// Transition to dragging revealed state
// Pass target slot to next state (it will query AlbumViewPage for flip status)
var card = _context.GetComponent<Card>();
if (card != null)
{
var draggingState = card.GetStateComponent<CardDraggingRevealedState>("DraggingRevealedState");
if (draggingState != null)
{
draggingState.SetTargetSlot(_targetSlot);
// If drag ended before we transitioned, tell next state to handle placement immediately
if (_dragEndedDuringFlip)
{
draggingState.SetDragAlreadyEnded(true);
Logging.Debug("[CardPendingFaceDownState] Passing drag-ended flag to DraggingRevealedState");
}
}
}
_context.StateMachine.ChangeState("DraggingRevealedState");
}

View File

@@ -1,41 +1,130 @@
using Core;
using Core;
using Core.SaveLoad;
using Data.CardSystem;
using UnityEngine;
namespace UI.CardSystem.StateMachine.States
{
/// <summary>
/// Placed in slot state - card is in an album slot and can be clicked to enlarge.
/// Manages the parent slot reference.
/// Placed in slot state - card is being/has been placed in an album slot.
/// SMART STATE: Handles snap-to-slot animation on entry, then finalizes placement.
/// </summary>
public class CardPlacedInSlotState : AppleState, ICardClickHandler
{
private CardContext _context;
private AlbumCardSlot _parentSlot;
private AlbumCardSlot _targetSlotForAnimation; // Set by DraggingRevealedState for animated placement
private void Awake()
{
_context = GetComponentInParent<CardContext>();
}
/// <summary>
/// Set placement info from previous state (for animated placement from drag)
/// </summary>
public void SetPlacementInfo(AlbumCardSlot targetSlot)
{
_targetSlotForAnimation = targetSlot;
}
public override void OnEnterState()
{
// Ensure card front is visible and facing camera
// This is important when spawning cards directly into album (skipping booster flow)
if (_context.CardDisplay != null)
{
_context.CardDisplay.gameObject.SetActive(true);
_context.CardDisplay.transform.localRotation = Quaternion.Euler(0, 0, 0);
}
Logging.Debug($"[CardPlacedInSlotState] Card placed in slot: {_context.CardData?.Name}");
// Card is now part of the album, no special visuals needed
// Just wait for interaction
// Check if this is animated placement (from drag) or direct placement (from spawn)
if (_targetSlotForAnimation != null)
{
// Animated placement - play tween to slot
Logging.Debug($"[CardPlacedInSlotState] Animating card '{_context.CardData?.Name}' to slot");
AnimateToSlot(_targetSlotForAnimation);
}
else
{
// Direct placement (spawned in slot) - already positioned correctly
// Disable dragging for spawned cards too
var card = _context.GetComponent<Card>();
if (card != null)
{
card.SetDraggingEnabled(false);
}
Logging.Debug($"[CardPlacedInSlotState] Card '{_context.CardData?.Name}' directly placed in slot");
}
}
/// <summary>
/// Set the parent slot this card belongs to
/// Animate card to slot position and finalize placement
/// </summary>
private void AnimateToSlot(AlbumCardSlot slot)
{
var card = _context.GetComponent<Card>();
if (card == null) return;
// Reparent to slot immediately, keeping world position
card.transform.SetParent(slot.transform, true);
// Tween position, scale, rotation simultaneously
float tweenDuration = 0.4f;
Pixelplacement.Tween.LocalPosition(card.transform, Vector3.zero, tweenDuration, 0f, Pixelplacement.Tween.EaseOutBack);
Pixelplacement.Tween.LocalScale(card.transform, Vector3.one, tweenDuration, 0f, Pixelplacement.Tween.EaseOutBack);
Pixelplacement.Tween.LocalRotation(card.transform, Quaternion.identity, tweenDuration, 0f, Pixelplacement.Tween.EaseOutBack,
completeCallback: () => FinalizePlacement(card, slot));
}
/// <summary>
/// Finalize placement after animation completes
/// </summary>
private void FinalizePlacement(Card card, AlbumCardSlot slot)
{
// Ensure final position/rotation
card.transform.localPosition = Vector3.zero;
card.transform.localRotation = Quaternion.identity;
// Resize to match slot
RectTransform cardRect = card.transform as RectTransform;
RectTransform slotRect = slot.transform as RectTransform;
if (cardRect != null && slotRect != null)
{
float targetHeight = slotRect.rect.height;
cardRect.sizeDelta = new Vector2(cardRect.sizeDelta.x, targetHeight);
}
// Set parent slot
_parentSlot = slot;
// Disable dragging - cards in slots should only respond to clicks for enlargement
card.SetDraggingEnabled(false);
// Notify slot
slot.AssignCard(card);
// Mark as placed in inventory
if (CardSystemManager.Instance != null)
{
CardSystemManager.Instance.MarkCardAsPlaced(card.CardData);
}
// Notify AlbumViewPage for registration
var albumPage = Object.FindFirstObjectByType<AlbumViewPage>();
if (albumPage != null)
{
albumPage.NotifyCardPlaced(card);
}
Logging.Debug($"[CardPlacedInSlotState] Card placement finalized: {card.CardData?.Name}");
// Clear animation target
_targetSlotForAnimation = null;
}
/// <summary>
/// Set the parent slot this card belongs to (for direct placement without animation)
/// </summary>
public void SetParentSlot(AlbumCardSlot slot)
{