guts/web/js/flight/tuning.js
jing 0a038582a7 [lane B] Round 1: flight controller, cannon, enemy framework on the stub
Flight (js/flight/): tube-mode controller in spline space (s,x,y) — flow-locked
forward motion, throttle as a spring-back lean, boost + i-frames, banking chase
cam on the parallel-transport frame, arcade wall response (shove + graze damage,
never a hard stop). Twin-stick input: WASD/left-stick moves the ship in the disc,
mouse/right-stick aims, Shift/Ctrl is throttle. Gamepad supported.

Combat (js/combat/): lysozyme cannon (pooled, instanced, heat-limited with
hysteresis lockout) + antacid torpedo emitting level:neutralize. Enemy framework
with floater/seeker/turret and turn-rate-limited homing darts, spawned from C's
level:event. Coat/hull model and the full bus surface E builds against.

Measured (deterministic stepper): flow-lock exact (3s at flow 14 -> s=44.0);
boost peak 30.8 u/s; heat 4s fire -> 2s lockout; wall slam 13.3 coat at 11.3 u/s
with forward speed untouched; 10 combat draws at 55 live enemies (budget <=80);
0.10 ms/frame CPU; 0 leaks over 20 create/dispose cycles. fps NOT claimed — rAF
does not run in the automated pane. Evidence: docs/shots/laneB/.

Three findings escalated in LANE_B_NOTES:
- qa.sh gate #1 is a no-op: node --check silently exits 0 on ESM, so the syntax
  gate has never checked anything. I shipped a real SyntaxError past a GREEN qa.
  Fix + verification handed to F.
- Surf is structurally broken: the peristalsis wave (13.64 u/s) is slower than
  the player (19.6 u/s), so level 2's signature mechanic loses to mashing
  throttle. Not tunable from this lane; escalated to A/F with options.
- Custom ShaderMaterials need #include <colorspace_fragment>, else ART_BIBLE's
  hostile amber reaches the display as rgb(255,26,6). Fixed here; stub/walls
  inherit it.

js/flight/dev.html is a dev harness (boot.js has no player wiring yet); it
retires once F pastes the snippet in LANE_B_NOTES.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 08:56:32 +10:00

114 lines
6.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// flight/tuning.js (Lane B) — THE FEEL. Every number that decides how GUTS handles lives
// here and nowhere else. Tuned by hand on the stub esophagus; re-tune per biome later.
//
// Stub reference frame (js/stub/world_stub.js), which all of these are sized against:
// biome.flow = 14 u/s · radius 10 ±15% wobble => 8.5..11.5
// wallRho = radius waveAmp(0.9) skin(0.6) => a ~7.0..10.0 playable disc radius
// peristalsis crest travels at WAVE_W/WAVE_K = 3.0/0.22 = 13.64 u/s
//
// SURF IS A SHOVE, NOT YET A RIDE — and that is a world-side problem, not a tuning one.
// Measured in-engine this round (numbers in LANE_B_NOTES §surf):
// crest phase speed = WAVE_W/WAVE_K = 3.0/0.22 = 13.64 u/s
// player top speed = flow × throttleMax = 14 × 1.4 = 19.6 u/s
// A wave travelling 13.64 u/s cannot carry anything faster than 13.64 u/s, so "ride the
// crest" can never be the fast line while the wave is slower than the player. Worse, the
// bonus is self-cancelling: the crest window is fixed in SPACE, so a bigger surf.gain just
// ejects you out the front sooner. Measured distance gained per crest is ~3.0 units at
// gain 0.4, 0.75 AND 1.2 — identical. Tuning this number cannot fix it.
//
// So surf currently reads as what the wave physically is: a rhythmic peristaltic SHOVE as
// each crest sweeps over you (~0.3-0.6 s at 24.5 u/s), which is honest and feels good, but
// it is not the level-2 speed line the GDD asks for. The fix belongs to whoever owns the
// wave constants (stub = F, world = A): crest speed must exceed 19.6 u/s. Escalated in
// LANE_B_NOTES §"-> Lane A / F" with options. Do not paper over it here.
//
// gain below is therefore chosen for FEEL (punch of the shove), not for speed economy.
export const TUNING = {
ship: {
radius: 0.9, // collision sphere (hull reads ~1.9 long, 1.2 wide)
},
// --- forward motion along s -------------------------------------------------------
flow: {
throttleMin: 0.6, // GDD: throttle is ±40% of biome flow, never a full stop
throttleMax: 1.4,
throttleRate: 1.8, // throttle units/s while held (0.6 -> 1.4 in ~0.45 s)
throttleReturn: 0.9, // /s spring back to neutral 1.0 when hands off
},
boost: {
gain: 1.2, // +120% of biome flow (GDD)
duration: 0.55, // s of burst
cooldown: 2.4, // s from spend until available again
iframes: 0.35, // s invulnerable "at spend" (GDD) — shorter than the burst,
// so boosting *through* something is a read, not a get-out
fovKick: 13, // degrees of extra FOV at full burst (speed you feel)
},
surf: {
threshold: 0.45, // world.flowPulse(s,t) above this = on the crest.
// pulse=pow(sin,3) so 0.45 => sin>0.766 => the peak 22% of
// each wave: a ~6.3 u window in a 28.6 u wavelength.
gain: 0.75, // +75% flow while on a crest => a 24.5 u/s shove for ~0.3-0.6 s.
// Feel-tuned only; see the surf note in the header before
// touching it expecting a speed change.
ramp: 3.0, // /s blend in/out so cresting doesn't pop
},
// --- movement inside the cross-section disc ---------------------------------------
disc: {
accel: 78, // u/s²
maxSpeed: 13, // u/s — crosses the ~16 u tube in ~1.2 s: dodgeable, not twitchy
damping: 6.5, // /s exponential drag => coasts to rest in ~0.3 s
},
// --- aim: the reticle leads, the ship follows (Star Fox lag) -----------------------
aim: {
distance: 26, // u ahead (in s) that the reticle plane floats
sensitivity: 0.022, // disc units per pixel of mouse movement
padSpeed: 20, // u/s at full right-stick deflection
range: 9, // max |aim| from the centreline (≈ the widest wallRho)
recenter: 0.8, // /s drift back to centre when hands off
lag: 9, // /s spring — the ship's nose chases the reticle, never snaps
},
// --- attitude: bank into lateral motion -------------------------------------------
bank: {
gain: 0.055, // rad of roll per u/s of lateral disc velocity
max: 0.75, // rad (~43°)
rate: 7, // /s spring toward target roll
pitchGain: 0.03, // rad of pitch per u/s of vertical disc velocity
},
// --- chase camera ------------------------------------------------------------------
cam: {
back: 7.2, // u behind the ship (along tan, measured in s)
up: 1.4, // u along the frame normal — ship sits low-centre
lookAhead: 16, // u ahead of the ship for the look target
discLag: 8.0, // /s spring on the camera's own disc offset
discFollow: 0.82, // camera tracks only 82% of the ship's offset, so hard dodges
// visibly move the ship within the frame instead of gluing it
aimInfluence: 0.35, // how much the look target drifts toward the reticle
fov: 75, // matches boot.js's camera so wiring in changes nothing
fovRate: 4.5, // /s toward target fov
},
// --- durability (GDD: regenerating mucus coat over a pickup-only hull) -------------
coat: {
max: 100,
regen: 8, // /s
regenDelay: 2.5, // s after the last damage before regen resumes
// ambient drain is the biome's: world.biomeAt(s).coatDrain (esophagus = 0.5/s)
},
hull: { max: 100 },
// --- wall contact: arcade. Shove + graze damage, NEVER a hard stop -----------------
wall: {
restitution: 0.35, // fraction of inward velocity returned on contact
grazeFloor: 3.0, // impact u/s below this is free — you can lean on the wall
damagePerSpeed: 1.6, // coat damage per u/s of impact above grazeFloor
maxHit: 28, // cap: a full-speed slam is a mistake, not a death
shoveOut: 1.0, // extra u/s pushed off the wall
cooldown: 0.25, // s between wall damage ticks (scraping ≠ machine-gun damage)
},
};