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/havokManager.ts
// Port of "ParentScript 3 - havokManager".
import { Vector3 } from 'three';
import { HavokWorld, type HavokRigidBody, type HavokOptions, type StaticBody } from '../physics/havok.js';

/**
 * pRB.friction = 10 (car.ls). Against the scenery's 0.3 the original behaves
 * like the lower value: landings keep their speed as in the logs, while the
 * geometric mean (1.73) used before scrubbed off up to a third of it.
 */
export const CHASSIS_CONTACT_FRICTION = 10;

export class HavokManager {
  readonly havok: HavokWorld;
  private readonly forces: Array<[HavokRigidBody, Vector3]> = [];
  private readonly forcesOnPoint: Array<[HavokRigidBody, Vector3, Vector3]> = [];

  constructor(statics: StaticBody[], gravity: Vector3, scale: number, overrides: Partial<HavokOptions> = {}) {
    const options: HavokOptions = {
      scale,
      gravity,
      contactFriction: CHASSIS_CONTACT_FRICTION,
      forceModel: 'impulse',
      ...overrides,
    };
    // pHavok.Initialize(p3d); dragParameters = [0, 0]
    this.havok = new HavokWorld(options, statics);
    // pHavok.gravity = pHavok.gravity * 0.2 ; every static restitution = 0
    this.havok.gravity = this.havok.gravity.multiplyScalar(0.2);
  }

  update(tDifMs: number): void {
    let tDif = tDifMs / 1000;
    if (tDif > 0.015) tDif = 0.015;
    if (tDif < 0.003) tDif = 0.003;
    for (const [rb, force] of this.forces) rb.applyForce(force);
    // The Lingo stores [tRB, tPoint, tForce] but callers pass (rb, force, point);
    // the positional call rb.applyForceAtPoint(tForce[2], tForce[3]) is (force, point).
    for (const [rb, force, point] of this.forcesOnPoint) rb.applyForceAtPoint(force, point);
    this.havok.step(tDif * 2.5, 7);
    this.forces.length = 0;
    this.forcesOnPoint.length = 0;
  }

  removeBody(rb: HavokRigidBody): void {
    for (const queue of [this.forces, this.forcesOnPoint]) {
      for (let i = queue.length - 1; i >= 0; i--) if (queue[i][0] === rb) queue.splice(i, 1);
    }
    this.havok.removeBody(rb);
  }

  applyForceAtPoint(rb: HavokRigidBody, force: Vector3, point: Vector3): void {
    this.forcesOnPoint.push([rb, force.clone(), point.clone()]);
  }

  /** Drop the forces queued for the next step (test harness experiments). */
  clearQueuedForces(): void {
    this.forces.length = 0;
    this.forcesOnPoint.length = 0;
  }

  applyForce(rb: HavokRigidBody, force: Vector3): void {
    this.forces.push([rb, force.clone()]);
  }

  getSimTime(): number {
    return this.havok.simTime;
  }

  destroy(): void {
    this.havok.free();
  }
}