feat: add active SF and LA HQ environments
This commit is contained in:
+61
-66
@@ -118,25 +118,21 @@ describe("the reference pack resolves cleanly", () => {
|
||||
* and setting it to the ceiling height is the classic way to bury one storey's
|
||||
* slab inside the one below.
|
||||
*/
|
||||
it("stacks the storeys floor-to-floor, not floor-to-ceiling", () => {
|
||||
const [first, second] = plan.levels;
|
||||
assert.ok(first && second, "both storeys should resolve");
|
||||
assert.equal(first.id, "level-1");
|
||||
assert.equal(second.id, "level-2");
|
||||
assert.equal(first.floorY, 0);
|
||||
|
||||
const ceiling = LUMBRIDGE_HQ.levels[0]?.wallHeight ?? 0;
|
||||
assert.ok(
|
||||
second.floorY > ceiling,
|
||||
`level 2's slab at ${second.floorY} m is inside level 1, whose ceiling is at ${ceiling} m`,
|
||||
);
|
||||
it("is an honest twelve-by-nine metre single-level studio", () => {
|
||||
assert.deepEqual(plan.levels.map((level) => [level.id, level.floorY]), [["level-1", 0]]);
|
||||
assert.deepEqual(LUMBRIDGE_HQ.levels[0]?.floorplan.rooms[3]?.outline, [
|
||||
{ x: 0.2, z: 3.15 },
|
||||
{ x: 0.2, z: 8.8 },
|
||||
{ x: 11.8, z: 8.8 },
|
||||
{ x: 11.8, z: 3.15 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps every viewpoint on a level that exists", () => {
|
||||
// `Plan` drops a viewpoint whose level does not resolve, so this is really
|
||||
// an assertion that none were dropped — which the count makes visible in a
|
||||
// way `problems` being empty already implies but does not state.
|
||||
assert.equal(plan.viewpoints.length, 8);
|
||||
assert.equal(plan.viewpoints.length, 5);
|
||||
const levels = new Set(plan.levels.map((l) => l.id));
|
||||
for (const v of plan.viewpoints) assert.ok(levels.has(v.levelId), `${v.id} is on nothing`);
|
||||
});
|
||||
@@ -163,7 +159,8 @@ describe("seat ids", () => {
|
||||
*/
|
||||
it("expand the desk banks to the documented ids", () => {
|
||||
const ids = new Set(seats.map((s) => s.id));
|
||||
for (const id of ["eng-01", "eng-12", "studio-01", "studio-12", "studio-b-01"]) {
|
||||
assert.equal(seats.length, 5);
|
||||
for (const id of ["sf-agent-01", "sf-agent-02", "sf-island-01", "sf-island-02", "sf-lounge-01"]) {
|
||||
assert.ok(ids.has(id), `${id} is missing`);
|
||||
}
|
||||
});
|
||||
@@ -186,8 +183,7 @@ describe("every room is reachable on foot", () => {
|
||||
|
||||
/** Where somebody arrives: the entrance downstairs, the landing upstairs. */
|
||||
const ENTRY: Record<string, { x: number; z: number }> = {
|
||||
"level-1": { x: 1.2, z: 4.9 },
|
||||
"level-2": { x: 2.4, z: 4.0 },
|
||||
"level-1": { x: 1.02, z: 7.68 },
|
||||
};
|
||||
|
||||
function reachableRooms(levelId: string): Set<string> {
|
||||
@@ -257,24 +253,12 @@ describe("every room is reachable on foot", () => {
|
||||
* the only place the difference between a balustrade and a handrail-shaped prop
|
||||
* is observable. A prop would leave the edge open and nothing would look wrong.
|
||||
*/
|
||||
describe("the gallery balustrade", () => {
|
||||
const OUTSIDE = 0.6;
|
||||
|
||||
it("stops a walker at all three open edges", () => {
|
||||
const edges: [string, { x: number; z: number }, { x: number; z: number }][] = [
|
||||
// North edge, at z = 2.0: standing on the gallery, stepping north.
|
||||
["north", { x: 36.0, z: 2.6 }, { x: 36.0, z: 2.0 - OUTSIDE }],
|
||||
// East edge, at x = 38.2: stepping out over the commons.
|
||||
["east", { x: 37.6, z: 9.0 }, { x: 38.2 + OUTSIDE, z: 9.0 }],
|
||||
// South edge, at z = 16.0.
|
||||
["south", { x: 36.0, z: 15.4 }, { x: 36.0, z: 16.0 + OUTSIDE }],
|
||||
];
|
||||
for (const [name, from, to] of edges) {
|
||||
assert.ok(
|
||||
plan.blocked("level-2", from, to, 0.3),
|
||||
`the ${name} edge of the gallery lets a walker off a 16.8 m drop`,
|
||||
);
|
||||
}
|
||||
describe("the studio arrival", () => {
|
||||
it("starts inside the live/work room with collision-clear movement", () => {
|
||||
const arrival = LUMBRIDGE_HQ.viewpoints[0]?.focus.at;
|
||||
assert.ok(arrival);
|
||||
assert.equal(plan.roomAt("level-1", arrival)?.id, "live-work");
|
||||
assert.equal(plan.blocked("level-1", arrival, { x: arrival.x + 0.6, z: arrival.z }, 0.3), false);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -282,8 +266,11 @@ describe("the gallery balustrade", () => {
|
||||
* balcony you cannot fall off and also cannot reach is a balcony nobody has
|
||||
* noticed is broken.
|
||||
*/
|
||||
it("does not seal the gallery off from the studio", () => {
|
||||
assert.ok(!plan.blocked("level-2", { x: 33.0, z: 7.4 }, { x: 35.0, z: 7.4 }, 0.3));
|
||||
it("keeps all five authored views meaningful and distinct", () => {
|
||||
assert.deepEqual(
|
||||
LUMBRIDGE_HQ.viewpoints.map((view) => view.id),
|
||||
["arrival", "studio", "agent-bench", "demo-lounge", "kitchen-home"],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -293,38 +280,26 @@ describe("the gallery balustrade", () => {
|
||||
* `Wall` carries a height; get the second wrong and the void has a lid at 2.8 m
|
||||
* that you cannot see but the light rig can.
|
||||
*/
|
||||
describe("the commons is open to both storeys", () => {
|
||||
it("has no ceiling", () => {
|
||||
describe("the studio programme", () => {
|
||||
it("names every promised live/work zone without pretending it is a tower floor", () => {
|
||||
const level1 = plan.levels.find((l) => l.id === "level-1");
|
||||
const commons = level1?.rooms.find((r) => r.id === "commons");
|
||||
assert.ok(commons, "the commons is missing");
|
||||
assert.equal(commons.ceiling, null);
|
||||
assert.deepEqual(
|
||||
level1?.rooms.map((room) => room.name),
|
||||
["Kitchen", "Sleeping Alcove", "Bath / Storage", "Live / Work Studio"],
|
||||
);
|
||||
});
|
||||
|
||||
it("is enclosed to two storeys rather than one", () => {
|
||||
const [level1, level2] = plan.levels;
|
||||
assert.ok(level1 && level2);
|
||||
|
||||
// The height the void *should* be, derived rather than typed: everything up
|
||||
// to level 2's slab, plus level 2's own room. That is what "open to both
|
||||
// storeys" means, and it stays true whatever the interstitial is set to.
|
||||
const ceiling = LUMBRIDGE_HQ.levels[1]?.wallHeight ?? 0;
|
||||
const expected = level2.floorY + ceiling;
|
||||
|
||||
// `solid` only. The splitting pass emits a run per piece, so a wall with a
|
||||
// door in it also yields a `lintel` over the opening — correct, and not the
|
||||
// wall's height.
|
||||
const wingWalls = level1.runs.filter(
|
||||
(run) => run.wallId.startsWith("wing-") && run.role === "solid",
|
||||
);
|
||||
assert.ok(wingWalls.length > 0, "the wing has no walls");
|
||||
for (const run of wingWalls) {
|
||||
assert.equal(
|
||||
run.top - run.bottom,
|
||||
expected,
|
||||
`${run.wallId} is ${run.top - run.bottom} m tall, not the ${expected} m the void needs`,
|
||||
);
|
||||
}
|
||||
it("uses the complete original habitat kit", () => {
|
||||
const kinds = new Set((LUMBRIDGE_HQ.levels[0]?.floorplan.props ?? []).map((prop) => prop.kind));
|
||||
for (const id of [
|
||||
"tera:bed.platform",
|
||||
"tera:sofa.modular",
|
||||
"tera:kitchen.run",
|
||||
"tera:kitchen.island",
|
||||
"tera:storage.wardrobe",
|
||||
"tera:seat.stool",
|
||||
"tera:light.floor",
|
||||
]) assert.ok(kinds.has(id), `${id} is not placed in SF HQ`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -530,6 +505,17 @@ describe("the office site table", () => {
|
||||
assert.equal(entry.site, pack.site, `${pack.id} has a site of its own`);
|
||||
}
|
||||
});
|
||||
|
||||
it("publishes truthful environment names and runtime status", () => {
|
||||
assert.deepEqual(
|
||||
OFFICE_SITES.map(({ id, name, status }) => ({ id, name, status })),
|
||||
[
|
||||
{ id: "lumbridge-hq", name: "SF HQ · Studio", status: "active" },
|
||||
{ id: "frontier-valley", name: "Frontier Valley", status: "building" },
|
||||
{ id: "mateo-court", name: "LA HQ · Office", status: "active" },
|
||||
],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -571,6 +557,15 @@ describe("the Mateo Court pack", () => {
|
||||
assert.deepEqual(mine.filter((id) => others.has(id)), []);
|
||||
});
|
||||
|
||||
it("caps the authored office at twenty-four useful seat addresses", () => {
|
||||
const ids = mc.allSeats().map((seat) => seat.id);
|
||||
assert.equal(ids.length, 24);
|
||||
for (const id of ["works-a-01", "works-a-12", "loft-a-01", "loft-a-02", "review-04", "loggia-01"]) {
|
||||
assert.ok(ids.includes(id), `${id} is missing`);
|
||||
}
|
||||
assert.equal(ids.some((id) => id.startsWith("works-b-")), false);
|
||||
});
|
||||
|
||||
/**
|
||||
* **The check that matters most in this pack.**
|
||||
*
|
||||
@@ -613,7 +608,7 @@ describe("the Mateo Court pack", () => {
|
||||
});
|
||||
|
||||
it("stands its upper-floor occupants on the deck", () => {
|
||||
for (const id of ["loggia-01", "palmetto-01"]) {
|
||||
for (const id of ["loggia-01", "loft-a-01"]) {
|
||||
assert.equal(mc.seat(id)?.y, 5, `${id} is not on the deck`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
import * as THREE from "three";
|
||||
import { createAssetContext, kit } from "../assets/kit.ts";
|
||||
import { MaterialRegistry } from "../assets/materials.ts";
|
||||
import "../assets/office/index.ts";
|
||||
|
||||
const HABITAT_IDS = [
|
||||
"tera:bed.platform",
|
||||
"tera:sofa.modular",
|
||||
"tera:kitchen.run",
|
||||
"tera:kitchen.island",
|
||||
"tera:storage.wardrobe",
|
||||
"tera:seat.stool",
|
||||
"tera:light.floor",
|
||||
] as const;
|
||||
|
||||
function seeded(): () => number {
|
||||
let value = 0x12345678;
|
||||
return () => {
|
||||
value = (Math.imul(value, 1664525) + 1013904223) >>> 0;
|
||||
return value / 0x1_0000_0000;
|
||||
};
|
||||
}
|
||||
|
||||
function disposeObject(object: THREE.Object3D): void {
|
||||
object.traverse((child) => {
|
||||
if (child instanceof THREE.Mesh) child.geometry.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
describe("procedural habitat assets", () => {
|
||||
it("registers finite metre-scale footprints for the complete studio kit", () => {
|
||||
for (const id of HABITAT_IDS) {
|
||||
assert.equal(kit.has(id), true, `${id} is not registered`);
|
||||
const footprint = kit.footprintOf(id);
|
||||
for (const value of [footprint.width, footprint.depth, footprint.height]) {
|
||||
assert.ok(Number.isFinite(value) && value > 0 && value < 5, `${id} has invalid footprint ${value}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("builds deterministic, non-placeholder geometry with finite bounds", () => {
|
||||
const materials = new MaterialRegistry({ quality: "low" });
|
||||
try {
|
||||
for (const id of HABITAT_IDS) {
|
||||
const first = kit.build(id, createAssetContext({ materials, rand: seeded() }));
|
||||
const second = kit.build(id, createAssetContext({ materials, rand: seeded() }));
|
||||
assert.equal(first.userData.assetId, id);
|
||||
assert.notEqual(first.userData.missing, true);
|
||||
const firstBox = new THREE.Box3().setFromObject(first);
|
||||
const secondBox = new THREE.Box3().setFromObject(second);
|
||||
const encode = (box: THREE.Box3) => [...box.min.toArray(), ...box.max.toArray()];
|
||||
assert.deepEqual(encode(firstBox), encode(secondBox), `${id} is not deterministic`);
|
||||
assert.ok(encode(firstBox).every(Number.isFinite), `${id} has non-finite bounds`);
|
||||
assert.ok(firstBox.max.y > 0, `${id} has no visible height`);
|
||||
disposeObject(first);
|
||||
disposeObject(second);
|
||||
}
|
||||
} finally {
|
||||
materials.dispose();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
import { samplePresenceAt, samplePresenceForOfficeAt } from "../adapters/sample.ts";
|
||||
import FRONTIER_VALLEY from "../offices/frontier-valley.ts";
|
||||
import LUMBRIDGE_HQ from "../offices/lumbridge-hq.ts";
|
||||
import MATEO_COURT from "../offices/mateo-court.ts";
|
||||
import { activityRobotsForOffice } from "../offices/runtime.ts";
|
||||
|
||||
describe("office-keyed runtime activity", () => {
|
||||
it("authors one deterministic SF robot, two LA robots, and none in a building preview", () => {
|
||||
assert.deepEqual(activityRobotsForOffice(LUMBRIDGE_HQ), [
|
||||
{ id: "sf-studio-activity-01", levelId: "level-1", seed: 0x53464851, jobSet: "studio-support" },
|
||||
]);
|
||||
assert.deepEqual(activityRobotsForOffice(MATEO_COURT), [
|
||||
{ id: "la-office-activity-01", levelId: "level-1", seed: 0x4c414851, jobSet: "office-operations" },
|
||||
{ id: "la-office-activity-02", levelId: "level-2", seed: 0x4c414852, jobSet: "model-review" },
|
||||
]);
|
||||
assert.deepEqual(activityRobotsForOffice(FRONTIER_VALLEY), []);
|
||||
});
|
||||
|
||||
it("returns defensive specs and drops a robot when a local pack removes its level", () => {
|
||||
const first = activityRobotsForOffice(MATEO_COURT);
|
||||
first[0]!.id = "mutated";
|
||||
assert.equal(activityRobotsForOffice(MATEO_COURT)[0]?.id, "la-office-activity-01");
|
||||
assert.deepEqual(activityRobotsForOffice({ id: MATEO_COURT.id, levels: [MATEO_COURT.levels[0]!] }), [
|
||||
{ id: "la-office-activity-01", levelId: "level-1", seed: 0x4c414851, jobSet: "office-operations" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("office-keyed presence fallback", () => {
|
||||
const workingHour = new Date(2026, 7, 19, 11, 0, 0);
|
||||
|
||||
it("never fabricates people at either active HQ", () => {
|
||||
assert.deepEqual(samplePresenceForOfficeAt("lumbridge-hq", workingHour), []);
|
||||
assert.deepEqual(samplePresenceForOfficeAt("mateo-court", workingHour), []);
|
||||
});
|
||||
|
||||
it("retains the legacy roster only as an explicit renderer fixture", () => {
|
||||
assert.ok(samplePresenceAt(workingHour).length > 0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user