auto-git:

[change] tests/unit/runtime-host.test.ts
This commit is contained in:
2026-04-25 03:34:53 +02:00
parent 3bdf5982cb
commit ea48fa897e

View File

@@ -519,6 +519,122 @@ describe("RuntimeHost", () => {
host.dispose();
});
it("resolves dialogue attention camera collision from the conversation midpoint", () => {
vi.spyOn(console, "warn").mockImplementation(() => undefined);
const resolveThirdPersonCameraCollision = vi.fn(
(pivot: { x: number; y: number; z: number }, desiredCameraPosition: { x: number; y: number; z: number }) => ({
x: pivot.x + (desiredCameraPosition.x - pivot.x) * 0.55,
y: pivot.y + (desiredCameraPosition.y - pivot.y) * 0.55,
z: pivot.z + (desiredCameraPosition.z - pivot.z) * 0.55
})
);
vi.spyOn(RapierCollisionWorld, "create").mockResolvedValue({
dispose: vi.fn(),
resolveThirdPersonCameraCollision
} as unknown as RapierCollisionWorld);
const playerStart = createPlayerStartEntity({
id: "entity-player-start-dialogue-collision",
position: {
x: 0,
y: 0,
z: 0
}
});
const npc = createNpcEntity({
id: "entity-npc-dialogue-collision",
position: {
x: 2,
y: 0,
z: 2
},
dialogues: [
{
id: "dialogue-collision",
title: "Collision",
lines: [
{
id: "dialogue-collision-line-1",
text: "Avoid the wall."
}
]
}
],
defaultDialogueId: "dialogue-collision"
});
const runtimeScene = buildRuntimeSceneFromDocument({
...createEmptySceneDocument({ name: "Dialogue Collision Scene" }),
entities: {
[playerStart.id]: playerStart,
[npc.id]: npc
}
});
const host = new RuntimeHost({
enableRendering: false
});
host.loadScene(runtimeScene);
const hostInternals = host as unknown as {
sceneReady: boolean;
camera: PerspectiveCamera;
collisionWorld: RapierCollisionWorld | null;
applyActiveCameraRig(
dt: number,
previousCameraPose?: {
position: Vector3;
lookTarget: Vector3;
}
): { entityId: string } | null;
createInteractionDispatcher(): {
startNpcDialogue(
npcEntityId: string,
dialogueId: string | null,
source?: {
kind: "interactionLink" | "npc" | "direct";
sourceEntityId: string | null;
linkId: string | null;
trigger: "enter" | "exit" | "click" | null;
}
): void;
};
};
const dispatcher = hostInternals.createInteractionDispatcher();
hostInternals.sceneReady = true;
hostInternals.collisionWorld = {
dispose: vi.fn(),
resolveThirdPersonCameraCollision
} as unknown as RapierCollisionWorld;
hostInternals.camera.position.set(0, 2.6, 6);
hostInternals.camera.lookAt(0, 1.6, 0);
dispatcher.startNpcDialogue(npc.id, null, {
kind: "npc",
sourceEntityId: npc.id,
linkId: null,
trigger: "click"
});
const gameplayPose = captureCameraPose(hostInternals.camera);
hostInternals.applyActiveCameraRig(0.175, gameplayPose);
const [pivot, desiredCameraPosition, radius] =
resolveThirdPersonCameraCollision.mock.calls.at(-1) ?? [];
expect(pivot).toMatchObject({
x: 1,
z: 1
});
expect(pivot?.y).toBeCloseTo(1.36, 5);
expect(radius).toBe(0.2);
expect(hostInternals.camera.position).toMatchObject({
x: pivot.x + (desiredCameraPosition.x - pivot.x) * 0.55,
y: pivot.y + (desiredCameraPosition.y - pivot.y) * 0.55,
z: pivot.z + (desiredCameraPosition.z - pivot.z) * 0.55
});
host.dispose();
});
it("keeps explicit camera rig overrides above dialogue attention", () => {
vi.spyOn(console, "warn").mockImplementation(() => undefined);
vi.spyOn(RapierCollisionWorld, "create").mockResolvedValue({