Skip to content

Instantly share code, notes, and snippets.

@csanz
Last active August 9, 2026 00:39
Show Gist options
  • Select an option

  • Save csanz/473e9858b399b3a0bc2a22a186d55403 to your computer and use it in GitHub Desktop.

Select an option

Save csanz/473e9858b399b3a0bc2a22a186d55403 to your computer and use it in GitHub Desktop.
PRD — Harpoon & Tow Cable

PRD — The Harpoon Run, Choreographed

Battle of Hoth Simulator · an AI wingman flies the cable; the player watches it happen

Status: v3 — client decisions applied: purely cinematic (no player kill window), Rogue 3 cannot be downed, callouts arrive as client-provided video transmissions, and the player is Luke Skywalker — interactivity follows the story Baseline: main @ 9c73330. The opening/transmission machinery is in active flux (d4e9305 removed the second Luke transmission; the clip is still hosted) — symbols authoritative, line numbers advisory for main.js. The player-skill Harpoon PRD v5 remains the spec for a separate future game app (client decision). Walker geometry and the collapse carry over from it unchanged.


0. The one-paragraph version

Luke's own transmission gives the order — use your harpoons and tow cables, go for the legs — and Rogue 3 answers it: breaks off, drops to the deck, threads the leg gap once to open the run, and fires the harpoon rearward from the tail mount as the leg falls behind. Four low circuits with the cable trailing from the tail, the wrap accumulating strand by strand, the walker's own head tracking the little craft it cannot hit — then it takes one more stride, can't, pitches forward and comes down chin-first into the snow, intact. The fireball belongs to the beat after: Rogue 3 wheels back and delivers the kill shot, exactly as the film cuts it. WHOA — THAT GOT HIM. The player is Luke Skywalker, Rogue Leader — the order in the corner square is their own transmission going out — and the run plays the way the story played: Wedge flies it, Luke covers.


1. What review changed

Film-accuracy round

v1 said The film says v2
"Rogue 2 has the cable" Rogue 2 is Zev Senesca — he dies at Hoth. The cable kill is Wedge Antilles, Rogue 3, with gunner Wes Janson. The most catchable error in the document. Rogue 3 leads; the success line name-checks the gunner
Harpoon from a wingtip The T-47 is a two-seater; the harpoon gun is the rear-facing gunner mount, fired backward as the speeder passes — the tail-trailing cable is the sequence's iconic silhouette §2 beat 3 — rear mount, fired on the flank pass
Fireballs on impact The walker comes down intact; the explosion is the post-fall kill shot on the downed hull §2 beats 7–8 — impact is snow and groan; the fireball moves to the kill shot
Run triggers on a flat timer The runs happen strictly after Luke's order — blasters fail, the order, then the cable §8 — chained to the re-wired second transmission
One voice, single-seat callouts The run is a pilot/gunner duet ("Cable out! Let her go!" is Janson) §3 — paired lines; abort shaped on "I've lost my gunner"
"The player is Rogue Leader" Luke is Rogue Leader — and he's on the wire in this demo's own transmissions Player is unnamed (or Rogue 5); wingmen are Rogue 2/3/4

Cinematography round

v1 defect v2
Staged the events, not the shots — the camera is an 11 m spring arm on the player's craft, so the audience's viewpoint is the player's flight path, and nothing put the player anywhere useful §3 — selection prefers the walker nearest the player (≤ ~250 m); the alert lands 4–6 s before the harpoon so the player has time to arrive
13 s of orbit with one variable (radius, −3 m/lap) — a frozen bank, a loop §4 — the run-in threads the leg gap (the money shot, free — the corridor is the PRD's own verified 3.90 m); laps differentiated by altitude, ω modulation, near-misses; an imperfection budget
The target walker is a prop for 13 s §4 — its head tracks the runner (_aimHead takes a per-call target; one override), and its fire redirects across the runner's path
No long-range carrier for the run itself §5 — every lap gets a light (anchor flash per completed turn, bolt-pool idiom) and a voice (the unpositioned voice bus is the one channel that survives 300 m)
Yaw bias aimed at the runner during the collapse — the runner deliberately exits frame §6 — subject handoff: runner at the call, walker from the bind onward; DoF rack and yaw bias agree on subject
Beat 2 booked 4–6 s for an unbounded transit §2 — selection distance-gates (~350 m); beat 2 is the one elastic beat, and the voice call fires when the run-in commits
Fixed-duration bind vs a gait-phase-dependent trigger §2 beat 5/6 — lap 4 ends at the first bound-leg swing-phase entry after four laps (bounded < ~2.3 s by the measured plant windows)

2. The sequence, beat by beat

Total ~26–30 s. Beats on a normalised clock (_runT), the goDown idiom; beat 2 is the only elastic beat.

# Beat Duration What happens Seen / heard
1 The order clip Luke's second transmission plays (§8) — the order itself is the herald The film's own line, in the corner square
2 The answer elastic, ≤ ~6 s Lead selected (§3); runner breaks off, dashes, drops to the deck. The callout fires when the run-in commits, not on a timer showFlightMsg("ROGUE 3 HAS THE CABLE", 2.6, true) + pilot voice + scaled marker
3 Thread & fire ~2 s The opening pass goes through the leg gap under the belly (corridor 3.90 m, belly floor ~16 m — verified geometry, scripted path, free money-shot); as the rear leg falls behind, the harpoon fires rearward from the tail mount Gunner: "Cable's out — let her go!" · harpoonFire/harpoonBite · anchor flash
4 Four circuits ~12–14 s Scripted orbit in the walker-local frame (§4), cable trailing from the tail, one strand banked per lap. The walker performs: head tracks the runner, cannon fire chases it cableRatchet + anchor flash per lap; lap-3 pilot line: "One more pass"
5 Bind & detach ≤ ~2.3 s Lap 4 ends at the first bound-leg swing-phase entry (gait-aware); cable released at the tail "Detach cable!" — runner breaks out and up at SPEED_BREAK
6 Failed stride 0.4–0.8 s The commanded swing stops dead; the head pitches down the fall line (telegraph) walkerSnag; ~0.7 s of clearance before the topple commits
7 Collapse ~1.5 s v5 §5.3 B–C: momentum topple, knees buckle, head whips, chin drives in at 45–52°. No fireball. Snow wave, groan, sparks, trauma by distance
8 The kill shot ~3 s Fully scripted — Rogue 3 wheels back and finishes the downed hull, then the fireball. Purely cinematic by decision; the interactive version lives in the future game app. "Whoa — that got him!" · WALKER DOWN — ROGUE 3

Rogue 3 cannot be downed — client decision, story-matching. The run is invulnerable to ambient attrition and enemy fire for its duration; the only interruption is a literal player collision, after which the cable slackens away and the run re-forms and tries again after a cooldown. The story self-heals; it does not lose its hero to a dice roll.


3. Lead selection & the alert

target = eligible walker NEAREST THE PLAYER (≤ ~250 m preferred)   ← the audience is in the score
lead   = healthy wingman × not mid-attack × within ~350 m of target × flank approach

Rogue 3 is the preferred first pick, so the guaranteed first run (§8 ③) is film-correct. Seats announce as Rogue 2 / 3 / 4; the player is Luke Skywalker, Rogue Leader (client decision). The corner-square transmissions are Luke's own broadcasts going out to Rogue Group — the player hears themself give the order. The governing principle, stated once: interactivity follows the story. Actions the film gives Luke belong to the player; actions it gives Wedge are sim-flown.

The alert, three channels, landing 4–6 s before the harpoon:

  1. HUDshowFlightMsg, zero new DOM.
  2. Voice — pilot/gunner pairs per beat. The client will supply these as video links (the introFilm transmission pattern — the corner square — already takes a URL list); until they arrive, ship on the excited-N placeholder pool with per-event manifest keys so the swap is a URL change, not a code change. Blocked-on-client: the video links.
  3. MarkerMuzzleMarkers ring over the runner, radius scaled with camera distance (≈ max(craftScale, dist × 0.015)) so it holds apparent size at 250 m, held through beat 3.

4. The choreography — scripted path, staged for the camera

Scripted path in the walker-local frame (precedent and determinism arguments unchanged from v1; sustained lateral acceleration stays inside the wingmen's own LAT_ACCEL = 75). The pilot publishes the standard controller surface, so hull, jet and camera read it with zero new presentation code. v2 adds the performance layer:

The laps are not a loop:

Lap r Character
entry The thread: through the leg gap, harpoon fires rearward on exit
1 24 m Wide and high in the band; the walker's head comes round
2 20 m Lower; one deliberate near-miss past a planted leg; walker fire chases
3 17 m "One more pass"; altitude sags and catches (the one scripted wobble)
4 15 m Tight and committed; ends gait-aware at the bound leg's swing phase

Imperfection budget (all feed the real v²/r → lean path, so the bank breathes): ±10–15 % ω modulation, 1–2 m low-frequency radial noise, a few degrees of bank overshoot on each radius transition, ±0.8 m altitude noise.

The walker performs: a per-walker look-target override — _aimHead already takes a target per call (walker.js:1139); the run points it at the runner for the duration — and its cannon fire redirects across the runner's path (the loose-aim scenery contract holds: nothing is actually hit by aim). The head-down pitch along the fall line in beat 6 doubles as the topple telegraph.

What the run suspends (the cableRun phase on the Wingman wrapper): the fate/attrition clock (scripted runs don't roll scripted deaths — only literal bolts and rams abort), avoidance against the target walker, gun auto-fire. Formation discipline skips the target walker; its recycle/leash is suppressed (v5 §5.5).


5. The collapse — and reading the run at range

v5 §5.3–5.6 verbatim: failed stride, three phases, multi-slot knee override, the _composeWorld branch, capsules live through the fall, the walker-wreck container. Minus the impact fireball (film round): impact is the snow wave, the groan, sparks and trauma by distance; the fireball belongs to beat 8.

Long-range carriers, per beat — a player at 300 m must be able to follow eyes-half-closed: the order (video + voice), the answer (voice + HUD), the fire (anchor flash), each lap (ratchet + flash), the snag (voice-adjacent groan through the canonical near³ distance model), the collapse (sub-boom + trauma), the kill (fireball — visible from anywhere on the field).


6. Watching it

No camera takeover (unchanged). The subject schedule fixes v1's aim error:

Beats Yaw-bias + DoF subject
2–3 The runner — who has the cable
4 Free — the alert did its work; the player is flying
5–8 The target walker — the what; settled before the stride fails

Bias is gentle, decaying, and any stick input overrides it. Mouse look is already free. Player non-interference stays one-way; the beat-8 kill window is the deliberate exception where the player is invited in.


7. Technical architecture

As v1 (src/game/cableRun.js, cableRibbon.js, shaders, audio set), plus the review deltas:

Change From
Craft-end cable anchor is the tail mount (negative-z offset — the ENGINE_NOZZLE.backZ convention) film round
Per-walker look-target override consumed by _aimHead cinematography
cableRun phase on Wingman: suspends fate clock, target avoidance, auto-fire cinematography
Marker radius scaling; per-event manifest keys cinematography
Kill-shot beat: fully scripted return strafe (no player window — client decision) film round
Impact de-fireballed; explosion budget spent on beat 8 film round

Not modified: CharacterController, CameraRig internals, the collision resolver, bakeWalker.mjs.


8. Triggering — chained to the order

The film's structure is blasters fail → the order → the run, and the demo already owns the order: the second Luke transmission (wired at c5ea197 to the player first closing on the walker line, removed in d4e9305, clip still hosted).

  1. Player closes on the walker line → re-wire the second transmission (the createIntroFilm block deleted in d4e9305; mid-battle staging — cuts in almost at once, per its own design note).
  2. The guaranteed first run (③) fires at the clip's end — Luke's actual line replaces v1's invented "GO FOR THE LEGS" HUD text. Rogue 3 answers.
  3. Ambient cadence (①) arms only after that first ordered run — no cable ever flies before the order has been given. Gap ~90 s, capped by cableWreckMax.

Player call-in (②) stays deferred.


9. Edge cases

v1's table stands, plus:

# Case Resolution
11 Player under the fall line at beat 6 The head-down telegraph + 0.7 s clearance; the crush applies (v5) — being under a falling walker is allowed to hurt
12 Player strafes the downed hull before beat 8 Harmless — bolts don't damage walkers; Rogue 3's scripted shot still lands the fireball
13 Second transmission already seen this session The order beat degrades to voice + HUD only; the run still fires
14 Runner's thread pass blocked (leg phase wrong) The entry thread is scripted to a plant window (2.04/2.21 s, trivially hittable); worst case the thread becomes a flank pass — the harpoon beat is unchanged

10. Phases & acceptance

Phase A — The run without a fall

Now includes: tail-mount anchor, the entry thread, lap differentiation + imperfection budget, walker head-tracking, elastic beat 2, scaled marker, subject-scheduled yaw bias. Accept: v1's criteria, plus — the cable visibly trails from the tail; the entry pass goes through the leg gap; no two laps read identically; the walker's head follows the runner; the callout names Rogue 3; the alert lands ≥ 4 s before the harpoon with the player ≤ 250 m away in the common case.

Phase B — The collapse & the kill shot

v5 Phase 4 verbatim, minus the impact fireball, plus the fully scripted beat-8 strafe. Accept: v5 Phase 4 criteria; the walker lands intact; the fireball fires only on Rogue 3's scripted kill shot; the fall-line telegraph precedes the topple by ~0.7 s; nothing the player does (short of ramming the runner) can derail the sequence.

Phase C — The order & cadence

Re-wire the second transmission; chain ③ to it; arm ① after; voice-line drop-ins; distance mix. Accept: no run ever precedes the order; a fresh session sees order → answer → kill in its first minutes; a player at 300 m can narrate the whole sequence from audio + HUD alone.

Out of scope

Player-flown cable (v5, separate app) · camera takeover · cable-vs-craft collision · multi-cable · AT-ST targets.


11. Decisions (all four v2 questions answered by the client)

Question Decision
Kill-shot window None — purely cinematic. The story flows as filmed; the interactive version is the future game app's business.
Voice assets Placeholders now; client supplies video links for the callouts. Build does not block on them.
Rogue 3 protection Absolute. "You can't down Rogue 3" — the story keeps its hero.
Kill credit Deferred with the game app itself. Standing principle for that app: the player is Luke and plays Luke's actions — the harpoon run stays Wedge's.

The one open item is an asset dependency, not a question: the client's video links for the transmission callouts (§3).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment