120 lines
3.5 KiB
Python
120 lines
3.5 KiB
Python
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"
|
|
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 |