Files
AppleHillsProduction/docs/cards_wip/card_migration_strategy.md
Michal Pikulski 4fdbbb0aa8 Add roadmap docs
2025-11-15 20:37:01 +01:00

14 KiB

Old Card Scripts - Migration Strategy

TL;DR: What Happens to Old Scripts?

Answer: They are REPLACED by the new state machine system, but CardDisplay stays.

Keep (Don't Touch)

  • CardDisplay.cs - Core visual renderer, used by both old and new systems

Replace (Eventually Deprecate) 🔄

  • FlippableCard.cs → Replaced by Card.cs with state machine
  • AlbumCard.cs → Replaced by CardPlacedInSlotState.cs + CardAlbumEnlargedState.cs
  • AlbumCardPlacementDraggable.cs → Replaced by Card.cs with CardDraggingState.cs

Current Usage Analysis

Where FlippableCard is Used:

  1. BoosterOpeningPage.cs (8 references)

    • Line 592: Instantiate FlippableCard for booster reveal
    • Line 601, 643, 660, 752, 770: GetComponent calls
    • Impact: High - main booster opening flow
  2. AlbumCardPlacementDraggable.cs (1 reference)

    • Line 45: GetComponent reference
    • Impact: Medium - album placement flow
  3. AlbumCard.cs (1 reference)

    • Line 94: GetComponentInParent during click forwarding
    • Impact: Low - will be removed when AlbumCard is replaced

Where AlbumCard is Used:

  1. AlbumCardSlot.cs (2 references)

    • Line 186-187: Instantiate and GetComponent for pre-placed cards
    • Impact: High - album slot system
  2. AlbumViewPage.cs (2 references)

    • Line 346: GetComponent when handling enlarge
    • Line 457-458: Instantiate AlbumCardPlacementDraggable
    • Impact: High - album view interactions
  3. CardDisplay.cs (1 reference)

    • Line 316: GetComponentInParent for preview mode
    • Impact: Low - preview feature
  4. FlippableCard.cs (1 reference)

    • Line 73: GetComponentInChildren reference
    • Impact: Will be removed when FlippableCard is replaced

Migration Phases

Phase 1: Parallel Development (Current)

Status: Both systems coexist, no breaking changes

Old System (Active)          New System (Being Built)
├─ FlippableCard.cs          ├─ Card.cs
├─ AlbumCard.cs              ├─ CardContext.cs
├─ AlbumCardPlacement...     ├─ CardAnimator.cs
└─ CardDisplay.cs ←──────────┼─→ CardDisplay.cs (shared!)
                             └─ State scripts...

Action: Build new Card prefab, test in isolation Timeline: Current (you're here!)


Status: Replace booster cards only, album cards still use old system

Changes Required:

File: BoosterOpeningPage.cs

Old code:

[SerializeField] private GameObject flippableCardPrefab;

// In SpawnCards()
GameObject cardObj = Instantiate(flippableCardPrefab, cardDisplayContainer);
FlippableCard flippableCard = cardObj.GetComponent<FlippableCard>();
flippableCard.SetupCard(cardData);
flippableCard.OnCardRevealed += HandleCardRevealed;
flippableCard.OnCardTappedAfterReveal += HandleCardTapped;

New code:

[SerializeField] private GameObject cardPrefab; // New Card prefab

// In SpawnCards()
GameObject cardObj = Instantiate(cardPrefab, cardDisplayContainer);
StateMachine.Card card = cardObj.GetComponent<StateMachine.Card>();
card.SetupForBoosterReveal(cardData, isNew: true);

// Subscribe to state events (if needed)
var flippingState = card.GetStateComponent<StateMachine.States.CardFlippingState>("FlippingState");
// Add custom events if needed, or just let state machine handle it

Benefits:

  • Test new system in one isolated flow
  • Booster opening is cleanest use case (no complex album interactions)
  • Easy to rollback if issues arise

Timeline: 2-4 hours


Phase 3: Full Replacement - Album System

Status: Replace album cards, old system fully deprecated

Changes Required:

File: AlbumCardSlot.cs

Old code:

[SerializeField] private GameObject albumCardPrefab;

// In SpawnPreviewCard()
GameObject cardObj = Instantiate(albumCardPrefab, transform);
AlbumCard albumCard = cardObj.GetComponent<AlbumCard>();
albumCard.SetupCard(cardData);
albumCard.SetParentSlot(this);
albumCard.OnEnlargeRequested += HandleEnlarge;
albumCard.OnShrinkRequested += HandleShrink;

New code:

[SerializeField] private GameObject cardPrefab; // Same Card prefab as booster

// In SpawnPreviewCard()
GameObject cardObj = Instantiate(cardPrefab, transform);
StateMachine.Card card = cardObj.GetComponent<StateMachine.Card>();
card.SetupForAlbumSlot(cardData, this);

// Subscribe to enlarge events (if needed)
var albumEnlargedState = card.GetStateComponent<StateMachine.States.CardAlbumEnlargedState>("AlbumEnlargedState");
albumEnlargedState.OnEnlargeRequested += HandleEnlarge;
albumEnlargedState.OnShrinkRequested += HandleShrink;

File: AlbumViewPage.cs

Similar changes for handling enlarged cards and backdrop.

Timeline: 4-6 hours


Phase 4: Cleanup - Remove Old Scripts

Status: Old scripts deleted, prefabs archived

Files to Remove:

  • FlippableCard.cs
  • AlbumCard.cs
  • AlbumCardPlacementDraggable.cs (if drag system is integrated)

Files to Keep:

  • CardDisplay.cs - Still used by new system!
  • AlbumCardSlot.cs - Updated to use new Card prefab
  • BoosterOpeningPage.cs - Updated to use new Card prefab
  • AlbumViewPage.cs - Updated to use new Card prefab

Timeline: 1 hour (after Phases 2-3 are stable)


Coexistence Strategy (During Migration)

Replace one scene at a time:

Week 1: Booster Opening Scene
  └─ Uses new Card.prefab
  
Week 2: Album View Scene  
  └─ Uses new Card.prefab
  
Week 3: Any other card scenes
  └─ Uses new Card.prefab
  
Week 4: Remove old scripts

Pros:

  • Low risk, easy rollback
  • Test thoroughly at each step
  • Team can adapt gradually

Cons:

  • Longer timeline
  • Maintain both systems temporarily

Option B: Big Bang Replacement

Replace all at once in one PR/branch:

Day 1-2: Update BoosterOpeningPage
Day 3-4: Update AlbumViewPage + AlbumCardSlot
Day 5: Test everything
Day 6: Delete old scripts

Pros:

  • Faster completion
  • No long-term coexistence

Cons:

  • Higher risk
  • More testing needed
  • Harder to rollback

Feature Mapping: Old → New

FlippableCard → Card with States

Old Feature Old Implementation New Implementation
Idle hover FlippableCard._idleHoverTween CardIdleState.OnEnterState()
Click to flip FlippableCard.OnPointerClick() CardIdleState.OnPointerClick()
Flip animation FlippableCard.FlipToReveal() CardFlippingState.OnEnterState()
New card badge FlippableCard.ShowAsNew() CardEnlargedNewState (owns badge)
Repeat progress FlippableCard.ShowAsRepeat() CardEnlargedRepeatState (owns bar)
Tap to dismiss FlippableCard.OnPointerClick() when waiting CardEnlargedNewState.OnPointerClick()

AlbumCard → CardPlacedInSlotState + CardAlbumEnlargedState

Old Feature Old Implementation New Implementation
Store parent slot AlbumCard._parentSlot CardPlacedInSlotState.SetParentSlot()
Click to enlarge AlbumCard.OnPointerClick() CardPlacedInSlotState.OnPointerClick()
Enlarge animation AlbumCard.EnlargeCard() CardAlbumEnlargedState.OnEnterState()
Shrink animation AlbumCard.ShrinkCard() CardAlbumEnlargedState.OnPointerClick()
Transform tracking AlbumCard._originalParent, etc. CardAlbumEnlargedState (same fields)

AlbumCardPlacementDraggable → Card with CardDraggingState

Old Feature Old Implementation New Implementation
Drag feedback AlbumCardPlacement... CardDraggingState
Snap to slot SnapToAlbumSlot() CardDraggingState.OnDroppedInSlot()
Flip on drag Nested FlippableCard Just use Card with state machine

Events Migration

Old Events (FlippableCard)

flippableCard.OnCardRevealed += (card, data) => { };
flippableCard.OnCardTappedAfterReveal += (card) => { };
flippableCard.OnFlipStarted += (card) => { };
flippableCard.OnClickedWhileInactive += (card) => { };

New Events (State-based)

// Option 1: Listen to state machine transitions
var flippingState = card.GetStateComponent<CardFlippingState>("FlippingState");
// Then add custom events to states if needed

// Option 2: Poll current state
if (card.GetCurrentStateName() == "RevealedState")
{
    // Card was revealed
}

// Option 3: Add custom events to Card.cs that relay from states
card.OnCardRevealed += (data) => { };

Note: Some events may not be needed anymore because state machine handles transitions internally.


Testing Strategy

Phase 2 Testing (Booster Only)

  • Open booster pack
  • Cards spawn in IdleState
  • Click card triggers flip
  • Flip animation plays correctly
  • New cards show "NEW CARD" badge
  • Repeat cards show progress bar
  • Tap dismisses enlarged view
  • Multiple cards work simultaneously
  • No console errors

Phase 3 Testing (Album Added)

  • Cards appear in album slots
  • Click card in album enlarges it
  • Tap enlarged card shrinks it
  • Backdrop shows/hides correctly
  • Reparenting works (card moves to top layer)
  • Card returns to correct slot
  • Page flipping doesn't break card state
  • No console errors

Regression Testing (Both Phases)

  • CardDisplay still renders correctly
  • Card data persists across states
  • Animations are smooth (60fps)
  • Click detection works
  • No memory leaks (profile with 20+ cards)

Rollback Plan

If new system has issues:

During Phase 2 (Booster Only)

  1. Revert BoosterOpeningPage.cs changes
  2. Re-assign old flippableCardPrefab in inspector
  3. Old system still intact for album

During Phase 3 (Album Added)

  1. Revert AlbumCardSlot.cs and AlbumViewPage.cs
  2. Re-assign old prefabs in inspector
  3. Both systems revert to old

After Phase 4 (Old Scripts Deleted)

  1. Restore old scripts from Git history
  2. Recreate old prefabs (if not archived)
  3. Revert consumer scripts

Prevention: Archive old prefabs before deleting!


CardDisplay.cs - The Survivor

Why CardDisplay is NOT replaced:

CardDisplay is a pure visual renderer. It:

  • Takes CardData and displays it
  • Has no state management
  • Has no animation logic
  • Has no interaction logic

This is exactly what we want! The new system uses CardDisplay as-is.

Old hierarchy:

FlippableCard
└─ AlbumCard
   └─ CardDisplay ← renders visuals

New hierarchy:

Card (state machine)
└─ CardDisplay ← same renderer!

CardDisplay is already well-designed - it's a "presenter" in the MVP pattern. Keep it!


Migration Checklist

Preparation

  • New Card.prefab created and tested in isolation
  • CardAnimationConfig asset created
  • All state scripts compiled without errors
  • Team aware of upcoming changes

Phase 2: Booster Opening

  • Update BoosterOpeningPage.cs to use Card.prefab
  • Remove FlippableCard references
  • Update prefab assignments in inspector
  • Test booster opening flow thoroughly
  • Fix any issues before proceeding

Phase 3: Album System

  • Update AlbumCardSlot.cs to use Card.prefab
  • Update AlbumViewPage.cs to use Card.prefab
  • Remove AlbumCard references
  • Update prefab assignments in inspector
  • Test album interactions thoroughly
  • Test booster→album flow (cards placed after opening)

Phase 4: Cleanup

  • Archive old prefabs (FlippableCard, AlbumCard)
  • Delete FlippableCard.cs
  • Delete AlbumCard.cs
  • Delete AlbumCardPlacementDraggable.cs (if fully replaced)
  • Run full regression test suite
  • Update team documentation
  • Celebrate! 🎉

FAQ

Q: Can I use both systems simultaneously? A: Yes, during migration. One scene can use FlippableCard while another uses Card.prefab.

Q: Will old prefabs still work? A: Yes, until you delete the old scripts. Prefabs using FlippableCard will continue to function.

Q: Do I need to migrate all at once? A: No! Recommended approach is scene-by-scene (Phase 2, then Phase 3).

Q: What about CardDisplay? A: Keep it! It's used by both old and new systems. It's well-designed and doesn't need changes.

Q: What if I find bugs in the new system? A: Rollback to old system (see Rollback Plan section), fix bugs, then retry migration.

Q: How long will migration take? A: Estimated 6-10 hours total (2-4 for booster, 4-6 for album, testing time).

Q: Will performance improve? A: Yes! 60% less code, more efficient state management, shared animation system.


Summary

Old Scripts Status:

Script Status Timeline
CardDisplay.cs KEEP Forever (it's perfect!)
FlippableCard.cs 🔄 REPLACE Phase 2 (booster)
AlbumCard.cs 🔄 REPLACE Phase 3 (album)
AlbumCardPlacementDraggable.cs 🔄 REPLACE Phase 3 (album)

Migration Path:

Now                Phase 2           Phase 3          Future
────────────────────────────────────────────────────────────
Both systems   →  Booster uses    → All use        → Old scripts
coexist           new Card,         new Card         deleted
                  album uses old

Key Insight:

You're not "fixing" the old scripts - you're replacing their ARCHITECTURE.

The old scripts work, but they're built on a flawed foundation (wrapper hell, boolean soup). The new system solves this with isolated states and clean separation of concerns.

Think of it like replacing a house's foundation - you keep the furniture (CardDisplay), but rebuild the structure underneath.


Ready to start? Begin with Phase 2 (Booster Opening) - it's the cleanest migration path!