from __future__ import annotations import random from dataclasses import dataclass, field from typing import List # Target kinds TARGET_ENEMY = "enemy" TARGET_ALL_ENEMIES = "all_enemies" TARGET_TWO_ENEMIES = "two_enemies" TARGET_ALLY = "ally" TARGET_ALL_ALLIES = "all_allies" TARGET_SELF = "self" TARGET_ANY = "any" # Effect kinds EFF_SHIELD = "shield" EFF_WILT = "wilt" # value = % of the skill's damage dealt, over 3 turns EFF_DOT = "dot" # value = damage per turn, duration = turns EFF_HOT = "hot" # value = heal per turn, duration = turns EFF_STATUS = "status" # applies status[key] with value/duration EFF_HEAL = "heal" # immediate heal @dataclass class Effect: kind: str value: int = 0 duration: int = 0 key: str = "" @dataclass class Skill: name: str power: int = 0 cooldown: int = 0 target: str = TARGET_ENEMY kind: str = "damage" # damage | heal | shield | utility hits: int = 1 effects: List[Effect] = field(default_factory=list) description: str = "" priority: int = 100 # lower = "best" (preselected / AI preference) mechanic: str = "" # special behaviour id handled by the engine extra_action: bool = False requires_form: str = "" # hydrangea: "acidic" | "alkaline" power_alt: int = 0 # conditional power (e.g. [50/75]) cooldown_left: int = field(init=False, default=0) def roll_crit(chance: float) -> bool: return random.random() < chance def skill_to_dict(sk: "Skill") -> dict: """Serialize a Skill (minus runtime cooldown) for persistence.""" return { "name": sk.name, "power": sk.power, "cooldown": sk.cooldown, "target": sk.target, "kind": sk.kind, "hits": sk.hits, "effects": [{"kind": e.kind, "value": e.value, "duration": e.duration, "key": e.key} for e in sk.effects], "description": sk.description, "mechanic": sk.mechanic, "extra_action": sk.extra_action, "requires_form": sk.requires_form, "power_alt": sk.power_alt, } def skill_from_dict(d: dict) -> "Skill": """Recreate a Skill from a skill_to_dict payload.""" return Skill( name=d["name"], power=d.get("power", 0), cooldown=d.get("cooldown", 0), target=d.get("target", TARGET_ENEMY), kind=d.get("kind", "damage"), hits=d.get("hits", 1), effects=[Effect(**e) for e in d.get("effects", [])], description=d.get("description", ""), mechanic=d.get("mechanic", ""), extra_action=d.get("extra_action", False), requires_form=d.get("requires_form", ""), power_alt=d.get("power_alt", 0), ) # --- shared status keys ------------------------------------------------- S_SPEED_DOWN = "speed_down" S_CRIT_DOWN = "crit_down" S_ATTACK_DOWN = "attack_down" S_DEFENSE_DOWN = "defense_down" S_HEAL_REDUCTION = "healing_reduction" S_HEAL_BOOST = "healing_boost" S_SLEEP = "sleep" S_VULNERABLE = "vulnerable" S_INTOXICATE = "intoxicate" S_OBLIVION = "oblivion" S_TETHER = "tether" S_MADNESS = "madness" S_DENTING = "denting" S_ATTACK_UP = "attack_up" S_DEFENSE_UP = "defense_up" S_SPEED_UP = "speed_up" S_WILT = "wilt" S_DOT = "dot" S_HOT = "hot" S_SHIELD = "shield" SHIELD_DURATION = 3 # turns a shield lasts before decaying (per application, doesn't refresh) S_ICICLES = "icicles" S_SNOWFLAKES = "snowflakes" S_ZINNIA_WEAKEN = "zinnia_weaken" S_ABSORB = "absorb" S_PREVENT_FIRST_DAMAGE = "prevent_first_damage" S_LYING_WAIT = "lying_wait" S_SEED = "seed" # orchid: erupts later S_ATTACKED_THIS_TURN = "attacked_this_turn" # marigold: first-hit guard