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)
200 lines
6.0 KiB
Python
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"
|