Files
Flora/engine/skills.py
T
Lena feb4500c6c small update
boss prototype, auto planter, visual reworks, bug fixes
2026-08-29 10:45:34 +02:00

121 lines
3.6 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"
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