Weapon FX v2 batch 2: Beam + Railgun blind shootout, Railgun mark, Thunder Wake + Firestorm zones — design
Weapon FX v2 batch 2 — design
Section titled “Weapon FX v2 batch 2 — design”Approved by Chris 2026-10-01. Parent spec: 2026-09-28-weapon-fx-v2-design.md (house rules, scaling
model, element motifs — all still apply). Branch fx-v2-batch2-beam-railgun.
- Beam and Railgun are the weakest weapons on the VFX board (
tools/vfx_library/library_manifest.gd, both rated 2). They share one legacy path,FxManager._BeamNodevia the"beam", "railgun_lance"handler (fx/fx_manager.gd:463), which reads onlypos,dir,length,element. - Bug 2 of the parent spec is still open: the handler ignores
width,powerandevolved, so Beam Lance (width ×2.5) and Prism Lance look identical to the base weapons. (docs/RELEASE-1.0.1.md:46-48 wrongly says batch 1 fixed it — batch 1 only fixed the slash arcs; corrected by this batch.) - Three player-facing mechanics have no renderer (red MISSING cards): the Railgun mark
(
enemies.mark_timer/mark_amt, +20% damage from every source, 0.6 s, compounding with themarkmod), Thunder Wake puddles (WeaponThunderWake._zones) and Firestorm zones (WeaponWildfire._burn_zones). Both zone arrays live on the weapon instance, notsim.zones, soZoneRenderernever sees them.
Scope change vs the parent spec: Thunder Wake + Firestorm zones move from batch 3 into batch 2 (Chris’s call — cheap plumbing, closes two MISSING cards). The Thunder Wake and Wildfire weapon looks stay in batch 3.
Part A — the looks: a blind shootout
Section titled “Part A — the looks: a blind shootout”Format (Chris’s call): four models each build one Beam look and one Railgun look — Fable 5.1,
Opus 5.5, Sonnet 5.5, Haiku 4.5. Eight candidates total, each a separate agent in its own worktree,
under the existing contract tools/weapon_vfx/CANDIDATE_BRIEF.md.
- Artistic licence, explicitly including mechanics. Candidates may reinterpret what the effect is
and are invited to propose weapon mechanic changes that would make the VFX better. Proposals are
written in
CANDIDATE.md(and, if the look depends on one, prototyped behind aconstinside the candidate folder) — never by editing/sim. Proposals are reviewed by Chris as a separate list after the pick; none ship by default. - Blind. Slugs are model names; the board shows letters only. Letters are shuffled independently
per weapon so a model’s style can’t be tracked across the two boards. Mapping in
fx/weapons/candidates/<wid>/_labels.json, not opened until the pick is made. - Staggered: Beam’s four first, then Railgun’s four, so windowed captures don’t queue on
scripts/dev/godot-window-lock.sh. - Judged on the shootout board (p0 + evolved, plus a rung row). The in-game live switch from the
Pulse shootout is uncommitted on
pulse-shootout; generalising it now would collide, so it is out of scope for this round.
Railgun candidates also own the mark
Section titled “Railgun candidates also own the mark”The mark is Railgun’s payoff, so it is part of the Railgun look, not a separate job. Each Railgun
candidate additionally delivers mark.gd + mark.gdshader + mark_mat.tres in its folder:
mark.gdextendsWeaponFxLayer(immediate mode:begin/put/commit), noclass_name, and exposesfunc draw_marks(enemies: EnemyPool, now: float) -> void, called once per rendered frame. One quad per enemy withmark_timer > 0;mark_timerdrives the fade-out,mark_amtmust make a stacked mark visibly stronger than a single one. Tint is the weapon’s light identity colour.- It must read as a target tell at a glance without whiting out a dense swarm (BASE_DIM budget rules from the brief apply). It is a second draw call; acceptable.
- A Railgun candidate with no mark fails the brief.
Part B — prep (single author, before any candidate is dispatched)
Section titled “Part B — prep (single author, before any candidate is dispatched)”- Generalise
tools/weapon_vfx/shootout.gdbeyond Pulse: per-weapon event synthesis with the real event fields (pos,dir,length,width,element, plus theWeaponVfx.stampfieldswid,power,evolved,rungs), using each weapon’s real sim consts and evolve multipliers for width and length; the v1 baseline column drawn through the real_BeamNode; Railgun draws two lances when evolved (Prism Lance forceslance_count ≥ 2). Pulse must keep working unchanged. - Mark scene in the shootout: a row of dummy enemies with mark states (fresh single, fading single,
stacked) driving each candidate’s
draw_marks. - Mark hook in the live game, dormant until a winner exists: the per-frame call site (render side,
read-only on
sim.enemies) that will drive the promoted mark layer. - Brief addendum for this batch: the mark contract above, the mechanic-proposal invitation, and the per-weapon notes (Beam: fast 0.1 s cooldown continuous-ish beam, light, rungs width/reach; Railgun: farthest-target piercing lance, light, rungs mark/lances(aether — motif not built, render as no-op)).
Part C — zones (single author, in parallel with the shootout)
Section titled “Part C — zones (single author, in parallel with the shootout)”- Thunder Wake puddles:
ZoneRendererreads each pilot’s Thunder Wake via its existing (currently uncalled)zones()accessor and draws a lightning-tinted disc fading onremaining / duration, in the same style assim.zones. - Firestorm zones: add a read-only
burn_zones()getter tosim/weapon_wildfire.gd(the only/simedit in this batch — no RNG draw, no state, no checksum effect) and draw a fire-tinted disc with a slow ember flicker so it doesn’t read as a Supernova remnant crater. - Neither zone carries an element field; the tint comes from the weapon’s element.
Part D — promotion (after Chris picks)
Section titled “Part D — promotion (after Chris picks)”COPY each winner into fx/weapons/ with a class_name, wire into V2_LOOKS, remove from
PENDING_V2_LOOKS (tests/test_weapon_fx_guards.gd), dispatch beam/railgun_lance through the look
(legacy _BeamNode stays as the v2_active() fallback), add the look + mark pipelines to PrewarmPass
family 12, and update manifest entries/scores. Every candidate folder, the winner’s included, stays
in fx/weapons/candidates/ as submitted (superseded 2026-10-01: Chris’s standing policy is that the
candidates are a permanent archive, never deleted, so a future model’s take can be compared against
all of them in-game; see fx/weapons/candidates/README.md). Mechanic proposals go to Chris as a list; any approved one is its own follow-up change with a
determinism check.
Testing
Section titled “Testing”scripts/gate.shgreen on every candidate worktree and on the merged branch.- Every new or edited shader compile-checked in a windowed run under BOTH
--rendering-method mobileand--rendering-method gl_compatibility;project.godotrestored after. - Prep and zones are TDD (GUT): shootout event shapes per weapon (width/evolved actually differ), mark
hook draws one instance per marked enemy and none for unmarked, a live puddle / Firestorm zone
produces a draw,
burn_zones()returns the live zones. Determinism baselines must be unchanged. - VFX library:
railgun_mark,thunder_wake_zone,wildfire_zonemove from MISSING to CURRENT with real scores;tests/test_vfx_library_manifest.gdpins them.
Out of scope
Section titled “Out of scope”Pulse (own shootout on pulse-shootout), the Thunder Wake / Wildfire weapon looks (batch 3), building
the aether/poison/time motifs, the in-game candidate switch, enemy element reactions (phase 6).