First human feedback: "ship movement feels sluggish". It was, and the cause was a bug rather
than a taste call.
`disc.damping` is applied every step AFTER accel, so the real cap on lateral speed was never
`maxSpeed` — it was accel/damping. At 78/6.5 that is 12 u/s in theory and a MEASURED 10.16,
which means the authored maxSpeed of 13 was unreachable and had been decorative since round 1.
A full tube crossing took 1.95 s. Against 12-20 u/s of forward flow that is 30+ units of canal
travelled while you cross the lumen once — so anything read late simply cannot be answered,
which is exactly what sluggish feels like from the cockpit.
accel 78 -> 155 (maxSpeed is now the ACTUAL cap: 155/6.5 = 23.8 > 19)
maxSpeed 13 -> 19 (crossing 1.95s -> 1.10s, 1.77x faster; ramp to top speed 0.12s)
damping 6.5 unchanged — it is what makes the ship STOP crisply, and precision was never
the complaint. Only the ceiling moved.
Knock-ons handled rather than left to rot:
- bank.gain 0.055 -> 0.04, so 19 u/s lands exactly at bank.max: full deflection still means
full roll and everything below it stays proportional. At the old gain the ship would have
been pinned at max roll almost permanently.
- arena thrust/maxSpeed 44/11 -> 90/16. Those were sized against the old disc; leaving them
would have made every room feel like treacle next to the corridor leading into it. Still
deliberately under the tube's flow, per the GDD's "slower, spatial".
Verified no tunnelling at the new speed: pinned at 19 u/s straight into the wall, the ship
reaches rho 10.51 against a wallRho of 11.38 and stays there — 0.32 u of travel per frame at
60 fps, well inside what collideWall resolves.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
160 lines
10 KiB
JavaScript
160 lines
10 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: {
|
||
// PLAYTEST FIX (round 3): "ship movement feels sluggish", and it measurably was.
|
||
// `damping` is applied every step AFTER accel, so the real cap is not `maxSpeed` at all —
|
||
// it is accel/damping. At 78/6.5 that made terminal 12 u/s in theory and a MEASURED 10.16,
|
||
// so the old maxSpeed 13 was unreachable and a full tube crossing took 1.95 s. Two seconds
|
||
// of lateral travel against 12-20 u/s of forward flow means you cannot answer anything you
|
||
// read late — which is exactly what "sluggish" feels like from the cockpit.
|
||
// Now: accel 155 with maxSpeed 19 makes maxSpeed the ACTUAL cap (155/6.5 = 23.8 > 19),
|
||
// reached in 0.12 s, and the crossing drops to ~1.0 s. Damping is untouched on purpose —
|
||
// it is what makes the ship stop crisply, and precision was never the complaint.
|
||
accel: 155, // u/s²
|
||
maxSpeed: 19, // u/s — a full tube crossing in ~1 s
|
||
damping: 6.5, // /s exponential drag => still 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: 90, // u/s² axial. Still softer than the disc: a room is read, not strafed
|
||
maxSpeed: 16, // u/s — under the esophagus's 12-20 flow on purpose (GDD: "slower"),
|
||
// but raised with the disc so a room does not feel like treacle
|
||
// next to the corridor that leads into it.
|
||
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.04, // rad of roll per u/s. Rescaled for the faster disc: 19 u/s now
|
||
// lands right at `max`, so full deflection = full roll and
|
||
// everything below it still reads proportionally.
|
||
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)
|
||
//
|
||
// --- the pH mechanic (L3) ---------------------------------------------------------
|
||
// On a level with an acid sea (`level.acid`), the biome's drain is scaled by WHETHER YOU
|
||
// ARE IN IT — world.acid.depthAt(pos) > 0 means submerged. This is the GDD's "pH as
|
||
// environmental damage; mucus shallows are safe lanes", and it makes L3's promise literal:
|
||
// position is the resource. Against the 8/s regen, stomach 2.2 x 5 = 11/s bleeds even at
|
||
// rest and is punishing mid-fight (regen is off for 2.5s after any hit); above the surface
|
||
// 2.2 x 0.15 = 0.33/s is effectively safe air. A level with no acid plane is untouched.
|
||
acidMult: 5.0,
|
||
surfaceMult: 0.15,
|
||
},
|
||
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)
|
||
},
|
||
};
|