Engine
@shipshitgames/engine is the shared Three.js game spine.
Use it for gameplay primitives that should not be rewritten in every game:
- world bounds
- data-driven arena maps
- render lifecycle and map lighting
- camera rigs
- input seams
- steering and agents
- spawn helpers
- HUD snapshots
- generic FX, projectile, and pickup lifecycles
Boundary rule
Gameplay is imperative and Three.js-centered. React is for shells, menus, HUD, and overlays.
The engine extraction boundary is tracked in
packages/engine/ENGINE-EXTRACTION-BOUNDARY.md. That spec separates reusable
engine core from game-owned Scourge Survivors content before runtime systems move
into the package.
Ownership and Deadrot consumption
packages/engine in this repo is the canonical source for
@shipshitgames/engine. Deadrot games should depend on the package release for
CI and release builds:
{
"dependencies": {
"@shipshitgames/engine": "^0.1.1",
"three": "^0.184.0"
}
}When testing unpublished engine changes locally, link the package from this repo instead of editing a Deadrot-owned copy:
cd ../shipshitgames/packages/engine
bun link
cd ../../../deadrotcom
bun link @shipshitgames/engine
bun installThe duplicate deadrot.com/packages/engine package is a temporary compatibility
copy until the Deadrot repo removes it or replaces it with a shim. Do not add
new engine features there, do not rename the package to @deadrot/engine, and
do not create a separate engine project board for this scope.
The full ownership contract is tracked in
packages/engine/CANONICAL-ENGINE.md.
Asset manifest schema
The engine package intentionally exports the shared game asset manifest schema:
import manifestSchema from "@shipshitgames/engine/assets-manifest.schema.json";Studio tooling validates each game’s src/assets/assets.json with this schema.
The schema belongs with the reusable engine contract; Deadrot-specific asset
files and generated source history still belong in ../deadrotcom/packages/assets.
Import examples
import { RectBounds, makeBounds } from "@shipshitgames/engine";
import { ArenaSystem, HudSystem, ProjectilesSystem } from "@shipshitgames/engine";
import { firstPersonPointerLock } from "@shipshitgames/engine";World bounds
Use bounds for clamp, cull, and spawn decisions instead of scattering global numbers through a game loop.
const bounds = RectBounds.square(40);
bounds.clampXZ(position, 1.5);Arena maps
Use ArenaSystem to interpret serializable map bounds, obstacles, themes, and
lights consistently across games.
const arena = new ArenaSystem({
id: "demo-arena",
bounds: { kind: "square", half: 40 },
obstacles: [{ kind: "rect", id: "crate-a", x: 3, z: -2, width: 2, depth: 2 }],
});
arena.isBlockedXZ(3, -2);Camera seam
Player systems read body and facing from the rig. The camera is render-only.
const rig = firstPersonPointerLock(camera, body);HUD and transients
Games supply typed state and content tables. The engine owns fan-out and lifecycle bookkeeping.
const hud = new HudSystem({ health: 100, ammo: 12 });
const projectiles = new ProjectilesSystem({
bullet: { type: "bullet", speed: 20, radius: 0.1, ttl: 1, damage: 4 },
});Check
cd packages/engine
bun run test
bun run typecheck