Back to Outbreak: Soycoon City
SOURCE / PINNED RELEASE

Made of little things.

Outbreak: Soycoon City

Release
db7750b1d009…
Author-recorded commit
3ee3f5fd27a6…
License
LICENSE
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-b87c04571a9

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

tests/polish.test.mjs
import test from 'node:test';
import assert from 'node:assert/strict';
import {readFile,stat} from 'node:fs/promises';
import {createHash} from 'node:crypto';
import {polishModules} from '../scripts/polish-modules.mjs';
const w=await polishModules(),{Game,createStage,CharacterAsset,Box3,Vector3}=w;
const idle={forward:0,turn:0,aim:false,run:false};
const fixture=()=>{const stage=createStage(),r=stage.rooms[0];r.props=[];r.zombies=[{x:0,z:-1},{x:3,z:-3}];r.spawn={x:0,z:0};r.herbs=[{id:'medicine',kind:'green',x:0,z:.4}];const g=new Game(stage);g.paused=false;return g;};
test('concave polygon boundaries, hysteresis padding and invalid polygons',()=>{
 const points=[{x:-2,z:-2},{x:2,z:-2},{x:2,z:0},{x:0,z:0},{x:0,z:2},{x:-2,z:2}],zone={points};
 assert.equal(w.polygonIssue(points),undefined);assert(w.inZone({x:-1,z:1},zone));assert(!w.inZone({x:1,z:1},zone));assert(w.inZone({x:0,z:1},zone));assert(w.inZone({x:.05,z:1},zone,.07));
 assert.match(w.polygonIssue([{x:0,z:0},{x:2,z:2},{x:0,z:2},{x:2,z:0}]),/cross/);
 for(const pts of [[],[{x:0,z:0},{x:0,z:0},{x:1,z:0}],[{x:NaN,z:0},{x:1,z:1},{x:0,z:2}]])assert(w.polygonIssue(pts));
 const s=createStage();s.rooms[0].cameras[0].zone=zone;assert.doesNotThrow(()=>w.parseStage(JSON.stringify(s)));s.rooms[0].cameras[0].zone.points.push(points[0]);assert.throws(()=>w.parseStage(JSON.stringify(s)),/Invalid camera/);
 const art=createStage();for(let i=art.assets.length;i<96;i++)art.assets.push({id:`extra-${i}`,sha256:'a'.repeat(64),size:1,mime:'image/png',path:`src/art-${i}.png`});assert.equal(w.parseStage(JSON.stringify(art)).assets.length,96);art.assets.push({...art.assets[0],id:'extra-over-limit'});assert.throws(()=>w.parseStage(JSON.stringify(art)),/96 linked/);
 const vertical={...createStage().rooms[0].cameras[0],position:[0,8,0],target:[0,0,0],fov:55};assert(w.cameraContains(vertical,{x:0,z:0}));assert(!w.cameraContains(vertical,{x:30,z:30}));
});
test('all 14 door arrivals and approaches frame the door and actor; floor recovery covers every clear cell',()=>{
 const stage=createStage();let count=0;
 for(const room of stage.rooms){
  for(const door of room.doors){count++;for(const inset of [.35,.7,1.1,1.5]){const arrival=w.doorArrival(door),p={x:door.x+(arrival.x-door.x)*inset/.7,z:door.z+(arrival.z-door.z)*inset/.7};if(w.blocked(room,p))continue;const c=w.cameraAt(room,p,room.cameras[0].id);assert(w.doorwayVisible(room,c,door),`${room.id}/${door.id} inset ${inset}: ${c.id}`);assert(w.cameraContains(c,p));}}
  for(let x=-room.w/2+.3;x<room.w/2-.29;x+=.5)for(let z=-room.d/2+.3;z<room.d/2-.29;z+=.5){const p={x,z};if(!w.blocked(room,p))assert(w.cameraContains(w.cameraAt(room,p),p),`${room.id} ${x},${z}`);}
  assert.deepEqual(w.cameraWarnings(room),[]);
 }assert.equal(count,14);
});
test('critical blast requires upward aim, close range, facing and unobstructed line; only accepted shots emit effects',()=>{
 const g=fixture();g.zombies[0].hp=8;g.shoot(true);assert.equal(g.zombies[0].hp,0);assert(g.zombies[0].headGone);assert.equal(g.bursts.length,1);assert(g.drainSounds().some(s=>s.id==='zombie_head_burst'));g.shoot(true);assert.equal(g.bursts.length,1);assert.equal(g.shotSerial,1);
 for(const change of [g=>g.zombies[0].z=-2.2,g=>g.player.yaw=0,g=>g.room.props.push({id:'cover',kind:'crate',x:0,z:-.5,w:.4,d:.2,h:.5})]){const f=fixture();f.zombies[0].hp=8;change(f);f.shoot(true);assert(!f.zombies[0].headGone);assert.equal(f.bursts.length,0);}
 const normal=fixture();normal.shoot();assert(!normal.zombies[0].headGone);assert.equal(normal.zombies[0].hp,0);
 const frozen=g.time;g.paused=true;g.update(.05,idle);assert.equal(g.time,frozen);assert.equal(g.bursts.length,1);g.paused=false;for(let i=0;i<40;i++)g.update(.05,idle);assert.equal(g.bursts.length,0);
});
test('herbs heal 35, remain when full, respect pause and room, and reset without mutating level JSON',()=>{
 const g=fixture(),initial=JSON.stringify(g.stage);assert.equal(g.interact(),false);assert(!g.herbs[0].collected);g.player.hp=80;g.paused=true;assert.equal(g.interact(),false);g.paused=false;assert(g.interact());assert.equal(g.player.hp,100);assert(g.herbs[0].collected);assert.equal(JSON.stringify(g.stage),initial);
 const other=fixture();other.player.hp=40;other.herbs[0].room='elsewhere';assert.equal(other.interact(),false);other.herbs[0].room=other.room.id;assert(other.interact());assert.equal(other.player.hp,75);assert(!new Game(other.stage).herbs[0].collected);
 const bad=createStage();bad.rooms[0].herbs[0].x=.9;bad.rooms[0].herbs[0].z=0;assert.throws(()=>w.parseStage(JSON.stringify(bad)),/herb.*inside/);
});
test('checked-in Blender sources and GLBs agree; real skinning and clips animate limbs, recoil and death',async()=>{
 const manifest=JSON.parse(await readFile('models/manifest.json','utf8'));
 for(const item of manifest.models){const bytes=await readFile(item.path);assert.equal(bytes.length,item.size);assert.equal(createHash('sha256').update(bytes).digest('hex'),item.sha256);assert((await stat(item.source)).size>1000);
  const asset=new CharacterAsset(bytes),actor=asset.instantiate();assert.deepEqual([...actor.actions.keys()].sort(),[...item.clips].sort());let meshes=0;actor.root.traverse(o=>{if(o instanceof w.SkinnedMesh){meshes++;assert.equal(o.skeleton.bones.length,item.bones.length);assert(o.geometry.getAttribute('skinWeight'));}});assert(meshes>=2);
  actor.root.updateMatrixWorld(true);const bounds=new Box3().setFromObject(actor.root);assert(bounds.max.y-bounds.min.y>1.7);assert(bounds.max.x-bounds.min.x<1.15);
  const leg=actor.root.getObjectByName('Thigh_L');actor.play('Walk');actor.update(.3);const angle=leg.quaternion.clone();actor.update(.7);assert(angle.angleTo(leg.quaternion)>.1);
  if(item.id==='soybert'){actor.play('AimUp');actor.update(.1);const muzzle=actor.root.getObjectByName('Muzzle'),weapon=actor.root.getObjectByName('Weapon');actor.root.updateMatrixWorld(true);const start=muzzle.getWorldPosition(new Vector3());actor.play('ShootUp',true,1);actor.update(.10);const kick=weapon.quaternion.clone();actor.root.updateMatrixWorld(true);assert(start.distanceTo(muzzle.getWorldPosition(new Vector3()))>.02);actor.update(.3);assert(kick.angleTo(weapon.quaternion)>.05);}
  actor.play('Death',true,1);actor.update(2);assert(actor.actions.get('Death').clampWhenFinished);assert(Math.abs(actor.root.getObjectByName('Root').quaternion.x)>.65);const corpse=leg.quaternion.clone();actor.update(1);assert(corpse.angleTo(leg.quaternion)<.001);actor.dispose();asset.dispose();
 }
 assert.throws(()=>w.decodeEmbeddedModel('https://example.com/model.glb'),/embedded/);assert.throws(()=>w.readCharacter(new Uint8Array(28)),/Invalid GLB/);
});