Skip to content

Drone System — Phase 4 (Drone HP + Shop Trees) Implementation Plan

Drone System — Phase 4 (Drone HP + Shop Trees) Implementation Plan

Section titled “Drone System — Phase 4 (Drone HP + Shop Trees) Implementation Plan”

For agentic workers: REQUIRED SUB-SKILL: superpowers:subagent-driven-development or executing-plans. Each task = one bh-dev-chunk cycle (TDD → --import → boot-check → full GUT suite → scripts/check-test-count.sh → determinism). Steps use - [ ].

Goal: Give drones real HP (destroyable) and a per-class upgrade economy in the shop, so spending gold on drones is a strategic axis beside weapons + the pilot.

Decisions (locked by Chris 2026-06-28): (1) Full HP — drones take damage and can die; Durability is an HP upgrade. (2) Full spec trees — every attribute incl. the novel class mechanics (so Phase 4 is multi-cycle: 4A HP → 4B scaling trees + shop → 4C novel mechanics → 4D review).

Architecture: DroneState gains hp/max_hp; an enemy-contact pass damages drones (Sentinel taunt ties in — it soaks). Drone upgrades reuse the existing shop: meta_upgrades with category: "Drones" and ids like unlock-drone-bomber / drone-bomber-power. apply_to skips them (unknown StatEffect); build_drone_loadout reads their levels to scale each class’s cfg; owns_class gates on the unlocks. The class behaviours read the cfg scales (durability/speed/range/recharge/power/area). All drone state is opt-in → the no-drone determinism baseline stays byte-identical.

Tech Stack: Godot 4.6 typed GDScript; /sim; GUT 9.6. Branch: feat/drone-system.

  • Determinism byte-identical — survival 1405185210/3122397125, crystals 91572468/1173256610. Every new pass iterates drones (empty in baseline) or reads default-0 levels → no-op. Re-check each task.
  • /sim pure; upgrades are data + the build_drone_loadout seam (render-side). Numbers tunable (playtest).
  • Don’t touch CLAUDE.md/git add -A; coordinate merges with the UI agent at the boundary.

Sub-cycle 4A — Drone HP (destroyable drones)

Section titled “Sub-cycle 4A — Drone HP (destroyable drones)”

Task 4A.1: DroneState HP + deploy + a contact-damage pass + death

Section titled “Task 4A.1: DroneState HP + deploy + a contact-damage pass + death”

Files: sim/drone_state.gd (hp/max_hp), sim/sim.gd (DRONE_BASE_HP, DRONE_RADIUS; set hp in deploy_drones from cfg.durability; _damage_drones_from_enemies(dt) called each tick after _update_drones; remove drones at hp<=0 with a death fx), tests/test_drone_hp.gd (new). Mechanics: deploy_drones sets d.max_hp = DRONE_BASE_HP * cfg.get("durability", 1.0), d.hp = max_hp. _damage_drones_from_enemies: for each drone, hash.query_circle(d.pos, DRONE_RADIUS, enemies) → sum enemies.contact_dmg[ei] * dtd.hp -= sum; if d.hp <= 0 remove the drone (emit death fx at d.pos). Tie-in: the Sentinel taunt already pulls enemies onto the drone → they now chip its HP.

  • Determinism: the pass iterates drones (empty in baseline) → no-op → byte-identical. Verify.
  • Test: a deployed drone surrounded by enemies loses HP and is removed when it hits 0; a drone with no enemies nearby keeps full HP; deploy scales max_hp by cfg.durability. Determinism unchanged. Commit.

Task 4A.2: HP in render-info + Bay/HUD readout (light)

Section titled “Task 4A.2: HP in render-info + Bay/HUD readout (light)”

Files: sim/sim.gd (drones_render_info/decoy_render_info include hp/max_hp), render/decoy_renderer.gd (optional thin HP ring on a drone). Render-only.

  • Render-only (boot-verify). Determinism untouched. Commit.

Sub-cycle 4B — Drone upgrade shop trees (scaling attributes)

Section titled “Sub-cycle 4B — Drone upgrade shop trees (scaling attributes)”

Task 4B.1: bible Drones meta_upgrades (unlocks + scaling attrs)

Section titled “Task 4B.1: bible Drones meta_upgrades (unlocks + scaling attrs)”

Files: data/bible.json (meta_upgrades += Drones-category entries), tests/test_drone_shop.gd (new). Entries (category “Drones”):

  • Unlocks: unlock-drone-bomber/interceptor/disruptor/logistics (type unlock, target class:<id>).
  • Global: drone-slots (the +1 slot, already referenced by MetaState.drone_slots()).
  • Per class (id drone-<class>-<attr>), the SCALING attrs: power (dmg), area (radius), endurance (life), durability (hp), speed, recharge. (Novel class-specific attrs land in 4C.)
  • Test: every Drones-category entry has a valid shape (id/category/base_cost/max_level/…); the unlocks target a known class; ContentDB.meta_upgrades() returns them; they group under ShopCategories.present.
  • Add via the tab-indented python round-trip. Commit (data only — determinism untouched).

Task 4B.2: build_drone_loadout applies upgrade levels + owns_class gates on unlocks

Section titled “Task 4B.2: build_drone_loadout applies upgrade levels + owns_class gates on unlocks”

Files: sim/meta_state.gd (owns_class reads unlock-drone-<klass> levels — sentinel always; build_drone_loadout(sentinel_cfg, defs) folds each class’s drone-<class>-<attr> levels into the cfg scales: dmg = 1 + power*STEP, radius = 1 + area*STEP, life, durability, speed, etc.), sim/sim.gd (the apply path / steps as consts), tests/test_drone_loadout.gd (extend).

  • Test: with levels["drone-bomber-power"]=2, the built bomber spec’s dmg scale rises; owns_class ("bomber") false until unlock-drone-bomber bought, true after. apply_to does NOT mutate PlayerState for drone ids (unknown StatEffect → no-op). Determinism untouched (meta-side). Commit.

Task 4B.3: class behaviours read the cfg scales

Section titled “Task 4B.3: class behaviours read the cfg scales”

Files: sim/sim.gd (thread cfg.speed/cfg.durability/cfg.range into _drone_bomber/_interceptor/ _disruptor/_logistics/_sentinel: e.g. BOMBER_SPEED * cfg.get("speed",1.0), targeting range from cfg.range, deploy max_hp from cfg.durability (done in 4A)), tests.

  • Test: a speed-upgraded interceptor moves faster; a range-upgraded drone targets farther. Determinism byte-identical (cfg defaults 1.0 → unchanged; opt-in). Commit.

Task 4B.4: main passes defs to build_drone_loadout

Section titled “Task 4B.4: main passes defs to build_drone_loadout”

Files: main.gd (build_drone_loadout(sim.sentinel_cfg(), defs) at run start + on Bay deploy, where defs = sim.content.meta_upgrades()). Boot + (playtest) the shop Drones tab → buy → drones scale up.

  • Boot + suite + determinism. Commit. 4B boundary: fetch+merge origin; flag the scaling trees + HP as deploy-ready for Chris to playtest.

Sub-cycle 4C — Per-class novel mechanics (separate plan)

Section titled “Sub-cycle 4C — Per-class novel mechanics (separate plan)”

Written after 4A+4B land. Each is a chunk with its own sim work + determinism check (all opt-in):

  • Sentinel: taunt strength/radius, damage-reflect (return a fraction of contact damage to attackers), shield-pulse on expire (AoE on death).
  • Bomber: multi-bomb payload (N blasts/cycle), armor-pen scaling.
  • Interceptor: target-chaining (strike hits N nearby), crit.
  • Disruptor: effect-unlocks (slow → fire-suppress [enemies in field don’t fire] → weaken [+dmg taken]).
  • Logistics: repair rate/radius, also-repairs-drones, overcharge burst.
  • Whole-branch review (determinism parity, drone-HP lockstep, shop-category integrity, /sim purity). fetch+merge origin; flag Phase 4 ready for Chris to merge + deploy. Then Phase 5 (counter-tuning vs the biomass enemy taxonomy, with playtest data).
  • 4A (HP) is the new system; 4B (shop trees) reuses the existing meta/shop infra (drone upgrades = Drones- category meta_upgrades whose levels build_drone_loadout reads — apply_to skips them). 4C (novel mechanics) is the big varied one → its own plan. Every task is determinism-neutral (drones + drone-upgrade levels are opt-in / default-0). Re-pin NEVER expected — a move = a leak into the no-drone baseline → STOP.