Back to Pursuit PIT
SOURCE / PINNED RELEASE

Made of little things.

Pursuit PIT

Release
565f4981bdc3…
Author-recorded commit
197afdbed44b…
License
LICENSE
Author’s source reference
nostr://npub12lm3rxsva8kfempyjkxupt09e02n3fezurkyadyqfejdc5e0r2kscqty6r/wss%3A%2F%2Fgit.napplet.soy%2F/n-951414e7fd7

Archive hash verified: 3b0485589942fd5c…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

src/game/engine.ts
// A 6-speed automatic for the patrol car: turns road speed and throttle into engine rpm.
// The engine sound and the tachometer both read from it.

const RPM_PER_MPS = [300, 330, 205, 145, 112, 90, 75]; // index 0 is reverse; 6th tops out at 160 mph
const IDLE = 800;
const REDLINE = 6200;

export class Gearbox {
  gear = 1;
  rpm = IDLE;
  /** 0..1 engine load after shift cuts, for the sound. */
  load = 0;
  private shiftCut = 0;

  update(speed: number, throttle: number, dt: number): void {
    const v = Math.abs(speed);
    if (speed < -0.5) this.gear = 0;
    else if (this.gear === 0) this.gear = 1;
    if (this.gear > 0) {
      const at = (g: number) => IDLE + v * RPM_PER_MPS[g];
      if (this.gear < 6 && at(this.gear) > (throttle > 0.5 ? 5800 : 3200)) {
        this.gear++;
        this.shiftCut = 0.22;
      } else if (this.gear > 1 && at(this.gear) < (throttle > 0.5 ? 2600 : 1500)) {
        this.gear--;
        this.shiftCut = 0.12;
      }
    }
    // Below ~2 m/s the torque converter slips, so throttle revs the engine freely.
    const slip = Math.max(0, 1 - v / 4) * Math.max(0, throttle) * 2600;
    const target = Math.min(REDLINE, IDLE + v * RPM_PER_MPS[this.gear] + slip);
    this.shiftCut = Math.max(0, this.shiftCut - dt);
    const dip = this.shiftCut > 0 ? 0.9 : 1;
    this.rpm += (target * dip - this.rpm) * Math.min(1, dt * (this.shiftCut > 0 ? 14 : 7));
    const wantLoad = this.shiftCut > 0 ? 0.1 : Math.max(0, throttle);
    this.load += (wantLoad - this.load) * Math.min(1, dt * 10);
  }
}