# Plants — Turn-Based Battle Game A turn-based battle game in Python. Normal battles are 4v4; the Tower mode starts small and ramps up. ## Run it ``` pip install -r requirements.txt python main.py ``` You land on the **main menu**: - **Seeds** — seed pack opening; the main way to get/enhance plants. - **Plants** — the plants you own, with their **level/stage**; open a plant to see its stats and **Water** (+1 level) or **Evolve** it (at milestone levels, consuming duplicates). - **Battle** — pick a difficulty stage (1–5, gated by enemy rarity), then the enemy team is rolled so you know the matchup, and you pick your 4 allies and fight. Higher stages grant more packs on a win; any loss grants a lesser pack. - **Tower** — a 20-stage gauntlet. Enemies start with 1 common plant and get stronger (more enemies, higher rarity, higher level) each stage; your team is fresh every fight. After each win you **choose one of three roguelike blessings** (shields, stat boosts, enemy debuffs, heal-over-time…) that last for the rest of the run — the core of the tower's roguelike loop. Stages 1–5 pay 1 water each, stages 6–20 pay scaling **Tower packs** (high-rarity odds), which you can open or redeem for 20 water. - **Collection** — plants you've discovered (they're added when you battle them); undiscovered plants show as blacked-out silhouettes. Saved in `data/collection.json`. - **Soil** — retrieve a **seed** from any owned plant (once per cooldown, from its detail page), plant it, then grow a copy in stages. Each stage fills over time; **fertilizing** (1 fertilizer per stage) advances it. Higher rarities have more stages; harvesting grants a duplicate of that plant. You start with **1 plot**; extra plots unlock for escalating fertilizer (10, then 20, 30…, up to 5 total). Soil unlocks once you own **8 distinct plants**. ## Rarities Every plant has a **rarity** tag (common / rare / epic / legendary) — purely cosmetic, only used to weight seed-pack drops. A **victory** grants seed packs equal to the difficulty stage (1–5) **plus fertilizer equal to the floor** (1–5); a **defeat** grants 1 **lesser pack** (worse rarity odds, no type weighting). Type packs roll the rarity first (matching the general pack) and then pick a plant of that rarity from the type when possible; rarer plants are rarer pulls. ## Watering, fertilizer & Evolution Two resources: - **Water** — the scarce, valuable currency. **Watering** gives an instant +1 level by spending 1 water, and 5 water can be converted into a seed pack. Water comes mainly from Tower rewards (stage 1–5 wins pay 1 water, Tower packs redeem for 10). - **Fertilizer** — the common currency earned from **battle wins**. It's used to fertilize growing Soil plots (each fertilizer advances a plot one stage). - **XP** is earned from battles (participation + win bonus); allies auto-level up. Higher difficulty stages pay more XP (scales with the stage number), and Tower wins pay XP too. The lowest-level plant on your team earns bonus XP based on the level gap (catch-up). - **Evolution** unlocks at milestone levels (5 → stage 2, 10 → stage 3) and consumes duplicates. Stages multiply base stats before the level curve. - Enemies in battle are scaled to roughly your team's level. - Stat growth: attack/defense +6%, health +8%, speed +4% per level (see `engine/growth.py`). ## Roguelike blessings (Tower) After every Tower win you're offered **3 random blessings** and keep one (or skip). Blessings stack for the rest of the run and apply at the **start of each battle**. They come in three rarity tiers: - **Common** — small shields/stat boosts and start-of-battle buffs (e.g. "team starts with a shield for 10% of max health"). - **Rare** — bigger boosts, heal-over-time, stronger healing, enemy debuffs (Speed/Attack Down). - **Epic** — strong shields, "+15% attack", "+20% max health", enemies start Vulnerable. - **Legendary** — rare picks with big payoff: block the first damage taken, a 40% shield, a one-time revive, or a large HP/attack combo. Blessings are offered from a **rarity-weighted** pool, so legendary relics show up rarely. Blessings are defined declaratively in `engine/relics.py` (`RELIC_POOL`); each relic is a `Relic` with an `apply(battle)` function that runs once per battle in `Battle.start()`. They're tracked per-run (not saved). An **"All blessings" overview** (`ui/relics_overview.py`) shows the whole pool grouped by rarity with the ones you've collected highlighted in gold; click any card for a closer look. It opens from the blessing-choice screen and by clicking the "Blessings:" line on the Tower battle screen. ## Plant types & type advantages Five types with a rock-paper-scissors cycle: **Radiant > Nocturnal > Ice > Garden > Wildflowers > Radiant** - Hitting a type you're strong against deals **+30%** damage. - Hitting a type that's strong against you deals **−30%**. - Applies only to damage — heals and shields are unaffected. ## Stats & damage - **Health** — the damage pool. - **Attack / Defense** — damage = skill power + attack − defense (min 1). - **Speed** — an **initiative timeline**: each plant acts based on `100 / speed` interval, so faster plants act *more often* and interleave with slower ones (e.g. a 110-speed enemy goes 110 → 75 → 110 → 50…). The blue bar under each unit's health shows how close it is to its next turn. Speed buffs/debuffs change the interval, and "reduce turn progress" effects literally push a plant's next turn back. - **Crit** — base 10%, doubles damage. ## Debuffs - **Wilt** — damage over 3 turns (total = % of the triggering hit's damage); reapplying resets it to 3 turns. - **Shield** — temporary HP that absorbs damage; cannot be healed. - **Damage-over-time** (Dark Corrosion, Chill) — deals its listed damage **every turn** while active; Dark Corrosion stacks. - **Sleep** — skips turns until the target has taken damage twice. - **Vulnerable X** — +50% damage for the next X hits. - **Intoxicate / Oblivion** — an Intoxicated plant's attacks splash half their damage onto one of its own allies; an Oblivious plant may only use its weakest skill (both last their stated duration, e.g. 1-2 turns). - Plus speed/crit/attack/defense down, Madness, Tether, and more. ## The plant pool (27) All defined declaratively in `engine/plants.py` so new plants are easy to add: - **Radiant:** Zinnia, Sunflower, Daisy, Marigold, Daffodil - **Garden:** Rose, Peony, Magnolia, Hydrangea, Sweet Pea, Chrysanthemum - **Wildflower:** Lily, Hibiscus, Poppy, Tulip, Lotus, Dandelion - **Nocturnal:** Nightshade, Moonflower, Orchid, Lavender, Venus Flytrap - **Ice:** Snowdrop, Primrose, Winter Jasmine, Hellebore, Camelia Each has a role (Attacker / Support / Tank), a unique passive, three skills with cooldowns, and base stats. Tanks protect their team from single-target attacks (AOE still hits everyone). ## Architecture ``` engine/ Pure-Python battle logic (types, skills, effects, plants, battle). No pygame — reusable for a web UI or Flet later. ui/ pygame frontend: team picker + battle screen. tests/ unittest coverage of the engine. ``` Adding a plant later = add one entry to `POOL` in `engine/plants.py`. ## Tests ``` python -m unittest discover -s tests ``` ## Roadmap ideas - Balance pass (sim currently favors the player's chosen teams) - More team strategies, saving team compositions - Web UI via Flask, or a real mobile app via Flet