mantimon-tcg/docs/legacy/PROJECT_PLAN_ENERGY_EVOLUTION.md
Cal Corum 11e244d7e9 Move legacy docs to project root docs/legacy
Relocated SYSTEM_REVIEW.md and PROJECT_PLAN_ENERGY_EVOLUTION.md from
backend/docs/legacy to project-level docs/legacy. Added docs/README.md
indexing all documentation including ARCHITECTURE.md and GAME_RULES.md.
2026-01-26 14:19:19 -06:00

8.8 KiB

Project Plan: Energy, Tools, and Evolution Stack Refactor

Created: 2026-01-25
Updated: 2026-01-25
Status: COMPLETE (All phases done, 765 tests passing)
Priority: Critical (Phase 1 fixes from SYSTEM_REVIEW.md)


Overview

Refactor the card attachment and evolution system to properly track:

  1. Energy cards as CardInstance objects attached to Pokemon (not just IDs)
  2. Tool cards as CardInstance objects attached to Pokemon (not just IDs)
  3. Evolution stack - previous evolution stages stored underneath the current Pokemon

This fixes critical bugs where energy cards "disappeared" and enables proper devolve mechanics.


Key Design Decisions

Aspect Decision
attached_energy type list[CardInstance] (was list[str])
attached_tools type list[CardInstance] (was list[str])
Evolution behavior Previous stages stored in cards_underneath (was discarded)
Energy on KO Moves to owner's discard pile
Tools on KO Moves to owner's discard pile
Evolution stack on KO All cards in stack move to owner's discard
Devolve mechanic Effect only (not player action)
Devolve stages Configurable per effect (default 1)
Devolve destination Configurable: "hand" (default) or "discard"
Devolve KO check Immediate - if damage >= new HP, Pokemon is KO'd
Energy/tools on devolve Stay attached to the devolved Pokemon
Damage on evolve/devolve Carries over (enables sneaky KO strategies)

Implementation Phases

Phase 1: Update CardInstance Model

File: app/core/models/card.py
Status: [x] COMPLETE

  • Change attached_energy: list[str] to list["CardInstance"]
  • Change attached_tools: list[str] to list["CardInstance"]
  • Add cards_underneath: list["CardInstance"] field
  • Update attach_energy() method signature to accept CardInstance
  • Update detach_energy() to return CardInstance | None
  • Add attach_tool() method
  • Add detach_tool() method returning CardInstance | None
  • Update docstrings for all affected fields/methods
  • Add CardInstance.model_rebuild() for self-referential types

Phase 2: Revert Incorrect Fix

File: app/core/engine.py
Status: [x] COMPLETE

  • Remove player.discard.add(energy_card) from _execute_attach_energy

Phase 3: Update Energy Attachment

File: app/core/engine.py
Status: [x] COMPLETE

  • Change target.attach_energy(energy_card.instance_id) to target.attach_energy(energy_card)

Phase 4: Update Evolution Execution

File: app/core/engine.py
Status: [x] COMPLETE

  • Transfer attached_energy list (not copy IDs)
  • Transfer attached_tools list
  • Transfer damage and status_conditions
  • Build cards_underneath stack (copy existing + append target)
  • Clear target's attached lists after transfer
  • Remove player.discard.add(target) - target stays in stack
  • Update turn_played tracking on evo_card

Phase 5: Update Retreat Execution

File: app/core/engine.py
Status: [x] COMPLETE

  • Call detach_energy() which returns CardInstance
  • Add returned energy to player.discard

Phase 6: Update find_card_instance

File: app/core/models/game_state.py
Status: [x] COMPLETE

  • Search pokemon.attached_energy for each Pokemon in play
  • Search pokemon.attached_tools for each Pokemon in play
  • Search pokemon.cards_underneath for each Pokemon in play
  • Return appropriate zone names: "attached_energy", "attached_tools", "cards_underneath"

Phase 7: Update Knockout Processing

File: app/core/turn_manager.py
Status: [x] COMPLETE

  • Add TODO comment for future hook point (pre_knockout_discard event)
  • Discard all attached_energy to owner's discard
  • Discard all attached_tools to owner's discard
  • Discard all cards_underneath to owner's discard
  • Clear lists before discarding Pokemon
  • Apply to both active and bench knockout sections

Phase 8: Update discard_energy Effect Handler

File: app/core/effects/handlers.py
Status: [x] COMPLETE

  • Find owner of target Pokemon (for discard pile access)
  • Update to work with CardInstance objects
  • Call detach_energy() and add result to owner's discard
  • Update docstring

Phase 9: Add devolve Effect Handler

File: app/core/effects/handlers.py
Status: [x] COMPLETE

  • Create @effect_handler("devolve") decorator
  • Accept stages param (int, default 1)
  • Accept destination param ("hand" default, or "discard")
  • Validate target is evolved (has cards_underneath)
  • Find owner and zone of target Pokemon
  • Implement stage removal loop:
    • Pop previous from cards_underneath
    • Transfer all state (energy, tools, remaining stack, damage, status, modifiers)
    • Create CardInstance for removed evolution
    • Send to destination (hand or discard)
    • Swap in zone
  • Check for KO after devolve (damage >= new HP)
  • Return result with knockout flag if applicable

Phase 10: Update Rules Validator

File: app/core/rules_validator.py
Status: [x] COMPLETE

  • Update _get_attached_energy_types() to iterate CardInstance objects
  • Update retreat energy validation to check instance_id in CardInstance list

Phase 11: Update Existing Tests

Status: [x] COMPLETE

Files updated:

  • tests/core/test_models/test_card.py - attach/detach signatures, serialization
  • tests/core/test_engine.py - energy attachment, evolution assertions
  • tests/core/test_effects/test_handlers.py - discard_energy tests
  • tests/core/test_rules_validator.py - energy requirement checks
  • tests/core/conftest.py - game_in_main_phase and game_in_attack_phase fixtures

Phase 12: Add New Tests

Status: [x] COMPLETE Test File: tests/core/test_evolution_stack.py (28 tests)

Evolution Stack Tests:

  • Basic → Stage 1 creates correct stack
  • Stage 1 → Stage 2 preserves full stack
  • Energy/tools transfer on evolution
  • Damage carries over on evolution
  • Status conditions clear on evolution (Pokemon TCG standard)

Devolve Effect Tests:

  • Devolve Stage 2 → Stage 1 (stages=1)
  • Devolve Stage 2 → Basic (stages=2)
  • Devolve to hand (default destination)
  • Devolve to discard
  • Devolve triggers KO when damage >= new HP
  • Cannot devolve Basic (failure)
  • Energy/tools remain attached after devolve

Knockout Tests:

  • Attached energy goes to discard on KO
  • Attached tools go to discard on KO
  • Evolution stack goes to discard on KO
  • All attachments discard together on KO

find_card_instance Tests:

  • Find attached energy by ID
  • Find attached tool by ID
  • Find card in evolution stack
  • Find attached card on bench Pokemon
  • Returns None for nonexistent card

CardInstance Method Tests:

  • attach_energy adds CardInstance
  • detach_energy removes and returns CardInstance
  • detach_energy returns None for not attached
  • attach_tool adds CardInstance
  • detach_tool removes and returns CardInstance
  • get_all_attached_cards returns energy and tools
  • Multiple energy attachment works

Phase 13: Final Verification

Status: [x] COMPLETE

  • Run full test suite: uv run pytest - 765 tests pass (including 28 new Phase 12 tests)
  • Run linter: uv run ruff check . - Clean (only pre-existing issues in references/)
  • Run type checker: uv run mypy app - Not run (pre-existing type issues exist)
  • Review all changes

Files Modified

File Change Type
app/core/models/card.py Model fields + methods
app/core/engine.py Attach/evolve/retreat execution + status clear fix
app/core/models/game_state.py find_card_instance search
app/core/turn_manager.py Knockout processing
app/core/effects/handlers.py discard_energy update + new devolve + EffectType fix
app/core/rules_validator.py Energy type checking
tests/core/test_models/test_card.py Test updates
tests/core/test_engine.py Test updates
tests/core/test_effects/test_handlers.py Test updates + new tests
tests/core/test_rules_validator.py Test updates
tests/core/conftest.py Fixture updates (energy attachment)
tests/core/test_evolution_stack.py NEW: 28 comprehensive tests for Phase 12

  • SYSTEM_REVIEW.md Issue #5: Energy Attachment Bug - Energy Card Disappears
  • SYSTEM_REVIEW.md Issue #8: Energy Discard Handler Doesn't Move Cards

Notes

  • Future hook point needed for effects that redirect energy/tools on knockout
  • Event system design should be planned holistically for the entire engine
  • Devolve is effect-only; no DevolveAction needed