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.
// 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;
}
}
