Stash work

This commit is contained in:
Michal Pikulski
2025-11-17 08:39:41 +01:00
parent 78aafb9275
commit 7aca1a17ac
13 changed files with 792 additions and 1845 deletions

View File

@@ -0,0 +1,460 @@
# Album Card Placement Flow - Refactored Design
## Current State Analysis
### Existing Flow (Pre-Refactor):
1. Pending cards spawn face-up in bottom-right slots
2. User drags card to album slot
3. Card placement triggers inventory move
4. Next card spawns
### Problems:
- Cards spawn face-up (should be face-down)
- No "smart selection" from pending queue based on current album page
- No auto-flip to correct album page when card is picked up
- States don't support "hold to reveal" behavior
---
## Proposed New Flow
### Visual Journey:
```
[Face-Down Card in Corner]
↓ (user holds/drags)
[Card Flips to Reveal] + [Album auto-flips to correct page]
↓ (user drags over album)
[Card hovers over slot]
↓ (user releases)
[Card snaps to slot] → [Revealed State in slot]
```
### Technical Implementation:
---
## 1. New Card States
### A. `CardPendingFaceDownState`
**Purpose:** Initial state for cards in pending corner slots
**Visuals:**
- Card back visible (card front hidden)
- Small scale (to fit in corner slot)
- Idle in corner
**Behavior:**
- Does NOT respond to clicks
- Responds to drag start (OnDragStarted)
- On drag start → trigger smart card selection + flip animation
**Transitions:**
- OnDragStarted → `CardFlippingPendingState`
---
### B. `CardFlippingPendingState`
**Purpose:** Transition state while card flips and album navigates
**Visuals:**
- Flip animation (card back → card front)
- Card follows cursor during flip
**Behavior:**
- Play flip animation (uses CardAnimator.PlayFlip)
- Emit event to AlbumViewPage to navigate to card's page
- Wait for flip animation complete
**Transitions:**
- OnFlipComplete → `CardDraggingRevealedState`
---
### C. `CardDraggingRevealedState`
**Purpose:** Card is revealed and being dragged around album
**Visuals:**
- Card front visible
- No badges (clean revealed state)
- Follow cursor/drag position
- Slight scale-up while dragging
**Behavior:**
- Respond to drag position updates
- Detect when hovering over valid AlbumCardSlot
- Visual feedback when over valid slot
- On drag end → snap to slot if valid, otherwise return to corner
**Transitions:**
- OnDragEnd (over valid slot) → slot's `PlacedInSlotState`
- OnDragEnd (invalid) → `CardPendingFaceDownState` (return to corner, flip back)
---
## 2. Smart Card Selection System
### AlbumViewPage Responsibilities:
```csharp
public class AlbumViewPage
{
private List<CardData> _pendingQueue; // All pending cards
private List<Card> _cornerCards; // 3 face-down card GameObjects in corner
private int _currentAlbumPageIndex;
/// <summary>
/// When user starts dragging ANY corner card, we pick which pending card to reveal
/// </summary>
private void OnCornerCardDragStarted(Card cornerCard)
{
// 1. Get current album page's expected cards
var currentPageSlots = GetSlotsOnCurrentPage();
var currentPageDefinitions = currentPageSlots
.Select(slot => slot.TargetCardDefinition)
.ToList();
// 2. Try to find a pending card that belongs on this page
CardData selectedCard = _pendingQueue.FirstOrDefault(card =>
currentPageDefinitions.Any(def => def.Id == card.DefinitionId && def.Rarity == card.Rarity)
);
// 3. If none on current page, pick random pending
if (selectedCard == null)
{
selectedCard = _pendingQueue[Random.Range(0, _pendingQueue.Count)];
// Navigate album to the page where this card belongs
int targetPage = FindPageForCard(selectedCard);
NavigateToPage(targetPage);
}
// 4. Assign the selected card data to the corner card being dragged
cornerCard.Context.SetupCard(selectedCard);
// 5. Trigger flip (handled by state)
cornerCard.Context.StateMachine.ChangeState("FlippingPendingState");
}
}
```
---
## 3. Card.cs Extensions
### New Setup Method:
```csharp
public class Card
{
/// <summary>
/// Setup for album pending placement (starts face-down in corner)
/// </summary>
public void SetupForAlbumPending()
{
// Start with NO card data (will be assigned on drag)
SetupCard(null, "PendingFaceDownState");
SetDraggingEnabled(true); // Enable drag immediately
}
}
```
### Drag Event Routing:
```csharp
// In Card.cs
public event Action<Card> OnDragStartedEvent;
private void OnDragStarted()
{
OnDragStartedEvent?.Invoke(this);
}
```
---
## 4. AlbumViewPage Modifications
### Spawn Pending Cards (Face-Down):
```csharp
private void SpawnPendingCards()
{
// Spawn up to 3 "blank" face-down cards in corner
for (int i = 0; i < MAX_VISIBLE_CARDS; i++)
{
GameObject cardObj = Instantiate(cardPrefab, bottomRightSlots.transform);
var card = cardObj.GetComponent<StateMachine.Card>();
if (card != null)
{
// Setup as pending (no data yet, face-down)
card.SetupForAlbumPending();
// Subscribe to drag start
card.OnDragStartedEvent += OnCornerCardDragStarted;
// Assign to corner slot
DraggableSlot slot = FindSlotByIndex(i);
card.AssignToSlot(slot, true);
_cornerCards.Add(card);
}
}
}
```
### Handle Drag Start (Smart Selection):
```csharp
private void OnCornerCardDragStarted(Card cornerCard)
{
if (_pendingQueue.Count == 0) return;
// Smart selection logic (from section 2)
CardData selectedCard = SelectSmartPendingCard();
// Assign data to the dragged corner card
cornerCard.Context.SetupCard(selectedCard);
// State transition to flipping (handled by state machine)
// FlippingPendingState will trigger flip animation + album navigation
}
```
### Navigate to Card's Page:
```csharp
public void NavigateToCardPage(CardData card)
{
int targetPage = FindPageForCard(card);
if (targetPage != _currentAlbumPageIndex)
{
// Trigger book page flip animation
bookController.FlipToPage(targetPage);
}
}
```
---
## 5. State Implementation Details
### CardPendingFaceDownState.cs
```csharp
public class CardPendingFaceDownState : AppleState
{
private CardContext _context;
public override void OnEnterState()
{
// Show card back, hide card front
if (_context.CardDisplay != null)
{
_context.CardDisplay.gameObject.SetActive(false); // Hide front
}
var cardBack = GetComponentInChildren<CardBack>(); // Assumes CardBack component exists
if (cardBack != null)
{
cardBack.gameObject.SetActive(true);
}
// Small scale for corner slot
_context.RootTransform.localScale = Vector3.one * 0.8f;
}
}
```
### CardFlippingPendingState.cs
```csharp
public class CardFlippingPendingState : AppleState
{
private CardContext _context;
public override void OnEnterState()
{
// Notify album page to navigate
var albumPage = FindObjectOfType<AlbumViewPage>();
if (albumPage != null)
{
albumPage.NavigateToCardPage(_context.CardData);
}
// Play flip animation
if (_context.Animator != null)
{
_context.Animator.PlayFlip(
startRotation: Quaternion.Euler(0, 180, 0), // back facing
endRotation: Quaternion.identity, // front facing
onComplete: OnFlipComplete
);
}
}
private void OnFlipComplete()
{
_context.StateMachine.ChangeState("DraggingRevealedState");
}
}
```
### CardDraggingRevealedState.cs
```csharp
public class CardDraggingRevealedState : AppleState
{
private CardContext _context;
private AlbumCardSlot _hoveredSlot;
public override void OnEnterState()
{
// Card front visible, clean revealed (no badges)
if (_context.CardDisplay != null)
{
_context.CardDisplay.gameObject.SetActive(true);
}
// Slightly larger while dragging
_context.Animator.PlayEnlarge(1.2f);
}
void Update()
{
// Detect hover over valid album slots
_hoveredSlot = DetectValidSlotUnderCursor();
if (_hoveredSlot != null)
{
// Visual feedback: highlight slot or card
}
}
public void OnDragEnded()
{
if (_hoveredSlot != null && _hoveredSlot.CanAcceptCard(_context.CardData))
{
// Snap to slot and transition to PlacedInSlotState
SnapToSlot(_hoveredSlot);
}
else
{
// Return to corner, flip back to face-down
ReturnToCorner();
}
}
}
```
---
## 6. Required New Components
### CardBack Component
```csharp
public class CardBack : MonoBehaviour
{
[SerializeField] private Image backImage;
public void Show() => gameObject.SetActive(true);
public void Hide() => gameObject.SetActive(false);
}
```
Attach to Card prefab as a sibling to CardDisplay.
---
## 7. Prefab Structure
```
Card (GameObject)
├── StateMachine (AppleMachine)
│ ├── PendingFaceDownState
│ ├── FlippingPendingState
│ ├── DraggingRevealedState
│ ├── PlacedInSlotState (existing)
│ └── ... (other states)
├── CardContext
├── CardAnimator
├── CardDisplay (front visuals)
├── CardBack (back visuals - NEW)
└── DraggableObject
```
---
## 8. Migration Steps
### Step 1: Create New States
- CardPendingFaceDownState.cs
- CardFlippingPendingState.cs
- CardDraggingRevealedState.cs
### Step 2: Add CardBack Component
- Create CardBack.cs script
- Add CardBack GameObject to Card prefab
- Design card back visual (sprite, frame, etc.)
### Step 3: Update Card.cs
- Add SetupForAlbumPending() method
- Add OnDragStartedEvent
- Wire drag events to state machine
### Step 4: Update AlbumViewPage
- Modify SpawnPendingCards() to spawn face-down
- Implement smart selection logic
- Add NavigateToCardPage() method
- Connect to book flip controller
### Step 5: Update CardAnimator
- Ensure PlayFlip() can handle arbitrary start/end rotations
- Add any needed drag-follow animation helpers
### Step 6: Testing
- Test corner card drag → flip → album navigation
- Test smart selection (page match prioritization)
- Test return-to-corner on invalid drop
- Test snap-to-slot on valid drop
- Test multiple cards in queue
---
## 9. Edge Cases & Considerations
### No Pending Cards
- Don't spawn corner cards if pending queue is empty
- Hide corner slots when no cards to place
### Album Page Navigation During Drag
- Lock page flipping while dragging (prevent user manual flip)
- Queue navigation if flip animation in progress
### Multiple Cards Dragged Simultaneously
- Only allow one card to be in FlippingPending/DraggingRevealed at a time
- Disable other corner cards while one is being dragged
### Card Returns to Corner
- Flip back animation (reverse of reveal)
- Re-enter PendingFaceDownState
- Unassign CardData (become "blank" again for next drag)
### Invalid Slot Drop
- Visual feedback (shake, red highlight)
- Smooth return animation to corner
---
## 10. Benefits of This Approach
**Consistent State Architecture** - Uses same state machine pattern as booster flow
**Smart UX** - Auto-navigation to correct album page
**Clean Separation** - States handle visuals/behavior, page handles logic
**Reusable** - States can be reused for other card flows
**Extensible** - Easy to add new behaviors (e.g., card preview on hover)
**Testable** - Each state can be tested independently
---
## Open Questions for Approval
1. **Card Back Design:** Should we use a generic back for all cards, or rarity-specific backs?
2. **Navigation Timing:** Should album flip happen instantly or animated during card flip?
3. **Return Animation:** Fast snap-back or gentle float-back when invalid drop?
4. **Multiple Rarities:** If pending queue has same card at multiple rarities, which to prioritize?
5. **Corner Slot Count:** Keep at 3, or make configurable?
---
Ready to implement once approved! 🎉