mantimon-tcg/backend/app/core/models/enums.py
Cal Corum dba2813f80 Add effects system with configurable weakness/resistance
Effects System (Week 3):
- EffectContext: helper methods for player/card access, params, coin flips
- EffectResult: success, message, effect_type, details for logging
- @effect_handler decorator with sync/async support and introspection
- resolve_effect() for executing effects by ID

Built-in Handlers (13 total):
- deal_damage: raw damage primitive (poison, burn, recoil)
- attack_damage: combat damage with modifiers, weakness, resistance
- heal, draw_cards, discard_from_hand, shuffle_deck
- apply_status, remove_status
- coin_flip_damage, bench_damage
- discard_energy, modify_hp, modify_retreat_cost

Configurable Weakness/Resistance:
- ModifierMode enum: MULTIPLICATIVE (x2) or ADDITIVE (+20)
- CombatConfig in RulesConfig for game-wide defaults
- WeaknessResistance supports per-card mode/value overrides
- Legacy 'modifier' field maintained for backwards compatibility

Test Coverage: 98% (418 tests)
- 84 tests for effects system (base, registry, handlers)
- Comprehensive edge case coverage for all handlers
- CardDefinition helper methods tested for non-Pokemon cards
- Zone edge cases (draw_bottom empty, peek_bottom overflow)
2026-01-25 00:25:38 -06:00

200 lines
6.0 KiB
Python

"""Enumeration types for the Mantimon TCG game engine.
This module defines all enum types used throughout the game engine. We use StrEnum
for JSON serialization compatibility - enum values serialize as strings directly.
Note on extensibility: While these enums define the standard types, the game engine
is designed to be configurable. Custom energy types or card types could be added
via configuration for free play mode.
"""
from enum import StrEnum
class CardType(StrEnum):
"""The primary type of a card.
Every card in the game belongs to exactly one of these types, which determines
how it can be played and what rules apply to it.
"""
POKEMON = "pokemon"
TRAINER = "trainer"
ENERGY = "energy"
class PokemonStage(StrEnum):
"""The evolution stage of a Pokemon card.
Determines how the Pokemon can be played:
- BASIC: Can be played directly from hand to bench
- STAGE_1: Must evolve from a Basic Pokemon
- STAGE_2: Must evolve from a Stage 1 Pokemon
Note: This is separate from PokemonVariant (EX, V, GX, etc.) which affects
knockout points but not evolution mechanics.
"""
BASIC = "basic"
STAGE_1 = "stage_1"
STAGE_2 = "stage_2"
class PokemonVariant(StrEnum):
"""Special variant classification for Pokemon cards.
Variants affect knockout points and may have special rules, but are
orthogonal to evolution stage. A card can be a "Basic EX" or "Stage 2 GX".
- NORMAL: Standard Pokemon, worth 1 knockout point
- EX: Worth 2 knockout points
- GX: Worth 2 knockout points, has GX attack (once per game)
- V: Worth 2 knockout points
- VMAX: Evolves from V variant, worth 3 knockout points
- VSTAR: Evolves from V variant, worth 3 knockout points, has VSTAR power
"""
NORMAL = "normal"
EX = "ex"
GX = "gx"
V = "v"
VMAX = "vmax"
VSTAR = "vstar"
class EnergyType(StrEnum):
"""Energy types available in the game.
Based on modern Pokemon TCG with 10 types. Colorless is special - any energy
can satisfy colorless requirements.
Note: The engine supports all types, but specific games can restrict which
types are enabled via RulesConfig.
"""
COLORLESS = "colorless"
DARKNESS = "darkness"
DRAGON = "dragon"
FIGHTING = "fighting"
FIRE = "fire"
GRASS = "grass"
LIGHTNING = "lightning"
METAL = "metal"
PSYCHIC = "psychic"
WATER = "water"
class TrainerType(StrEnum):
"""Subtypes of Trainer cards.
Each subtype has different rules for how many can be played per turn:
- ITEM: Unlimited per turn
- SUPPORTER: One per turn
- STADIUM: One per turn, stays in play
- TOOL: Attached to Pokemon, unlimited per turn
"""
ITEM = "item"
SUPPORTER = "supporter"
STADIUM = "stadium"
TOOL = "tool"
class TurnPhase(StrEnum):
"""Phases within a player's turn.
Turn structure:
1. SETUP: Initial game setup (draw starting hand, place basics, set prizes)
2. DRAW: Draw a card from deck
3. MAIN: Play cards, attach energy, evolve, use abilities, retreat
4. ATTACK: Declare and resolve an attack (optional)
5. END: Apply end-of-turn effects, check knockouts, score points
Valid transitions:
- SETUP -> DRAW (game start, first player's turn)
- DRAW -> MAIN
- MAIN -> ATTACK or END (can skip attack)
- ATTACK -> END
- END -> DRAW (next player's turn)
"""
SETUP = "setup"
DRAW = "draw"
MAIN = "main"
ATTACK = "attack"
END = "end"
class StatusCondition(StrEnum):
"""Status conditions that can affect Pokemon in play.
Status conditions have specific effects and removal conditions:
- POISONED: 10 damage between turns; removed by evolution, retreat, or card effect
- BURNED: 20 damage between turns + flip to remove; removed on heads
- ASLEEP: Cannot attack or retreat; removed on heads flip; overrides PARALYZED/CONFUSED
- PARALYZED: Cannot attack or retreat for 1 turn; removed at end of next turn;
overrides ASLEEP/CONFUSED
- CONFUSED: Flip to attack, 30 self-damage on tails; removed by evolution/retreat;
overrides ASLEEP/PARALYZED
Note: POISONED and BURNED stack with other conditions. ASLEEP, PARALYZED, and
CONFUSED override each other (only one can be active at a time).
"""
POISONED = "poisoned"
BURNED = "burned"
ASLEEP = "asleep"
PARALYZED = "paralyzed"
CONFUSED = "confused"
class ActionType(StrEnum):
"""Types of actions a player can take during their turn.
Each action type has specific validation rules and can only be performed
during certain phases. See rules_validator.py for details.
"""
PLAY_POKEMON = "play_pokemon"
EVOLVE = "evolve"
ATTACH_ENERGY = "attach_energy"
PLAY_TRAINER = "play_trainer"
USE_ABILITY = "use_ability"
ATTACK = "attack"
RETREAT = "retreat"
PASS = "pass"
class GameEndReason(StrEnum):
"""Reasons why a game ended.
Used in GameState.end_reason to indicate how the game concluded:
- PRIZES_TAKEN: A player took all required prize points
- NO_POKEMON: A player has no Pokemon in play (active or bench)
- DECK_EMPTY: A player cannot draw a card at the start of their turn
- RESIGNATION: A player resigned from the match
- TIMEOUT: A player ran out of time (multiplayer only)
- DRAW: The game ended in a draw (tie on points at timer expiration)
"""
PRIZES_TAKEN = "prizes_taken"
NO_POKEMON = "no_pokemon"
DECK_EMPTY = "deck_empty"
RESIGNATION = "resignation"
TIMEOUT = "timeout"
DRAW = "draw"
class ModifierMode(StrEnum):
"""How damage modifiers (weakness/resistance) are calculated.
- MULTIPLICATIVE: Multiply damage by modifier (e.g., x2 for weakness)
- ADDITIVE: Add modifier to damage (e.g., +20 for weakness, -30 for resistance)
Standard Pokemon TCG uses multiplicative weakness (x2) and additive resistance (-30).
Some game variants or house rules may prefer different calculations.
"""
MULTIPLICATIVE = "multiplicative"
ADDITIVE = "additive"