Drone System — Phase 3 (Drone Bay UI) Implementation Plan
Drone System — Phase 3 (Drone Bay UI) Implementation Plan
Section titled “Drone System — Phase 3 (Drone Bay UI) Implementation Plan”For agentic workers: REQUIRED SUB-SKILL: superpowers:subagent-driven-development or executing-plans. Each task = one
bh-dev-chunkcycle (TDD →--import→ boot-check → full GUT suite →scripts/check-test-count.sh→ determinism). Steps use- [ ].
Goal: Make the Phase-2 drone classes player-fieldable — a Drone Bay where you assign classes to your slots and deploy, persisted between runs.
Architecture: A MetaState.drone_loadout (slot → class id) persisted in the save; a render-side
loadout builder turns it into the INPUT specs Sim.set_loadout() already consumes; a DroneBayPanel
overlay (opened from the pause menu) edits the loadout; the drone button quick-deploys the saved loadout
(the Phase-1 input.decoy edge already deploys _current_loadout() — we just keep set_loadout fed from
MetaState). All render/meta-side → determinism untouched (drones are opt-in; the sim already has
deploy_drones).
Tech Stack: Godot 4.6 typed GDScript; CanvasLayer UI; GUT 9.6.
Branch/worktree: feat/drone-system at ~/Claude/bullet-heaven-drones.
Global Constraints
Section titled “Global Constraints”- Determinism byte-identical — survival
1405185210/3122397125, crystals91572468/1173256610. Pure UI + meta + the existingset_loadout/deploy_dronesseam. Re-check each task. - tvOS focus nav (debounced d-pad +
JOY_BUTTON_A;not visibleinput guard) — mirror PauseMenu/MetaShopPanel. - Phase 4 (shop) is what UNLOCKS classes; for Phase 3,
owns_classdefaults all 5 available so the Bay is usable now (Phase 4 tightens it). Numbers/loadout shape are forward-compatible. - Don’t touch
CLAUDE.md/git add -A; coordinate merges with the UI agent at the phase boundary.
File Structure
Section titled “File Structure”sim/meta_state.gd—drone_loadout: Array(slot→class),owns_class(),build_drone_loadout(), persistence.ui/drone_bay_panel.gd(new) — the Bay overlay (slots × class cycler + Deploy).ui/pause_menu.gd— a Drone Bay button (mirrors the Shop button) →bay_requested.main.gd— open the Bay from pause; on Deploy,sim.set_loadout(meta.build_drone_loadout(...))+ trigger a deploy + unpause; keepset_loadoutfed at run start so the drone button quick-deploys.- Tests:
tests/test_drone_loadout.gd(meta),tests/test_drone_bay_panel.gd(UI), pause-menu test update.
Task P3.1: MetaState drones section (loadout + persistence + builder)
Section titled “Task P3.1: MetaState drones section (loadout + persistence + builder)”Files: sim/meta_state.gd, tests/test_drone_loadout.gd (new).
Interfaces — Produces:
var drone_loadout: Array = ["sentinel"]— slot→class id (“” = empty slot). Default a single Sentinel (Phase-1 parity).owns_class(klass) -> bool— sentinel always; others via a futureunlock-drone-<klass>level (default true in Phase 3 so the Bay is usable; Phase 4 adds the unlock entries + this gates on them).build_drone_loadout(defs, sentinel_cfg) -> Array— maps each non-empty owned slot to a resolved spec: sentinel → thesentinel_cfg(fromselected_decoy); others →{klass, life:1.0, dmg:1.0, radius:1.0, heal:0.0, extras:0}(default scales; the sim’s per-class consts are the base, Phase-4 shop raises scales).to_dict/from_dictround-tripdrone_loadout.- Test: default loadout is
["sentinel"]; round-trips through to_dict/from_dict;build_drone_loadoutdrops empty/unowned slots + maps sentinel to the cfg and others to default-scale specs;owns_class("sentinel")true. - Implement; full suite + determinism unchanged. Commit.
Task P3.2: DroneBayPanel overlay
Section titled “Task P3.2: DroneBayPanel overlay”Files: ui/drone_bay_panel.gd (new), tests/test_drone_bay_panel.gd (new).
Interfaces — Produces:
open_bay(meta, defs, max_slots)— showsmax_slotsslot rows; each row CYCLES through["", sentinel, bomber, interceptor, disruptor, logistics](only owned classes), seeded frommeta.drone_loadout. A Deploy row at the end.signal deploy_requested(writes the edited loadout back tometa.drone_loadoutMetaStore.save_state+ emits),signal closed(back without deploy).
- tvOS focus nav (debounced;
ui_accept/JOY_BUTTON_Acycles the focused slot or activates Deploy;ui_cancel/B closes);not visibleinput guard; layer above the pause menu. - Test: opening builds
max_slotsslot controls + a Deploy control; cycling a slot updates the staged loadout; Deploy writes it back tometa.drone_loadout(use a fresh MetaState — assert the array, do NOT exercise the real-save path). Boot-verify the panel renders. - Implement; suite + determinism unchanged. Commit.
Task P3.3: main wiring — open the Bay + deploy + quick-deploy
Section titled “Task P3.3: main wiring — open the Bay + deploy + quick-deploy”Files: ui/pause_menu.gd (add a Drone Bay button → bay_requested), main.gd.
Interfaces: PauseMenu gains signal bay_requested + a button (mirror the Shop button).
main: create the persistent DroneBayPanel (like meta_shop); wire pause_menu.bay_requested → hide pause +
open the Bay (run stays paused); the Bay’s deploy_requested → sim.set_loadout(meta.build_drone_loadout( defs, _sentinel_cfg())), deploy the drones now (drain charge + sim.deploy_drones(...) or set a pending
“deploy on next tick”), then close the Bay + resume; closed → re-show pause. At run start, call
sim.set_loadout(meta.build_drone_loadout(...)) so the drone button quick-deploys the saved loadout
(replaces the Phase-1 single-Sentinel fallback). _shop_open()-style guard so main hotkeys don’t fire
under the Bay.
- Boot + playtest desktop: pause → Drone Bay → assign classes → Deploy → the chosen drones launch; the drone button re-deploys the saved loadout. Determinism unchanged. Commit.
- Deferred (note it): the spec’s tap = deploy / hold = open Bay on the drone button (needs press-
duration detection) — Phase 3 ships the pause-menu Bay entry + drone-button quick-deploy; tap/hold is a
later input refinement. Targeting priority lists (auto vs per-type) also deferred — Phase 3 fields
classes with
autotargeting (policy 0); the priority-list UI is a follow-up.
Task P3.4: Phase-3 review + merge prep
Section titled “Task P3.4: Phase-3 review + merge prep”- Self-review (determinism,
/simuntouched, save round-trip, tvOS nav). fetch + merge origin/main into the branch; FLAG Phase 3 ready for Chris to merge + deploy + playtest the fieldable classes. Phase 4 (shop trees: per-class unlock + attribute upgrades ++1 slot) gets its own plan — that’s what gives the classes their upgrade depth + gates ownership.
Sequencing notes
Section titled “Sequencing notes”- P3.1 (meta) and P3.2 (panel) are independent; P3.3 wires them into the run. All pure UI/meta/seam → the determinism baseline never moves. After Phase 3, the classes are player-fieldable with default stats; Phase 4 adds the shop depth, Phase 5 the counter-tuning.