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/game/types.ts
import type * as THREE from 'three';

/**
 * World scale: 1 unit ≈ 10 cm. A mini samurai is about 1 unit tall, so a kitchen
 * table (75 cm) is 7.5 units high. +Y is up. Rooms sit on the floor at y = 0.
 */

export type RoomId = 'bathroom' | 'bedroom' | 'kitchen' | 'living' | 'dining' | 'garage' | 'house';

/** Axis-aligned solid box. Fighters stand on tops, collide with sides and can hang from top edges. */
export interface Solid {
  minX: number;
  minY: number;
  minZ: number;
  maxX: number;
  maxY: number;
  maxZ: number;
  /** False for walls and other surfaces whose top edges should never be grabbed. Default true. */
  grab?: boolean;
  /** Round or slippery top (a ball): nobody can stand on it, they slide off. */
  slide?: boolean;
  /** Optional label for debugging and AI ("bed", "chair-seat", ...). */
  name?: string;
  /** Moves every frame (the toy train): kept out of the AI's map and item placement. */
  moving?: boolean;
  /** A cylinder standing in its (square) box: collisions, edges and ledges follow the circle. */
  round?: boolean;
}

/** A walkable floor rectangle of a multi-room map (a room, the hallway or a doorway). */
export interface Area {
  minX: number;
  maxX: number;
  minZ: number;
  maxZ: number;
  /** Which room's household items belong here (none for hallways and doorways). */
  room?: RoomId;
  /** Doorway: walkable, but too small to place things in. */
  doorway?: boolean;
}

/** Loop of toy-train track on the floor: square corners in order, each rounded with radius r. */
export interface TrackDef {
  points: [number, number][];
  r: number;
}

/** A moving machine in a room (built and run by machines.ts). Positions are in room space. */
export type MachineDef =
  /** Ceiling fan: blades turning at height y (blade bottom), reaching out to radius r. */
  | { kind: 'fan'; x: number; y: number; z: number; r: number; speed: number }
  /** Record player: a platter of radius r whose top is at y, turning at speed (radians per second). */
  | { kind: 'turntable'; x: number; y: number; z: number; r: number; speed: number }
  /** Toaster: two slots along yaw whose top is at y; pops every period seconds. */
  | { kind: 'toaster'; x: number; y: number; z: number; yaw: number; period: number }
  /**
   * Pendulum hanging from a pivot at (x, y, z), swinging across yaw (its front faces yaw):
   * the grandfather clock's bob.
   */
  | { kind: 'pendulum'; style: 'clock'; x: number; y: number; z: number; yaw: number; length: number; swing: number; period: number };

/** Something round that rolls when it's hit or pushed (a ball, a tin can, a cotton reel). */
export interface RollerDef {
  kind: 'ball' | 'can' | 'reel';
  x: number;
  y: number;
  z: number;
}

/** A basin that slowly fills with water (the bath): its floor rectangle and how high the water gets. */
export interface PoolDef {
  minX: number;
  maxX: number;
  minZ: number;
  maxZ: number;
  /** Height of the basin floor, and the lowest and highest water levels. */
  floor: number;
  low: number;
  high: number;
  /** Seconds to fill from low to high (it then drains and fills again). */
  fill: number;
  /** Where the tap pours in. */
  tap: { x: number; y: number; z: number };
}

/** A dripping tap: drips land at (x, z) on the floor and spread into a slippery puddle. */
export interface DripDef {
  x: number;
  y: number;
  z: number;
  /** Largest the puddle grows. */
  r: number;
}

export interface SpawnPoint {
  x: number;
  y: number;
  z: number;
  /** Facing angle in radians around +Y (0 faces +Z). */
  facing: number;
}

export interface RoomData {
  id: RoomId;
  name: string;
  /** Root object holding all room meshes; added to the scene by the caller. */
  root: THREE.Group;
  /** Every solid a fighter can collide with, including floor and walls. */
  solids: Solid[];
  /** Exactly four fighter spawns: [team A #1, team B #1, team A #2, team B #2]. */
  spawns: SpawnPoint[];
  /** Places where household items appear at round start (6–10 of them; more in the house). */
  itemSpawns: { x: number; y: number; z: number; kinds?: string[] }[];
  /** Item ids (from items.ts) that suit this room. */
  itemKinds: string[];
  /** Playable floor rectangle inside the walls. */
  bounds: { minX: number; maxX: number; minZ: number; maxZ: number };
  /** Tallest walkable surface height, for camera framing. */
  ceiling: number;
  /** Walls (meshes) that the camera may hide when they sit between it and the fight. */
  cutaway: { mesh: THREE.Object3D; normal: THREE.Vector3; plane?: WallPlane }[];
  /** Multi-room maps: where fighters may be (anywhere in `bounds` otherwise). */
  areas?: Area[];
  /** Toy-train loop, if this map has one. */
  track?: TrackDef;
  /** Moving machines (fan, record player, toaster, pendulums). */
  machines?: MachineDef[];
  rollers?: RollerDef[];
  /** Wooden crates holding a toy car each (the garage). */
  crates?: { x: number; y: number; z: number }[];
  pools?: PoolDef[];
  drips?: DripDef[];
  /** Background colour / fog colour for the room. */
  sky: number;
  /** Sunlight direction (from the light toward the scene), normalised. */
  sunDir: THREE.Vector3;
}

/**
 * Where a wall stands, for maps with many rooms: the wall is hidden while it stands
 * between the camera and what it looks at. `axis` is the axis the wall faces along
 * (its plane is axis = at); it runs from u0 to u1 along the other axis.
 */
export interface WallPlane {
  axis: 'x' | 'z';
  at: number;
  u0: number;
  u1: number;
  /** How far past its ends a sight line may cross and still hide it (7 by default). */
  pad?: number;
}

export type WeaponStyle = 'katana' | 'slash' | 'stab' | 'blunt';

export interface WeaponDef {
  id: string;
  name: string;
  style: WeaponStyle;
  /** Distance from the grip point (origin) to the striking tip, in units. */
  length: number;
  /** Damage multiplier relative to the katana (1). */
  damage: number;
  /** Attack speed multiplier relative to the katana (1); heavier items are below 1. */
  speed: number;
  /** Knockback multiplier. */
  knockback: number;
  /** Hits before it breaks. The katana never breaks (Infinity). */
  durability: number;
  /** Held with both hands (long or heavy items). */
  twoHanded: boolean;
  /** Two sticks on a cord (nunchaku): the free stick (mesh named "flail") swings loose and does the hitting. */
  flail?: boolean;
  /**
   * Builds the low-poly mesh. Convention: origin = where the fist grips,
   * the striking part extends along +Y to y = length, a short handle end may
   * extend to negative Y. Flat blades are thin along X with the cutting edge facing +Z.
   */
  build(): THREE.Object3D;
}

/** A household object that is lifted over the head and thrown (cups, bottles, soap, a big wrench...). */
export interface ThrowDef {
  id: string;
  name: string;
  /** Damage on a direct hit (fighters have 100 health; a katana slash does about 7). */
  damage: number;
  /** Knockback speed on hit; 5 or more knocks the target down. */
  knock: number;
  /** 0.5 (feather-light) .. 1.5 (very heavy): slows the carrier and the throw. */
  weight: number;
  /** Shatters on impact (glass, ceramic, eggs) instead of bouncing and staying in the room. */
  breaks: boolean;
  /** A cube (a die) that comes to rest on whichever face it lands on, instead of standing upright. */
  anyFace?: boolean;
  /** Round (a marble): keeps rolling along the floor after it lands. */
  rolls?: boolean;
  /** Flicked rather than thrown (a rubber band): this fast, in a nearly straight line. */
  flick?: number;
  /** Weapons that spill out when it breaks (a crayon box). */
  burst?: string[];
  /** Leaves a slippery puddle this big where it bursts (a water balloon). */
  splash?: number;
  /** A pick-up that does something when grabbed instead of being carried. */
  power?: 'sugar' | 'thimble' | 'button';
  /** Height and horizontal radius of the object in units (1 unit ≈ 10 cm). */
  height: number;
  radius: number;
  /** Colours used for the shards / splash when it breaks. */
  chips: number[];
  /**
   * Builds the low-poly mesh. Convention: origin at the centre of the object's
   * BOTTOM face, standing upright along +Y up to y = height, footprint within radius.
   */
  build(): import('three').Object3D;
}