The 6DOF unlock the player header has promised since round 1 ("Arena/6DOF mode is round 2;
modeAt() is guarded below"). It unblocks L1 and L3, which are both arena levels in the GDD and
have been skeletons waiting on this.
The insight that made it small: spline space ALREADY spans a room — s is the axial coordinate,
x/y are the cross-section — and world.collide() already resolves against arena spheres
(arenaSpatial, Lane A round 2). So this is not a new controller. The only real difference
between a tube and a room is WHO OWNS s: in a tube the flow carries you and you may only trim
it +/-40%; in a room `intent.throttle` becomes signed thrust with its own damping, so you can
stop dead and you can reverse. That is the whole feature, and it is what "slower, spatial,
exploratory" actually means in play.
- tuning.js: arena{thrust 44, maxSpeed 11, damping 2.2}. maxSpeed sits UNDER the esophagus's
12-20 flow on purpose — the GDD asks for slower.
- player.js: st.vs (axial velocity), the mode branch, surf disabled in rooms (no travelling
wave to ride, and a hanging surfBlend would speed-lock you to a crest that isn't there),
respawn() clears vs so a death in a room can't leak momentum into a tube.
- player:state gains `mode`; `flow` reads 0 in a room and `speed` goes signed.
- hud.js prints "6dof · free" instead of "flow 0 · thr 100%" — two lies on one instrument.
Safe by construction: both arena triggers are opt-in (level.arenas, or a segment marked
mode:"open"). L1/L2/L4 declare neither and are byte-for-byte unaffected.
Verified in L3's stomach: tube hands-off carries you at 4.53 u/s, acid-sea hands-off coasts to
a dead stop (0.0), and a room lets you travel BACKWARDS (-3.8u). Bus reports
{mode:arena,flow:0,speed:0} vs {mode:tube,flow:3,speed:3}.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
137 lines
8.2 KiB
JavaScript
137 lines
8.2 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
|
||
},
|
||
|
||
// --- arena / 6DOF: in a room, the anatomy stops carrying you -----------------------
|
||
// GDD: "the same controller unlocked to free 6DOF, Descent-style, no gravity. Slower,
|
||
// spatial, exploratory." The felt difference is NOT extra axes — spline space already spans
|
||
// a room (s is the axial one, x/y are the cross-section) — it is that YOU own the forward
|
||
// axis. You can stop. You can reverse. Nothing is shoving you at the boss.
|
||
arena: {
|
||
thrust: 44, // u/s² axial. Softer than disc accel 78: a room is read, not strafed
|
||
maxSpeed: 11, // u/s — under the esophagus's 12-20 flow on purpose (GDD: "slower")
|
||
damping: 2.2, // /s — a long coast (no gravity), but it does come to rest
|
||
},
|
||
|
||
// --- 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)
|
||
},
|
||
};
|