Back to Mini Samurai
SOURCE / PINNED RELEASE

Made of little things.

Mini Samurai

Release
a7beb798adbc…
Author-recorded commit
4f97be1515b8…
License
LICENSE
Author’s source reference
nostr://npub12lm3rxsva8kfempyjkxupt09e02n3fezurkyadyqfejdc5e0r2kscqty6r/wss%3A%2F%2Fgit.napplet.soy%2F/n-3b28f0efc5b

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

src/debug-poses.ts
// Development-only pose viewer: ?poses=<clip>&t=<seconds>. Renders a samurai from three angles.
import * as THREE from 'three';
import { Rig } from './game/rig.js';
import * as A from './game/anims.js';
import { copyPose, emptyPose, type Pose } from './game/pose.js';
import { KATANA, itemById } from './game/items.js';

export function runPoseViewer(params: URLSearchParams): void {
  document.body.innerHTML = '';
  const renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true });
  renderer.setSize(1200, 420);
  renderer.shadowMap.enabled = true;
  document.body.append(renderer.domElement);
  const scene = new THREE.Scene();
  scene.background = new THREE.Color(0xd9cbb4);
  scene.add(new THREE.HemisphereLight(0xfff4e0, 0x6b5a48, 1.2));
  const sun = new THREE.DirectionalLight(0xffffff, 2);
  sun.position.set(2, 4, 3);
  sun.castShadow = true;
  scene.add(sun);
  const floor = new THREE.Mesh(new THREE.PlaneGeometry(10, 10), new THREE.MeshStandardMaterial({ color: 0xb08a5a }));
  floor.rotation.x = -Math.PI / 2;
  floor.receiveShadow = true;
  scene.add(floor);

  const name = params.get('poses') ?? 'stance';
  const t = Number(params.get('t') ?? '0');
  const item = params.get('item');
  const def = item ? itemById(item) : KATANA;
  const pose = emptyPose();
  const g: A.Grip = params.get('grip') === 'fists' ? 'fists' : def.twoHanded ? 'katana' : 'one';
  const set = g === 'fists' ? A.FIST_CLIPS : g === 'one' ? A.ONE_CLIPS : A.KATANA_CLIPS;
  const sample = (p: Pose): void => {
    const clips: Record<string, () => void> = {
      stance: () => A.locomotion(p, { phase: 0, speed: 0, crouch: 0, lean: 0, moveX: 0, moveZ: 1, time: t, grip: g, sprint: 0 }),
      walk: () => A.locomotion(p, { phase: t, speed: 1.5, crouch: 0, lean: 0, moveX: 0, moveZ: 1, time: t, grip: g, sprint: 0 }),
      run: () => A.locomotion(p, { phase: t, speed: 5, crouch: 0, lean: 0, moveX: 0, moveZ: 1, time: t, grip: g, sprint: 1 }),
      crouch: () => A.locomotion(p, { phase: t, speed: 0, crouch: 1, lean: 0, moveX: 0, moveZ: 1, time: t, grip: g, sprint: 0 }),
      air: () => A.airPose(p, Number(params.get('vy') ?? '4'), g, t),
      hang: () => A.hangPose(p, 1.0, 0.2, t, 0, 0),
      climb: () => A.climbPose(p, t, 1.0 - t * 1.0, 0.2 - t * 0.4),
      block: () => copyPose(p, A.blockPose(g)),
      slide: () => Object.assign(p, A.SLIDE),
      l1: () => set.light[0]!.sample(t, p),
      l2: () => set.light[1]!.sample(t, p),
      l3: () => set.light[2]!.sample(t, p),
      heavy: () => set.heavy.sample(t, p),
      dash: () => set.dash.sample(t, p),
      low: () => set.low.sample(t, p),
      airatk: () => set.air.sample(t, p),
      airland: () => set.airLand.sample(t, p),
      knock: () => A.KNOCKDOWN.sample(t, p),
      getup: () => A.GETUP(A.STANCE_KATANA).sample(t, p),
      pickup: () => A.PICKUP(A.STANCE_FISTS).sample(t, p),
      victory: () => A.VICTORY.sample(t, p),
      flinch: () => A.FLINCH(A.STANCE_KATANA, 1).sample(t, p),
    };
    (clips[name] ?? clips.stance!)();
  };

  const views = [
    { x: 0, z: 2.6, label: 'front' },
    { x: 2.6, z: 0, label: 'side' },
    { x: -1.6, z: -1.9, label: 'back' },
  ];
  const rigs = views.map(() => {
    const rig = new Rig({ armor: 0xc8372d, trim: 0xe3b23c, cloth: 0x2b2d42, ribbon: 0xf2efe6 }, scene);
    if (g !== 'fists') rig.setWeapon(def.build(), def.style === 'katana' ? -0.12 : 0.18);
    scene.add(rig.root);
    return rig;
  });
  sample(pose);
  rigs.forEach((rig, i) => {
    rig.root.position.set(i * 6 - 6, 0, 0);
    rig.apply(pose, 0, new THREE.Vector3());
    rig.resetSecondary();
    for (let k = 0; k < 30; k++) rig.apply(pose, 1 / 60, new THREE.Vector3());
  });
  const w = 400;
  renderer.setScissorTest(true);
  views.forEach((view, i) => {
    const cam = new THREE.PerspectiveCamera(35, 1 / 1.05, 0.05, 50);
    const cx = i * 6 - 6;
    cam.position.set(cx + view.x, 0.75, view.z);
    cam.lookAt(cx, 0.55, 0);
    renderer.setViewport(i * w, 0, w, 420);
    renderer.setScissor(i * w, 0, w, 420);
    renderer.render(scene, cam);
  });
  document.title = 'ready';
}