Browser reference stack for PSX-era third-person adventure: fixed cameras, inventory puzzles, Box3D physics, host-authoritative P2P co-op, and a modular character harness. Ships the Ashgrove Precinct Level 1 investigation demo with a full cast and nine linked rooms.
100 lines
3.6 KiB
TypeScript
100 lines
3.6 KiB
TypeScript
import type { RoomDef } from "@psx/shared";
|
|
import { door, floor, prop, STONE, wall, WALL_HALF, WALL_THICKNESS } from "./helpers.js";
|
|
|
|
const METAL_TABLE = 0x5a6068;
|
|
|
|
/**
|
|
* Interview room — Nora Voss (civilian-f).
|
|
*
|
|
* Two-way glass, metal table, classic interrogation staging. Free access from
|
|
* the squad hall so the player can start gathering testimony immediately.
|
|
*/
|
|
export const INTERVIEW: RoomDef = {
|
|
id: "precinct-interview",
|
|
displayName: "Ashgrove Precinct — Interview Room",
|
|
fog: { color: 0x0a0c10, near: 2.5, far: 12 },
|
|
ambientLight: { color: 0x384048, intensity: 0.9 },
|
|
lights: [
|
|
{
|
|
kind: "point",
|
|
color: 0xc8d0e0,
|
|
intensity: 24,
|
|
position: { x: 0, y: 2.5, z: 0 },
|
|
distance: 11,
|
|
},
|
|
],
|
|
geometry: [
|
|
floor("floor", { x: 0, y: 0, z: 0 }, 3.2, 3.2),
|
|
wall("wall-n", { x: 0, y: WALL_HALF, z: -3.35 }, { x: 3.4, y: WALL_HALF, z: WALL_THICKNESS }, STONE),
|
|
wall("wall-s-left", { x: -1.9, y: WALL_HALF, z: 3.35 }, { x: 1.1, y: WALL_HALF, z: WALL_THICKNESS }),
|
|
wall("wall-s-right", { x: 1.9, y: WALL_HALF, z: 3.35 }, { x: 1.1, y: WALL_HALF, z: WALL_THICKNESS }),
|
|
wall("wall-e", { x: 3.35, y: WALL_HALF, z: 0 }, { x: WALL_THICKNESS, y: WALL_HALF, z: 3.2 }, STONE),
|
|
wall("wall-w", { x: -3.35, y: WALL_HALF, z: 0 }, { x: WALL_THICKNESS, y: WALL_HALF, z: 3.2 }, STONE),
|
|
prop("table", { x: 0, y: 0.4, z: -0.4 }, { x: 1.6, y: 0.8, z: 0.9 }, METAL_TABLE),
|
|
prop("chair-suspect", { x: 0, y: 0.35, z: -1.5 }, { x: 0.45, y: 0.7, z: 0.45 }, 0x3a3a40),
|
|
prop("chair-cop", { x: 0, y: 0.35, z: 0.7 }, { x: 0.45, y: 0.7, z: 0.45 }, 0x3a3a40),
|
|
prop("mirror-slab", { x: -3.15, y: 1.3, z: 0 }, { x: 0.08, y: 1.4, z: 2.2 }, 0x6a7888),
|
|
],
|
|
cameraZones: [
|
|
{
|
|
id: "interview",
|
|
trigger: { center: { x: 0, y: 1.5, z: 0 }, halfExtents: { x: 3.2, y: 2, z: 3.2 } },
|
|
camera: {
|
|
position: { x: 2.6, y: 2.8, z: 2.8 },
|
|
lookAt: { x: 0, y: 1.0, z: -0.6 },
|
|
fov: 58,
|
|
trackPlayer: true,
|
|
trackRadius: 1.2,
|
|
},
|
|
blend: { mode: "cut" },
|
|
priority: 0,
|
|
},
|
|
],
|
|
interactables: [
|
|
{
|
|
id: "two-way-glass",
|
|
label: "Two-Way Glass",
|
|
position: { x: -3.0, y: 1.3, z: 0 },
|
|
halfExtents: { x: 1.1, y: 1.3, z: 1.4 },
|
|
action: {
|
|
kind: "examine",
|
|
text:
|
|
"Your reflection sits where the observer should. Someone watched this room last night — a coffee ring on the sill, still sticky.",
|
|
},
|
|
repeatable: true,
|
|
visual: { kind: "none" },
|
|
},
|
|
{
|
|
id: "statement-pad",
|
|
label: "Statement Pad",
|
|
position: { x: 0, y: 0.95, z: -0.4 },
|
|
halfExtents: { x: 1.1, y: 1.0, z: 1.0 },
|
|
action: { kind: "pickup", itemId: "witness-statement" },
|
|
visual: { kind: "box", size: { x: 0.28, y: 0.02, z: 0.36 }, color: 0xf0e8d8 },
|
|
},
|
|
{
|
|
id: "talk-nora",
|
|
label: "Nora Voss",
|
|
position: { x: 1.6, y: 0.7, z: -1.2 },
|
|
halfExtents: { x: 1.3, y: 1.2, z: 1.3 },
|
|
action: {
|
|
kind: "examine",
|
|
text:
|
|
'"I was in the lounge. Kane said he was getting coffee — he was gone too long. When he came back he smelled like the cellar, not the kitchen. That\'s all I\'m saying without counsel." Her hands shake, but her eyes stay fixed on the two-way glass.',
|
|
},
|
|
repeatable: true,
|
|
visual: { kind: "none" },
|
|
},
|
|
door({
|
|
id: "door-interview-to-gallery",
|
|
label: "Squad Hall",
|
|
position: { x: 0, y: 1.1, z: 3.2 },
|
|
targetRoomId: "manor-gallery",
|
|
spawnPointId: "from-interview",
|
|
}),
|
|
],
|
|
spawnPoints: [
|
|
{ id: "from-gallery", position: { x: 0, y: 0, z: 2.0 }, yaw: Math.PI },
|
|
],
|
|
};
|