1
0
This repository has been archived on 2026-08-25. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
tera/src/test/officeWalker.test.ts
T

100 lines
3.5 KiB
TypeScript

import assert from "node:assert/strict";
import { describe, it } from "node:test";
import * as THREE from "three";
import { createOfficeWalker } from "../interiors/officeWalker.ts";
import { Plan } from "../interiors/plan.ts";
import type { Level, Office, Room, Wall } from "../interiors/types.ts";
const FLOOR: Room = {
id: "floor",
name: "Floor",
floor: "floor" as never,
outline: [{ x: 0, z: 0 }, { x: 10, z: 0 }, { x: 10, z: 8 }, { x: 0, z: 8 }],
};
function makePlan(walls: Wall[] = [], elevation = 2.5): Plan {
const level: Level = {
id: "ground",
name: "Ground",
elevation,
wallHeight: 3,
wallThickness: 0.1,
floorplan: { rooms: [FLOOR], walls },
};
const office: Office = { id: "actor-test", name: "Actor Test", levels: [level], viewpoints: [] };
return new Plan(office, { warn: false });
}
describe("office walker actor adapter", () => {
it("is inert by default and becomes a floor-aware humanoid when activated", () => {
const actor = createOfficeWalker(makePlan(), {
levelId: "ground",
position: { x: 2, z: 2 },
speed: 1,
fixedStep: 0.1,
});
assert.equal(actor.state().actor, "humanoid");
assert.equal(actor.root.position.y, 2.5);
actor.setAction({ x: 2, z: 0 });
actor.tick(0.2);
assert.deepEqual(actor.state().position, { x: 2, z: 2 });
actor.setActive(true);
assert.deepEqual(actor.action(), { x: 0, z: 0 }, "activation never replays stale input");
actor.setAction({ x: 2, z: 0 });
actor.tick(0.2);
assert.ok(actor.state().position.x > 2.19);
assert.equal(actor.view.position.y, 2.5);
assert.ok(Math.abs(actor.root.rotation.y + Math.PI / 2) < 1e-12);
actor.dispose();
});
it("builds an anonymous dog and publishes a defensive chase-camera contract", () => {
const actor = createOfficeWalker(makePlan(), {
levelId: "ground",
position: { x: 4, z: 4 },
actor: { kind: "anonymous-dog", coatColor: 0x222222 },
active: true,
});
assert.equal(actor.root.userData.actorType, "anonymous-dog");
const pose = actor.followPose();
assert.ok(pose.position.z > 4, "camera is behind a -Z-facing actor");
assert.ok(pose.target.z < 4, "camera aims ahead of the actor");
pose.position.x = 999;
assert.notEqual(actor.followPose().position.x, 999);
actor.dispose();
});
it("uses resolved door gaps while retaining wall collision and reset state", () => {
const plan = makePlan([{
id: "divider",
from: { x: 5, z: 0 },
to: { x: 5, z: 8 },
openings: [{ kind: "door", start: 3.4, width: 1.2, sill: 0, head: 2.1 }],
}]);
const actor = createOfficeWalker(plan, {
levelId: "ground",
position: { x: 4, z: 4 },
speed: 2,
fixedStep: 0.1,
active: true,
});
actor.setAction({ x: 1, z: 0 });
for (let index = 0; index < 10; index += 1) actor.tick(0.1);
assert.ok(actor.state().position.x > 5.5);
const reset = actor.reset({ levelId: "ground", position: { x: 2, z: 2 } });
assert.deepEqual(reset.position, { x: 2, z: 2 });
assert.deepEqual(reset.action, { x: 0, z: 0 });
assert.equal(reset.distance, 0);
actor.dispose();
});
it("removes and disposes its actor root idempotently", () => {
const actor = createOfficeWalker(makePlan(), { levelId: "ground", position: { x: 2, z: 2 } });
const parent = new THREE.Group();
parent.add(actor.root);
actor.dispose();
actor.dispose();
assert.equal(actor.root.parent, null);
});
});