Back to Soybert 3D
SOURCE / PINNED RELEASE

Made of little things.

Soybert 3D

Release
4bf80e758af1…
Author-recorded commit
298fa2d0e8b1…
License
LICENSE
Author’s source reference
nostr://npub1n8ga89w8h6tvwamxusfyzexw8gjy84yxu9rxgnmk955cxtml4ujswzxydd/wss%3A%2F%2Fgit.napplet.soy%2F/n-9021e1b6a96

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

src/simulation.ts
export const STAGE_RADIUS = 5.6;
export const DEFAULT_MOVE_SPEED = 6.2;
export type ActorState = { x: number; z: number; y: number; vy: number; vx: number; vz: number; heading: number; celebration: number; recoil: number };
export function initialState(): ActorState { return { x: 0, z: 0, y: 0, vy: 0, vx: 0, vz: 0, heading: 0, celebration: 0, recoil: 10 }; }
export function jump(state: ActorState): boolean {
  if (state.y > .001 || state.vy !== 0 || state.celebration > 0) return false;
  state.vy = 7.8; return true;
}
export function shoot(state: ActorState): boolean {
  if (state.recoil < .65 || state.celebration > 0) return false;
  state.recoil = 0; return true;
}
export function celebrate(state: ActorState): boolean {
  if (state.y > .001 || state.celebration > 0) return false;
  state.celebration = 2.8; return true;
}
export function step(state: ActorState, dx: number, dz: number, dt: number, speed: number): boolean {
  dt = Math.min(.05, Math.max(0, dt));
  state.recoil += dt; state.celebration = Math.max(0, state.celebration - dt);
  const length = Math.hypot(dx, dz);
  if (length > 1) { dx /= length; dz /= length; }
  if (state.celebration > 0) { dx = 0; dz = 0; }
  const ease = 1 - Math.exp(-(length > .01 ? 22 : 28) * dt);
  state.vx += (dx * speed - state.vx) * ease; state.vz += (dz * speed - state.vz) * ease;
  state.x += state.vx * dt; state.z += state.vz * dt;
  const distance = Math.hypot(state.x, state.z);
  if (distance > STAGE_RADIUS) { state.x *= STAGE_RADIUS / distance; state.z *= STAGE_RADIUS / distance; }
  if (Math.hypot(dx, dz) > .05) {
    const target = Math.atan2(dx, dz);
    const delta = Math.atan2(Math.sin(target - state.heading), Math.cos(target - state.heading));
    state.heading += delta * (1 - Math.exp(-20 * dt));
  }
  const wasAirborne = state.y > 0;
  state.vy -= 22 * dt; state.y += state.vy * dt;
  if (state.y <= 0) { state.y = 0; state.vy = 0; }
  return wasAirborne && state.y === 0;
}