tabras / primitives.py
vvennelakanti's picture
Build Tabras card duel prototype
6bbf552
Raw
History Blame Contribute Delete
4.76 kB
from dataclasses import dataclass
from typing import Literal
PrimitiveId = Literal[
"deal",
"burn",
"bomb",
"block",
"ward",
"weak",
"draw",
"energy",
"initiative",
"multi_hit",
"vulnerable",
"conditional",
"scaling",
]
School = Literal["fire", "ice", "earth"]
@dataclass(frozen=True)
class PrimitiveSpec:
id: PrimitiveId
name: str
category: str
schools: frozenset[School]
@dataclass(frozen=True)
class Effect:
primitive_id: PrimitiveId
amount: int = 0
duration: int = 0
delay: int = 0
hits: int = 1
condition: str = ""
PRIMITIVES: dict[PrimitiveId, PrimitiveSpec] = {
"deal": PrimitiveSpec("deal", "Deal", "damage", frozenset(("fire",))),
"burn": PrimitiveSpec("burn", "Burn", "damage", frozenset(("fire",))),
"bomb": PrimitiveSpec("bomb", "Bomb", "damage", frozenset(("fire",))),
"block": PrimitiveSpec("block", "Block", "defense", frozenset(("earth",))),
"ward": PrimitiveSpec("ward", "Ward", "defense", frozenset(("earth",))),
"weak": PrimitiveSpec("weak", "Weak", "defense", frozenset()),
"draw": PrimitiveSpec("draw", "Draw", "tempo", frozenset()),
"energy": PrimitiveSpec("energy", "Energy", "tempo", frozenset()),
"initiative": PrimitiveSpec("initiative", "Initiative", "tempo", frozenset(("ice",))),
"multi_hit": PrimitiveSpec("multi_hit", "Multi-hit", "combo", frozenset(("ice",))),
"vulnerable": PrimitiveSpec("vulnerable", "Vulnerable", "combo", frozenset(("ice",))),
"conditional": PrimitiveSpec("conditional", "Conditional", "combo", frozenset(("ice", "earth"))),
"scaling": PrimitiveSpec("scaling", "Scaling", "combo", frozenset(("fire", "earth"))),
}
SCHOOL_PRIMITIVES: dict[School, tuple[PrimitiveId, ...]] = {
"fire": ("deal", "burn", "bomb", "scaling", "draw", "energy", "block", "conditional"),
"ice": ("deal", "initiative", "vulnerable", "multi_hit", "conditional", "draw", "energy", "block"),
"earth": ("deal", "ward", "block", "weak", "scaling", "conditional", "draw", "energy"),
}
SCHOOL_BIAS: dict[School, str] = {
"fire": "Prefer immediate Deal first, then Burn/Scaling, then occasional Bomb, Draw, Energy, or Block.",
"ice": "Prefer tempo pressure through Deal, Initiative, Vulnerable, Multi-hit, and Conditional, with occasional Draw or Block.",
"earth": "Prefer Block and Ward to bank shield charge, then Scaling to release that charge as burst damage. Add some Deal, Weak, and Draw.",
}
# Return all primitive identifiers in canonical order.
def primitive_ids() -> tuple[PrimitiveId, ...]:
return tuple(PRIMITIVES)
# Return a primitive spec by id.
def get_primitive(primitive_id: PrimitiveId) -> PrimitiveSpec:
return PRIMITIVES[primitive_id]
# Return the primitive ids available to a school.
def school_primitives(school: School) -> tuple[PrimitiveId, ...]:
return SCHOOL_PRIMITIVES[school]
# Return whether a primitive belongs in a school's draft pool.
def primitive_allowed_for_school(primitive_id: PrimitiveId, school: School) -> bool:
return primitive_id in SCHOOL_PRIMITIVES[school]
# Return a school's generation bias.
def school_bias(school: School) -> str:
return SCHOOL_BIAS[school]
# Render a costed effect into fixed card text.
def render_effect(effect: Effect) -> str:
match effect.primitive_id:
case "deal":
return f"Deal {effect.amount} damage."
case "burn":
return f"Burn {effect.amount} damage for {effect.duration} turns."
case "bomb":
return f"Bomb: deal {effect.amount} damage in {effect.delay} turns."
case "block":
return f"Gain {effect.amount} block until your next turn."
case "ward":
return f"Gain {effect.amount} ward."
case "weak":
return f"Opponent deals {effect.amount} less damage until your next turn."
case "draw":
return f"Draw {effect.amount} card{'s' if effect.amount != 1 else ''}."
case "energy":
return f"Gain {effect.amount} energy this turn."
case "initiative":
return "Opponent acts second next round."
case "multi_hit":
return f"Deal {effect.amount} damage {effect.hits} times."
case "vulnerable":
return f"Opponent takes {effect.amount} more damage for {effect.duration} turns."
case "conditional":
return f"Deal up to {effect.amount} damage based on opponent's missing HP."
case "scaling":
if effect.condition == "shield_charge":
return f"Deal {effect.amount} damage plus your banked shield charge, then empty it."
return f"Deal {effect.amount} damage plus cards played this turn."