Landed before the cut: surf as a speed-lock onto world.crestSpeed(s) with surf.authority (ruling #1) — riding beats throttle and holding a crest is throttle discipline; tuning.js header corrected to the real wallRho frame (C's flag); enemies.js reads rho as a FRACTION of safe radius (C's placement law); balance.js hazard + pickup constants; NEW hazards.js (reflux_surge / aortic_squeeze / ring_gate, self-scheduling telegraphs, reset(fromS) built for respawn) and NEW pickups.js (all five kinds, shape-coded). Cut off before: wiring — nothing imports hazards/pickups yet; the fiction-id -> archetype resolve (round-2 task #1) does NOT exist despite the enemies.js comment saying index.js does it; player.kill/shove/refill are called but were never added; checkpoint/respawn and gate -> level:complete not started; NOTES unwritten. F lands the glue next commit. Committed by F to protect the shared tree. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
126 lines
7.4 KiB
JavaScript
126 lines
7.4 KiB
JavaScript
// 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.
|
||
//
|
||
// Reference frame these are sized against — CORRECTED round 2 (C caught the stale numbers,
|
||
// LANE_C_NOTES §→ Lane B #4). The round-1 header quoted the round-0 stub; A's real world has
|
||
// since measured the esophagus wave at amp 1.4 (not 0.9) + breathe 0.15, and `wallRho`
|
||
// subtracts BOTH plus SKIN:
|
||
// biome.flow = 14 u/s · radius 10 ±15% wobble => 8.50 .. 11.50
|
||
// wallRho = radius − (amp 1.4 + breathe 0.15) − SKIN 0.6 => 6.35 .. 9.35 playable disc
|
||
// minus ship.radius 0.9 => 5.45 .. 8.45 free disc
|
||
// The envelope shrank ~0.65 u under us. disc.maxSpeed/accel and the graze numbers were sized
|
||
// against the old 7.0..10.0 and still read fine at 6.35..9.35, but that is luck, not design —
|
||
// if A retunes the wave again, re-check `disc` and `wall` here, not just C's clearances.
|
||
//
|
||
// SURF: FIXED THIS ROUND — it is a ride now, not a shove.
|
||
// Round 1 measured the mechanic as strictly dominated: a crest travelling at flow (13.64 u/s)
|
||
// cannot carry anything faster than itself, while throttle-mashing gave 19.6 u/s, so the
|
||
// correct play was to ignore the level's signature mechanic. Tuning could not fix it (the
|
||
// crest window is fixed in SPACE, so a bigger bonus just ejected you sooner — measured ~3.0
|
||
// units gained per crest at gain 0.4, 0.75 AND 1.2, identical).
|
||
//
|
||
// F's round-2 ruling #1 fixed it at the source: `crestSpeed = CREST_FACTOR(1.6) × flow`, which
|
||
// beats throttleMax 1.4. So the wave now outruns the player and B **speed-locks to
|
||
// world.crestSpeed(s)** while riding instead of adding a flat bonus. Riding at flow 14 =
|
||
// 22.4 u/s vs 19.6 for mashing: +14%, and because you match the crest's own speed you STAY on
|
||
// it instead of being thrown off the front. Throttle still decides how long you hold it —
|
||
// see surf.authority. Read the crest, don't hold W.
|
||
|
||
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. Under CREST 1.6 the wavelength grew to ~45.7 u at flow
|
||
// 14, so that window is now ~10 u long (was ~6.3) — the crest is
|
||
// a wider, faster lane than it was in round 1.
|
||
authority: 0.85, // how completely the crest owns your speed while riding, 0..1.
|
||
// THE knob. At 1.0 the wave takes you at exactly crestSpeed and
|
||
// throttle stops mattering while surfing — you'd ride forever
|
||
// and the mechanic would have no skill floor. At 0.85 you sit
|
||
// just under the crest and drift off the back at a rate YOU set:
|
||
// throttle 1.4 -> 21.98 vs crest 22.4 => drift 0.42 u/s, ~12 s ride
|
||
// throttle 1.0 -> 21.14 => drift 1.26 u/s, ~4 s ride
|
||
// throttle 0.6 -> 20.30 => drift 2.10 u/s, ~2 s ride
|
||
// So catching a crest is positioning; HOLDING one is throttle
|
||
// discipline. Never applied when it would slow you (boosting
|
||
// past the crest keeps your speed — see player.js).
|
||
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)
|
||
},
|
||
};
|