450 lines
25 KiB
Python
450 lines
25 KiB
Python
from __future__ import annotations
|
|
|
|
import copy
|
|
from typing import Dict
|
|
|
|
from .growth import compute_stats
|
|
from .skills import (
|
|
S_ATTACK_DOWN, S_ATTACK_UP, S_CRIT_DOWN, S_DEFENSE_DOWN,
|
|
S_DEFENSE_UP, S_DENTING, S_HEAL_REDUCTION, S_ICICLES, S_INTOXICATE, S_MADNESS,
|
|
S_OBLIVION, S_SLEEP, S_SNOWFLAKES, S_SPEED_DOWN, S_TETHER, S_VULNERABLE,
|
|
EFF_DOT, EFF_HEAL, EFF_HOT, EFF_SHIELD, EFF_STATUS, EFF_WILT, Effect, Skill,
|
|
TARGET_ALL_ALLIES, TARGET_ALL_ENEMIES, TARGET_ALLY, TARGET_ANY, TARGET_ENEMY,
|
|
TARGET_SELF, TARGET_TWO_ENEMIES,
|
|
)
|
|
from .types import TYPE_GARDEN, TYPE_ICE, TYPE_NOCTURNAL, TYPE_RADIANT, TYPE_WILDFLOWER
|
|
from .unit import ROLE_ATTACKER, ROLE_SUPPORT, ROLE_TANK, TEAM_ENEMY, TEAM_PLAYER, Unit
|
|
|
|
|
|
def _sk(name, power=0, target=TARGET_ENEMY, cd=0, hits=1, kind="damage",
|
|
effects=None, desc="", priority=4, mechanic="", power_alt=0,
|
|
extra_action=False, form=""):
|
|
return Skill(name=name, power=power, cooldown=cd, target=target, kind=kind,
|
|
hits=hits, effects=effects or [], description=desc, priority=priority,
|
|
mechanic=mechanic, power_alt=power_alt, extra_action=extra_action,
|
|
requires_form=form)
|
|
|
|
|
|
def _st(key, value, dur=0):
|
|
return Effect(EFF_STATUS, value=value, duration=dur, key=key)
|
|
|
|
|
|
def _wilt(pct):
|
|
return Effect(EFF_WILT, value=pct)
|
|
|
|
|
|
def _dot(dmg, turns):
|
|
return Effect(EFF_DOT, value=dmg, duration=turns)
|
|
|
|
|
|
def _hot(amount, turns):
|
|
return Effect(EFF_HOT, value=amount, duration=turns)
|
|
|
|
|
|
def _shield(value):
|
|
return Effect(EFF_SHIELD, value=value)
|
|
|
|
|
|
POOL: Dict[str, dict] = {
|
|
"Zinnia": dict(role=ROLE_ATTACKER, type=TYPE_RADIANT, stats=(63, 68, 51, 48),
|
|
passive="zinnia_solar", skills=[
|
|
_sk("Blinding Slash", 20, hits=2, desc="20 dmg, 2 hits"),
|
|
_sk("Glitter", 10, hits=5, cd=3, mechanic="hits_per_effect", priority=1,
|
|
desc="10 dmg, 5 hits, +1 per effect on target"),
|
|
_sk("Glimmer", 10, hits=5, cd=3, mechanic="hits_per_self_effect", priority=2,
|
|
desc="10 dmg, 5 hits, +1 per effect on self"),
|
|
]),
|
|
"Sunflower": dict(role=ROLE_SUPPORT, type=TYPE_RADIANT, stats=(57, 68, 52, 46),
|
|
passive="sunflower_incandescent", passive_value=0.7, skills=[
|
|
_sk("Beam", 50, desc="50 dmg"),
|
|
_sk("Face The Sun", kind="utility", target=TARGET_ALL_ALLIES, cd=3, priority=1,
|
|
effects=[_hot(35, 3)], desc="Heal all allies 35/turn for 3 turns"),
|
|
_sk("Daybreak", kind="utility", target=TARGET_ALLY, cd=3,
|
|
mechanic="reset_cooldowns", priority=2, desc="Reset an ally's cooldowns"),
|
|
]),
|
|
"Daisy": dict(role=ROLE_SUPPORT, type=TYPE_RADIANT, stats=(57, 72, 48, 42),
|
|
passive="daisy_gentle", skills=[
|
|
_sk("Flutter", 40, mechanic="heal_random_ally", desc="40 dmg, heals a random ally"),
|
|
_sk("Sunkissed Glow", power=50, kind="heal", target=TARGET_ALL_ALLIES, cd=2, priority=1,
|
|
desc="Heal all allies for 50"),
|
|
_sk("Motivate", kind="heal", target=TARGET_ALLY, cd=4, mechanic="full_heal",
|
|
priority=2, desc="Heal an ally to full health"),
|
|
]),
|
|
"Marigold": dict(role=ROLE_TANK, type=TYPE_RADIANT, stats=(43, 50, 68, 73),
|
|
passive="marigold_golden", skills=[
|
|
_sk("Rustle", 25, desc="25 dmg"),
|
|
_sk("Puff Up", kind="utility", target=TARGET_SELF, cd=3, mechanic="puff_up",
|
|
priority=1, desc="Heal 30% max HP, cleanse self"),
|
|
_sk("Absorb", kind="utility", target=TARGET_SELF, cd=3, mechanic="absorb",
|
|
priority=2, desc="Take 20% less damage until next turn"),
|
|
]),
|
|
"Daffodil": dict(role=ROLE_TANK, type=TYPE_RADIANT, stats=(55, 47, 64, 70),
|
|
passive="daffodil_toxic", skills=[
|
|
_sk("Consume", 45, mechanic="consume_wilt",
|
|
desc="45 dmg, heal for damage dealt if target Wilted"),
|
|
_sk("Poison Ravish", 50, target=TARGET_ALL_ENEMIES, cd=3, mechanic="wilt_boost",
|
|
power_alt=75, priority=1, desc="50/75 dmg to all, more if Wilted"),
|
|
_sk("Deteriorate", 50, target=TARGET_ALL_ENEMIES, cd=3, effects=[_wilt(50)],
|
|
priority=2, desc="50 dmg to all, Wilt for 50% of damage"),
|
|
]),
|
|
"Rose": dict(role=ROLE_ATTACKER, type=TYPE_GARDEN, stats=(74, 66, 52, 49),
|
|
passive="rose_briars", skills=[
|
|
_sk("Thorned Strike", 60, effects=[_wilt(20)], desc="60 dmg, Wilt 20%"),
|
|
_sk("Petal Kiss", 90, cd=3, mechanic="ignore_defense", power_alt=0.3, priority=1,
|
|
desc="90 dmg, ignores 30% defense"),
|
|
_sk("Brambles", 75, target=TARGET_ALL_ENEMIES, cd=3,
|
|
effects=[_wilt(15), _st(S_HEAL_REDUCTION, 40, 2)],
|
|
priority=2, desc="75 dmg to all, Wilt 15%, -40% healing 2 turns"),
|
|
]),
|
|
"Peony": dict(role=ROLE_ATTACKER, type=TYPE_GARDEN, stats=(77, 58, 61, 61),
|
|
passive="peony_robust", skills=[
|
|
_sk("Petal Punch", 55, desc="55 dmg"),
|
|
_sk("Snap", 75, cd=3, mechanic="ignore_defense", power_alt=0.15, priority=1,
|
|
effects=[_st(S_DEFENSE_DOWN, 20, 3)], desc="75 dmg, -20% defense 3 turns"),
|
|
_sk("Denting Blows", 65, target=TARGET_TWO_ENEMIES, cd=3, effects=[_st(S_DENTING, 1)],
|
|
priority=2, desc="65 dmg to 2, permanent +3% damage taken (stacks)"),
|
|
]),
|
|
"Magnolia": dict(role=ROLE_ATTACKER, type=TYPE_GARDEN, stats=(73, 64, 55, 58),
|
|
passive="magnolia_nobility", skills=[
|
|
_sk("Royal Smack", 50, effects=[_st(S_DEFENSE_DOWN, 20, 1)], desc="50 dmg, -20% defense 1 turn"),
|
|
_sk("Crown", 55, cd=3, mechanic="strip_buffs", priority=1, desc="55 dmg, removes target buffs"),
|
|
_sk("Regal Order", 90, cd=3, mechanic="shield_lifesteal", power_alt=15, priority=2,
|
|
effects=[_st("stun", 1)], desc="90 dmg, stun 1 turn, shield self 15% of damage"),
|
|
]),
|
|
"Hydrangea": dict(role=ROLE_ATTACKER, type=TYPE_GARDEN, stats=(68, 70, 52, 54),
|
|
passive="hydrangea_ph", skills=[
|
|
_sk("Cloud", 45, mechanic="ph_cloud",
|
|
desc="45 dmg; Acidic: Wilt 30%, Alkaline: heal allies 30%"),
|
|
_sk("Alkaline Blossom", kind="utility", target=TARGET_SELF, cd=3, form="alkaline",
|
|
mechanic="extend_timers", priority=1, extra_action=True,
|
|
desc="Extend buffs/debuffs by 1 turn (extra action)"),
|
|
_sk("Acidic Blossom", 80, target=TARGET_ALL_ENEMIES, cd=3, form="acidic",
|
|
effects=[_wilt(40)], priority=2, extra_action=True,
|
|
desc="80 dmg to all, Wilt 40% (extra action)"),
|
|
]),
|
|
"Sweet Pea": dict(role=ROLE_ATTACKER, type=TYPE_GARDEN, stats=(71, 71, 36, 45),
|
|
passive="sweetpea_scent", skills=[
|
|
_sk("Vine Whip", 40, mechanic="tether_lowest",
|
|
desc="40 dmg, tethers lowest-HP ally for 1 turn"),
|
|
_sk("Sugar Bloom", 70, target=TARGET_ALL_ENEMIES, cd=3, priority=1,
|
|
desc="70 dmg to all enemies"),
|
|
_sk("Fragrant Bond", kind="utility", target=TARGET_ALL_ALLIES, cd=3,
|
|
effects=[_st(S_TETHER, 1, 1)], priority=2,
|
|
desc="Tether all allies; tethered allies heal from Sweet Pea's damage"),
|
|
]),
|
|
"Chrysanthemum": dict(role=ROLE_TANK, type=TYPE_GARDEN, stats=(38, 41, 70, 76),
|
|
passive="chrys_longevity", skills=[
|
|
_sk("Imperial Smite", 45, mechanic="def_bonus", desc="45 dmg + bonus from defense"),
|
|
_sk("Autumn Shield", power=50, kind="shield", target=TARGET_ALL_ALLIES, cd=2, priority=1,
|
|
effects=[_st(S_DEFENSE_UP, 8, 1)], desc="Shield all allies (scales with Attack), +defense 1 turn"),
|
|
_sk("Golden Dynasty", kind="utility", target=TARGET_ALL_ALLIES, cd=4,
|
|
mechanic="perma_hp_def", priority=2,
|
|
desc="Permanently +15% HP and +10% defense for all allies"),
|
|
]),
|
|
"Lily": dict(role=ROLE_ATTACKER, type=TYPE_WILDFLOWER, stats=(79, 65, 41, 36),
|
|
passive="lily_passionate", passive_value=0.5, skills=[
|
|
_sk("Flora Blast", 30, target=TARGET_ALL_ENEMIES, desc="30 dmg to all enemies"),
|
|
_sk("Fervent Blossom", 50, target=TARGET_ALL_ENEMIES, cd=2, mechanic="low_hp_boost",
|
|
power_alt=80, priority=1, desc="50/80 dmg to all, more under 40% HP"),
|
|
_sk("Whirlwind", 95, target=TARGET_ALL_ENEMIES, cd=4, mechanic="high_hp_boost",
|
|
power_alt=120, priority=2, desc="95/120 dmg to all, more above 80% HP"),
|
|
]),
|
|
"Hibiscus": dict(role=ROLE_ATTACKER, type=TYPE_WILDFLOWER, stats=(72, 67, 49, 55),
|
|
passive="hibiscus_intensifying", skills=[
|
|
_sk("Tropical Burst", 50, desc="50 dmg"),
|
|
_sk("Crimson Strike", 70, cd=3, mechanic="thresh_boost", power_alt=90, priority=1,
|
|
desc="70/90 dmg, more if target is high or low HP"),
|
|
_sk("Crimson Brew", 65, target=TARGET_ALL_ENEMIES, cd=3, mechanic="crimson_brew", priority=2,
|
|
desc="65 dmg to all, +attack to self for 2 turns"),
|
|
]),
|
|
"Poppy": dict(role=ROLE_ATTACKER, type=TYPE_WILDFLOWER, stats=(76, 63, 59, 55),
|
|
passive="poppy_hypnotic", skills=[
|
|
_sk("Scarlet Lash", 55, desc="55 dmg"),
|
|
_sk("Satin Smite", 70, cd=3, effects=[_st(S_INTOXICATE, 1, 2)], priority=1,
|
|
desc="70 dmg, Intoxicate 2 turns"),
|
|
_sk("Narcosis", 70, target=TARGET_TWO_ENEMIES, cd=3,
|
|
effects=[_st(S_CRIT_DOWN, 20, 2), _st(S_OBLIVION, 1, 2)], priority=2,
|
|
desc="70 dmg to 2, -20% crit chance and Oblivion 2 turns"),
|
|
]),
|
|
"Tulip": dict(role=ROLE_ATTACKER, type=TYPE_WILDFLOWER, stats=(69, 65, 53, 57),
|
|
passive="tulip_equinox", skills=[
|
|
_sk("Bulb Blast", 40, target=TARGET_TWO_ENEMIES, desc="40 dmg to 2 enemies"),
|
|
_sk("Spring Rain", 55, target=TARGET_ALL_ENEMIES, cd=3,
|
|
effects=[_st(S_ATTACK_DOWN, 10, 2)], priority=1, desc="55 dmg to all, -attack 2 turns"),
|
|
_sk("Vibrant Bells", power=80, kind="shield", target=TARGET_ALL_ALLIES, cd=3, priority=2,
|
|
effects=[_st("bulb_reflect", 1)], desc="Shield all allies (scales with Attack); reflects Bulb Blast"),
|
|
]),
|
|
"Lotus": dict(role=ROLE_SUPPORT, type=TYPE_WILDFLOWER, stats=(59, 75, 44, 53),
|
|
passive="lotus_rebirth", skills=[
|
|
_sk("Revitalize", power=40, kind="heal", target=TARGET_ALLY, mechanic="scale_heal",
|
|
desc="Heal an ally (scales with Attack)"),
|
|
_sk("Mud Resilience", kind="utility", target=TARGET_ALL_ALLIES, cd=2,
|
|
effects=[_st(S_ATTACK_UP, 10, 2)], priority=1, desc="+attack for all allies 2 turns"),
|
|
_sk("Purify", kind="utility", target=TARGET_ALL_ALLIES, cd=4,
|
|
mechanic="clear_debuffs_allies", priority=2, extra_action=True,
|
|
desc="Remove all debuffs from all allies (extra action)"),
|
|
]),
|
|
"Dandelion": dict(role=ROLE_SUPPORT, type=TYPE_WILDFLOWER, stats=(62, 74, 45, 37),
|
|
passive="dandelion_cover", skills=[
|
|
_sk("Drifting Seed", 40, effects=[_st(S_ATTACK_DOWN, 10, 1)], desc="40 dmg, -attack 1 turn"),
|
|
_sk("Bloom", power=90, kind="shield", target=TARGET_ALLY, cd=2, priority=1, desc="Shield an ally (scales with Attack)"),
|
|
_sk("Make A Wish", power=15, kind="shield", target=TARGET_ALL_ALLIES, cd=4,
|
|
effects=[_hot(20, 3)], priority=2,
|
|
desc="Shield all allies (scales with Attack), heal 20/turn for 3 turns"),
|
|
]),
|
|
"Nightshade": dict(role=ROLE_ATTACKER, type=TYPE_NOCTURNAL, stats=(76, 69, 45, 53),
|
|
passive="nightshade_corruption", skills=[
|
|
_sk("Astra", 60, desc="60 dmg"),
|
|
_sk("Falling Star", 25, hits=4, cd=3, priority=1, desc="25 dmg, 4 hits"),
|
|
_sk("Dark Corrosion", kind="utility", target=TARGET_ALL_ENEMIES, cd=3,
|
|
effects=[_dot(70, 3)], priority=2, desc="70 dmg/turn to all for 3 turns (stacks)"),
|
|
]),
|
|
"Moonflower": dict(role=ROLE_ATTACKER, type=TYPE_NOCTURNAL, stats=(77, 66, 54, 43),
|
|
passive="moonflower_night", skills=[
|
|
_sk("Lunar Beam", 45, mechanic="ramping", desc="45 dmg, +5 every use"),
|
|
_sk("Night Fog", kind="utility", target=TARGET_ALL_ENEMIES, cd=3,
|
|
effects=[_st(S_HEAL_REDUCTION, 30, 2), _st(S_VULNERABLE, 1)], priority=1,
|
|
desc="-healing 2 turns, Vulnerable 1 on all"),
|
|
_sk("Glimmering Moon", 65, target=TARGET_ALL_ENEMIES, cd=3,
|
|
mechanic="moon_double", priority=2, desc="65 dmg to all, chance to hit twice"),
|
|
]),
|
|
"Orchid": dict(role=ROLE_ATTACKER, type=TYPE_NOCTURNAL, stats=(74, 68, 47, 64),
|
|
passive="orchid_epiphytic", passive_value=0.4, skills=[
|
|
_sk("Branch", 60, desc="60 dmg"),
|
|
_sk("Sow", kind="utility", target=TARGET_ENEMY, cd=3, mechanic="sow", priority=1,
|
|
desc="Plant a seed that erupts for 115 dmg in 2 turns"),
|
|
_sk("Sweeping Elegance", 70, target=TARGET_ALL_ENEMIES, cd=3,
|
|
mechanic="strip_buffs_aoe", priority=2, desc="70 dmg to all, removes a buff each"),
|
|
]),
|
|
"Lavender": dict(role=ROLE_ATTACKER, type=TYPE_NOCTURNAL, stats=(72, 77, 36, 43),
|
|
passive="lavender_soporific", skills=[
|
|
_sk("Tranquilize", 30, effects=[_st(S_SPEED_DOWN, 3, 1), _st(S_VULNERABLE, 1)],
|
|
desc="30 dmg, -speed 1 turn, Vulnerable 1"),
|
|
_sk("Soothing Lullaby", 50, target=TARGET_ALL_ENEMIES, cd=2,
|
|
effects=[_st(S_CRIT_DOWN, 10, 2)], priority=1, desc="50 dmg to all, -10% crit 2 turns"),
|
|
_sk("Sedate", 60, cd=4, effects=[_st(S_SLEEP, 2)], priority=2, desc="60 dmg, Sleep"),
|
|
]),
|
|
"Venus Flytrap": dict(role=ROLE_TANK, type=TYPE_NOCTURNAL, stats=(55, 44, 73, 68),
|
|
passive="flytrap_feed", skills=[
|
|
_sk("Devour", 45, desc="45 dmg"),
|
|
_sk("Lying In Wait", kind="utility", target=TARGET_SELF, cd=3,
|
|
mechanic="lying_wait", priority=1, desc="Enemy attacks charge Dissolve"),
|
|
_sk("Dissolve", 25, hits=2, cd=3, mechanic="dissolve", priority=2,
|
|
desc="25 dmg twice, +10 per charge"),
|
|
]),
|
|
"Snowdrop": dict(role=ROLE_ATTACKER, type=TYPE_ICE, stats=(73, 78, 52, 38),
|
|
passive="snowdrop_winter", skills=[
|
|
_sk("Snow Pierce", 50, effects=[_st(S_DEFENSE_DOWN, 20, 1)], desc="50 dmg, -20% defense 1 turn"),
|
|
_sk("Chill", kind="utility", target=TARGET_ENEMY, cd=2, effects=[_dot(45, 2)],
|
|
priority=1, desc="45 dmg/turn for 2 turns"),
|
|
_sk("Whiteout", 10, target=TARGET_ALL_ENEMIES, cd=4, mechanic="delay_all",
|
|
priority=2, desc="10 dmg to all, delays their next turns"),
|
|
]),
|
|
"Primrose": dict(role=ROLE_ATTACKER, type=TYPE_ICE, stats=(77, 73, 36, 42),
|
|
passive="primrose_coldhearted", skills=[
|
|
_sk("Swipe", 40, desc="40 dmg"),
|
|
_sk("Frostbite", 75, cd=3, effects=[_st(S_SPEED_DOWN, 3, 2), _st(S_HEAL_REDUCTION, 50, 2)],
|
|
priority=1, desc="75 dmg, -speed and -50% healing 2 turns"),
|
|
_sk("Icy Glare", 60, cd=3, effects=[_wilt(40)], priority=2, desc="60 dmg, Wilt 40%"),
|
|
]),
|
|
"Winter Jasmine": dict(role=ROLE_ATTACKER, type=TYPE_ICE, stats=(67, 73, 38, 55),
|
|
passive="jasmine_janus", skills=[
|
|
_sk("Solstice Spark", 45, desc="45 dmg"),
|
|
_sk("Thaw", kind="utility", target=TARGET_ALLY, cd=3, mechanic="vigil", priority=1,
|
|
desc="Remove debuffs, shield (scales with Attack); when hit, counter Solstice Spark"),
|
|
_sk("Catching Snowflakes", kind="utility", target=TARGET_ALL_ALLIES, cd=3,
|
|
mechanic="snowflakes", priority=2, extra_action=True,
|
|
desc="All allies' hits +10 (stacks). Extra action"),
|
|
]),
|
|
"Hellebore": dict(role=ROLE_SUPPORT, type=TYPE_ICE, stats=(76, 71, 33, 48),
|
|
passive="hellebore_toxin", skills=[
|
|
_sk("Pacify", power=45, kind="heal", target=TARGET_ALLY, mechanic="pacify",
|
|
desc="Heal an ally; removes Madness if below 40% HP"),
|
|
_sk("Madness", kind="utility", target=TARGET_ANY, cd=3, effects=[_st(S_MADNESS, 1)],
|
|
priority=1, desc="Apply Madness to a plant"),
|
|
_sk("Gradual Annihilation", 40, cd=3, effects=[_wilt(200)],
|
|
priority=2, desc="40 dmg, Wilt for 200% of damage"),
|
|
]),
|
|
"Camelia": dict(role=ROLE_TANK, type=TYPE_ICE, stats=(60, 49, 68, 71),
|
|
passive="camelia_frigid", skills=[
|
|
_sk("Hail", 55, effects=[_st(S_ICICLES, 1)], desc="55 dmg, gain 1 Icicle"),
|
|
_sk("Igloo", power=70, kind="shield", target=TARGET_SELF, cd=2, priority=1,
|
|
desc="Shield self (scales with Attack); gain 2 Icicles per hit while shielded"),
|
|
_sk("Blizzard", kind="utility", target=TARGET_ALL_ENEMIES, cd=4,
|
|
mechanic="shatter_icicles", priority=2, desc="Shatter Icicles to damage all"),
|
|
]),
|
|
# Common critters: each exists in all five types (the sprite is shared).
|
|
# They're always weaker than real plants and carry no passive.
|
|
"Pinwheel": dict(role=ROLE_ATTACKER, type=TYPE_GARDEN, stats=(30, 35, 28, 42),
|
|
skills=[
|
|
_sk("Spin Slash", 25, desc="25 dmg to one enemy"),
|
|
]),
|
|
"Butterfly": dict(role=ROLE_SUPPORT, type=TYPE_GARDEN, stats=(30, 40, 26, 40),
|
|
skills=[
|
|
_sk("Nectar", power=15, kind="heal", target=TARGET_ALLY, cd=2,
|
|
desc="Heal an ally (scales with Attack)"),
|
|
_sk("Cocoon", power=25, kind="shield", target=TARGET_ALLY, cd=2,
|
|
desc="Shield an ally (scales with Attack)"),
|
|
]),
|
|
"Toadstool": dict(role=ROLE_TANK, type=TYPE_GARDEN, stats=(30, 30, 42, 54),
|
|
skills=[
|
|
_sk("Spore Bash", 22, mechanic="spore_siphon", power_alt=25,
|
|
desc="22 dmg, heal self for 25% of damage dealt"),
|
|
]),
|
|
# __MORE_PLANTS__
|
|
}
|
|
|
|
|
|
HP_MULT = 2.5 # balance: defense is now a % reduction, so HP needs less inflation
|
|
|
|
|
|
RARITY = {
|
|
# common critters
|
|
"Pinwheel": "common", "Butterfly": "common", "Toadstool": "common",
|
|
# super super rare (was legendary)
|
|
"Sunflower": "super_super_rare", "Hydrangea": "super_super_rare", "Poppy": "super_super_rare",
|
|
"Lavender": "super_super_rare", "Snowdrop": "super_super_rare", "Hellebore": "super_super_rare",
|
|
"Zinnia": "super_super_rare",
|
|
# super rare (was epic)
|
|
"Daisy": "super_rare", "Magnolia": "super_rare", "Sweet Pea": "super_rare",
|
|
"Chrysanthemum": "super_rare", "Lotus": "super_rare", "Nightshade": "super_rare",
|
|
"Moonflower": "super_rare", "Winter Jasmine": "super_rare", "Camelia": "super_rare", "Tulip": "super_rare",
|
|
"Rose": "super_rare", "Lily": "super_rare", "Orchid": "super_rare",
|
|
# rare (was common + rare)
|
|
"Hibiscus": "rare", "Dandelion": "rare", "Venus Flytrap": "rare",
|
|
"Marigold": "rare", "Daffodil": "rare", "Peony": "rare", "Primrose": "rare",
|
|
}
|
|
|
|
# Plant rarities. "common" (Golems) and "ultra_rare" (Ubers/Fruits) have no plants yet.
|
|
RARITY_ORDER = {
|
|
"common": 0, "rare": 1, "super_rare": 2, "super_super_rare": 3, "ultra_rare": 4,
|
|
}
|
|
RARITY_LABELS = {
|
|
"common": "Common",
|
|
"rare": "Rare",
|
|
"super_rare": "Super Rare",
|
|
"super_super_rare": "Super Super Rare",
|
|
"ultra_rare": "Ultra Rare",
|
|
}
|
|
|
|
RARITY_COLORS = {
|
|
"common": (215, 215, 220), # White
|
|
"rare": (110, 170, 235), # Blue
|
|
"super_rare": (170, 120, 220), # Purple
|
|
"super_super_rare": (245, 180, 70), # Gold
|
|
"ultra_rare": (235, 75, 75), # Red
|
|
"epic": (170, 120, 220), # relics (legacy)
|
|
"legendary": (245, 180, 70), # relics (legacy)
|
|
}
|
|
|
|
# Critters that share one sprite across all five types; the type is assigned
|
|
# per unit (and shown with type icons in the collection).
|
|
VARIABLE_TYPE_UNITS = ("Pinwheel", "Butterfly", "Toadstool")
|
|
|
|
PASSIVES = {
|
|
"zinnia_solar": ("Solar Decay", "Enemies Zinnia attacks deal 1% less damage per hit, up to 30%."),
|
|
"sunflower_incandescent": ("Incandescent", "Heal the ally with the lowest health 70% of Attack each turn."),
|
|
"daisy_gentle": ("Gentle Encouragement", "Healing an ally removes a debuff and boosts their speed."),
|
|
"marigold_golden": ("Golden Gleam", "The first hit each turn is reduced by 30%."),
|
|
"daffodil_toxic": ("Toxic Sap", "When attacked, Wilt the attacker for 50%; drain their Wilt to heal instead."),
|
|
"rose_briars": ("Briars", "Reflect 40% of damage taken back to the attacker as Wilt."),
|
|
"peony_robust": ("Robust", "Attacks ignore 20% of defense."),
|
|
"magnolia_nobility": ("Nobility", "Immune to stuns; periodic debuffs last 1 turn less."),
|
|
"hydrangea_ph": ("pH Shift", "Switches between Acidic and Alkaline each turn."),
|
|
"sweetpea_scent": ("Sweet Scent", "Tethered allies take 10% less damage and heal 30% more."),
|
|
"chrys_longevity": ("Longevity", "Gain 3% defense when hit."),
|
|
"lily_passionate": ("Passionate", "+Attack and +Speed while enemies are below 50% health."),
|
|
"hibiscus_intensifying": ("Intensifying", "Deal more damage to enemies with more max health."),
|
|
"poppy_hypnotic": ("Hypnotic", "10% chance per attack to Intoxicate (1 turn) and Oblivion (1 turn) an enemy. Intoxicate: half the victim's damage splashes onto one of their own allies. Oblivion: they may only use their weakest skill."),
|
|
"tulip_equinox": ("Verdant Equinox", "Allies deal 10% more damage while shielded."),
|
|
"lotus_rebirth": ("Rebirth", "Revive the first fallen ally at 50% health."),
|
|
"dandelion_cover": ("Cover", "The first time an ally drops below 40% HP, shield them for 50% of max HP."),
|
|
"nightshade_corruption": ("Corruption", "When an enemy dies with Dark Corrosion, it spreads to the next most vulnerable enemy. Dark Corrosion also applies a random stat debuff to the target for 2 turns."),
|
|
"moonflower_night": ("Everlasting Night", "Damage all enemies each turn, growing stronger; Glimmering Moon's double-hit chance rises 10% each turn."),
|
|
"orchid_epiphytic": ("Epiphytic", "+Speed and +Attack while enemies are below 40% health."),
|
|
"lavender_soporific": ("Soporific", "+Speed and +Attack while an enemy is asleep."),
|
|
"flytrap_feed": ("Feed", "Heal for 20% of damage dealt."),
|
|
"snowdrop_winter": ("Winter Breeze", "Slow all enemies at start; Chill the attacker of a fallen ally."),
|
|
"primrose_coldhearted": ("Coldhearted", "The first skill bypasses tanks; afterwards only Frostbite does."),
|
|
"jasmine_janus": ("Janus Bloom", "Reduce the first damage each ally takes by 80%."),
|
|
"hellebore_toxin": ("Toxin", "Madness allies deal +50% and take +30%; enemies deal +30% and take +50%."),
|
|
"camelia_frigid": ("Frigid", "Gain an Icicle when hit."),
|
|
}
|
|
|
|
|
|
def plant_rarity(name: str) -> str:
|
|
return RARITY.get(name, "common")
|
|
|
|
|
|
def _apply_injection(name: str, team: int, skills):
|
|
"""Replace a plant's skill with its persisted injected ability, if any.
|
|
Only the player's own plants carry injections (enemies never inherit them).
|
|
Returns (injected_index, injected_name) or (-1, "")."""
|
|
if team != TEAM_PLAYER:
|
|
return -1, ""
|
|
from . import collection # lazy: collection imports plants at module level
|
|
from .skills import skill_from_dict
|
|
inj = collection.injection_of(name)
|
|
idx = inj.get("index", 1) if inj else -1
|
|
if inj and 0 < idx < len(skills):
|
|
skills[idx] = skill_from_dict(inj["skill"])
|
|
return idx, inj["skill"]["name"]
|
|
return -1, ""
|
|
|
|
|
|
def make(name: str, team: int, ptype: str = None) -> Unit:
|
|
spec = POOL[name]
|
|
atk, spd, dfn, hp = spec["stats"]
|
|
skills = [copy.deepcopy(s) for s in spec["skills"]]
|
|
inj_index, inj_name = _apply_injection(name, team, skills)
|
|
# Display order = list order (basic -> 2nd -> 3rd). Selection priority is
|
|
# position-based: the LAST skill is the strongest, so it is preselected first.
|
|
for i, s in enumerate(skills):
|
|
s.priority = len(skills) - i
|
|
unit_type = ptype if ptype is not None or name in VARIABLE_TYPE_UNITS else spec["type"]
|
|
unit = Unit(name, team, unit_type, max_hp=int(hp * HP_MULT), attack=atk, speed=spd, defense=dfn,
|
|
skills=skills,
|
|
role=spec["role"], passive=spec.get("passive", ""),
|
|
passive_value=spec.get("passive_value", 0))
|
|
unit.injected_index = inj_index
|
|
unit.injected_name = inj_name
|
|
_apply_runes(name, team, unit)
|
|
return unit
|
|
|
|
|
|
def _apply_runes(name: str, team: int, unit: Unit) -> None:
|
|
"""Load the player's equipped runes for `name` onto the unit as
|
|
(effect, pct) pairs. Only the player's own plants carry runes (enemies never
|
|
inherit them)."""
|
|
if team != TEAM_PLAYER:
|
|
return
|
|
from . import collection # lazy: collection imports plants at module level
|
|
from . import runes
|
|
equipped = collection.equipped_runes(name)
|
|
if not equipped:
|
|
return
|
|
by_id = {r["id"]: r for r in collection.rune_inventory()}
|
|
effects = []
|
|
for rune_id in equipped:
|
|
rune = by_id.get(rune_id)
|
|
if rune and rune["color"] in runes.RUNE_EFFECTS:
|
|
effects.append((runes.effect_for(rune["color"]), rune.get("pct", 30) / 100.0))
|
|
unit.runes = effects
|
|
|
|
|
|
def make_leveled(name: str, team: int, level: int = 1, stage: int = 1, ptype: str = None) -> Unit:
|
|
u = make(name, team, ptype)
|
|
if level > 1 or stage > 1:
|
|
atk, spd, dfn, hp = compute_stats(spec := POOL[name]["stats"], level, stage)
|
|
u.attack = atk
|
|
u.speed = spd
|
|
u.defense = dfn
|
|
u.max_hp = int(hp * HP_MULT)
|
|
u.hp = u.max_hp
|
|
u.level = level
|
|
u.stage = stage
|
|
return u
|
|
|
|
|
|
def build_pool() -> Dict[str, Unit]:
|
|
return {name: make(name, TEAM_PLAYER) for name in POOL} |