Back to Supersonic RC Revive
SOURCE / PINNED RELEASE

Made of little things.

Supersonic RC Revive

Release
1ba42f1ca1d6…
Author-recorded commit
baecad10b1cd…
License
LICENSE
Author’s source reference
nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.napplet.soy%2F/n-143146b0d6f

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

src/game/lingo/car.ts
// Port of "ParentScript 4 - car": one Havok rigid body ("car", mass 3, convex)
// carried by four raycast wheels.
import { Matrix4, Quaternion, Vector3 } from 'three';
import type { Input } from '../input.js';
import type { CollisionInfo, HavokRigidBody } from '../physics/havok.js';
import type { RayWorld } from '../physics/rayWorld.js';
import type { HavokManager } from './havokManager.js';
import { Wheel, type WheelCar } from './wheel.js';

export const CAR_START_POSITION = new Vector3(3790.17, -5696.19, 338.6598);

/** Where the car starts: the original's hard-coded spot, or a level's own start marker. */
export interface CarStart {
  position: Vector3;
  quaternion?: Quaternion;
}
const LIFT_BODY = 2;

export interface CarEvents {
  /** sound(3) crash one-shot: true when the car is upright (zAxis.z > 0.5). */
  crash?(upright: boolean): void;
}

export class Car implements WheelCar {
  readonly rb: HavokRigidBody;
  readonly wheels: Wheel[];
  pSkidding = 0;
  pReversing = 0;
  pStopped = 0;
  events: CarEvents = {};
  /** gGameTypeManager.getTimer().stop() */
  onStopDriving: (() => void) | null = null;
  /** pCarSound (carsound.update runs before the wheels each chunk). */
  sound: { update(): void; stopDriving(): void } | null = null;

  constructor(
    private readonly havokManager: HavokManager,
    rays: RayWorld,
    input: Input,
    hull: Float32Array,
    hullNodeMatrix: Matrix4,
    start: CarStart = { position: CAR_START_POSITION },
    name = 'car',
  ) {
    const havok = havokManager.havok;
    this.rb = havok.makeMovableRigidBody(name, 3, hull, hullNodeMatrix);
    this.rb.position = start.position.clone();
    if (start.quaternion) this.rb.quaternion = start.quaternion.clone();
    this.rb.shiftCenterOfMass(new Vector3(0, 0, -11.5 - LIFT_BODY));
    this.rb.friction = 10;
    this.rb.restitution = 0.01;
    const lift = LIFT_BODY;
    this.wheels = [
      new Wheel(this, havokManager, rays, input, new Vector3(-9.5, 17, -5 - lift), 1, 1),
      new Wheel(this, havokManager, rays, input, new Vector3(9.5, 17, -5 - lift), 1, 2),
      new Wheel(this, havokManager, rays, input, new Vector3(-9.5, -17, -5 - lift), 0, 3),
      new Wheel(this, havokManager, rays, input, new Vector3(9.5, -17, -5 - lift), 0, 4),
    ];
    // registerInterest("car", #all, 0, 0, #collision, me)
    havok.registerInterest(this.rb, 0, 0, (info) => this.collision(info));
  }

  modelTransform(): Matrix4 {
    return this.rb.transform;
  }

  gravityLength(): number {
    return this.havokManager.havok.gravity.length();
  }

  /** Visual car ("car2", a clone of holypolycrap) relative to the car model. */
  static visualOffset(): Matrix4 {
    return new Matrix4()
      .makeTranslation(0, 0, 4 - LIFT_BODY)
      .multiply(new Matrix4().makeRotationZ(Math.PI))
      .multiply(new Matrix4().makeScale(0.17, 0.17, 0.17));
  }

  collision(info: CollisionInfo): void {
    const tMax = 4;
    const l = this.rb.angularMomentum;
    if (l.length() > tMax) this.rb.angularMomentum = l.normalize().multiplyScalar(tMax);
    const w = this.rb.angularVelocity;
    if (w.length() > tMax) this.rb.angularVelocity = w.normalize().multiplyScalar(tMax);
    if (info.normalRelativeVelocity > 1) {
      const zAxis = new Vector3().setFromMatrixColumn(this.modelTransform(), 2);
      this.events.crash?.(zAxis.z > 0.5);
    }
  }

  /** car.update(tDif): carsound update, then every wheel. */
  update(): void {
    this.sound?.update();
    for (const wheel of this.wheels) wheel.update();
  }

  /** car.stopDriving: sound, then timer (via the game type), then wheels. */
  stopDriving(): void {
    this.sound?.stopDriving();
    this.onStopDriving?.();
    for (const wheel of this.wheels) wheel.stopDriving();
    this.pStopped = 1;
  }
}