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.

tests/simulation.test.mjs
import test from 'node:test';
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import ts from 'typescript';
const source = await readFile(new URL('../src/simulation.ts', import.meta.url), 'utf8');
const code = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022 } }).outputText;
const { initialState, step, jump, shoot, celebrate, STAGE_RADIUS, DEFAULT_MOVE_SPEED } = await import(`data:text/javascript;base64,${Buffer.from(code).toString('base64')}`);

test('diagonal movement has the same speed and stays on stage', () => {
  const straight = initialState(), diagonal = initialState();
  for (let i=0; i<40; i++) { step(straight, 1, 0, 1/60, DEFAULT_MOVE_SPEED); step(diagonal, 1, 1, 1/60, DEFAULT_MOVE_SPEED); }
  assert.ok(Math.abs(Math.hypot(diagonal.x, diagonal.z)-straight.x) < .001);
  for (let i=0; i<1000; i++) step(diagonal, 1, 1, 1/60, DEFAULT_MOVE_SPEED);
  assert.ok(Math.hypot(diagonal.x, diagonal.z) <= STAGE_RADIUS + .001);
});
test('jump leaves ground once and lands; no midair jump', () => {
  const s=initialState(); assert.equal(jump(s),true); assert.equal(jump(s),false);
  let peak=0, landings=0;
  for(let i=0;i<100;i++) { if(step(s,0,0,1/60,DEFAULT_MOVE_SPEED)) landings++; peak=Math.max(peak,s.y); }
  assert.ok(peak>1); assert.equal(landings,1); assert.equal(s.y,0); assert.equal(jump(s),true);
});
test('shotgun cooldown and celebration gate incompatible actions', () => {
  const s=initialState(); assert.equal(shoot(s),true); assert.equal(shoot(s),false);
  for(let i=0;i<41;i++) step(s,0,0,1/60,DEFAULT_MOVE_SPEED);
  assert.equal(shoot(s),true); assert.equal(celebrate(s),true); assert.equal(shoot(s),false); assert.equal(jump(s),false);
  for(let i=0;i<180;i++) step(s,0,0,1/60,DEFAULT_MOVE_SPEED);
  assert.equal(shoot(s),true); assert.equal(jump(s),true);
});
test('movement and jumping remain close at 30 and 120 fps', () => {
  const runs=[30,120].map(fps=>{const s=initialState();jump(s);for(let i=0;i<fps;i++)step(s,1,0,1/fps,DEFAULT_MOVE_SPEED);return s;});
  assert.ok(Math.abs(runs[0].x-runs[1].x)<.08); assert.equal(runs[0].y,0); assert.equal(runs[1].y,0);
});

test('run accelerates quickly, stops cleanly, and turns responsively', () => {
  const s=initialState();
  for(let i=0;i<12;i++)step(s,1,0,1/60,DEFAULT_MOVE_SPEED);
  assert.ok(s.vx>5.8); assert.ok(Math.abs(s.heading-Math.PI/2)<.04);
  for(let i=0;i<10;i++)step(s,0,0,1/60,DEFAULT_MOVE_SPEED);
  assert.ok(Math.hypot(s.vx,s.vz)<.1);
});
test('floating hands inherit shotgun recoil and the pump motion', async () => {
  const model=JSON.parse(await readFile(new URL('../src/assets/soybert-rig.json',import.meta.url),'utf8'));
  const parents=Object.fromEntries(model.bones.map(b=>[b.name,b.parent]));
  assert.equal(parents.hand_grip,'shotgun'); assert.equal(parents.hand_pump,'pump'); assert.equal(parents.pump,'shotgun');
  assert.ok(!model.bones.some(b=>b.name.startsWith('arm_')));
});