File size: 4,757 Bytes
6bbf552
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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."