Files
healer-man/src/game/partyNavigationAsset.test.ts
T
2026-08-14 15:56:39 -04:00

68 lines
2.5 KiB
TypeScript

import { NodeIO } from "@gltf-transform/core";
import { BufferAttribute, BufferGeometry } from "three";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { beforeAll, describe, expect, it } from "vitest";
import {
buildPartyNavigationGraph,
findPartyNavigationPath,
type PartyNavigationGraph,
type PartyNavigationPosition,
} from "./partyNavigation";
import { WAILING_CAVERNS_DEFINITION } from "./dungeons/wailingCaverns";
const PARTY_BOSS_OBJECTIVES = WAILING_CAVERNS_DEFINITION.objectiveOrder;
const ENTRANCE: PartyNavigationPosition = [163.49, -73.66, 132.9];
const projectDirectory = fileURLToPath(new URL("../..", import.meta.url));
const navigationPath = path.join(
projectDirectory,
"src",
"assets",
"game",
"dungeons",
"wailing-caverns",
"wailing-caverns-navigation.glb",
);
let graph: PartyNavigationGraph;
beforeAll(async () => {
const document = await new NodeIO().read(navigationPath);
const primitive = document.getRoot().listMeshes()[0]?.listPrimitives()[0];
const positions = primitive?.getAttribute("POSITION")?.getArray();
const indices = primitive?.getIndices()?.getArray();
if (!positions || !indices) throw new Error("Shipping party navigation asset is not indexed.");
const geometry = new BufferGeometry();
geometry.setAttribute("position", new BufferAttribute(new Float32Array(positions), 3));
geometry.setIndex(new BufferAttribute(new Uint32Array(indices), 1));
graph = buildPartyNavigationGraph(geometry);
geometry.dispose();
});
describe("shipping Wailing Caverns party navigation", () => {
it("routes every approved main-component objective from the previous encounter", () => {
let origin: PartyNavigationPosition = ENTRANCE;
for (const objective of PARTY_BOSS_OBJECTIVES.slice(0, 5)) {
const route = findPartyNavigationPath(graph, origin, objective.position, {
maxStartSnapDistance: 3,
maxEndSnapDistance: 3,
simplifyTolerance: 0.01,
});
expect(route, `Missing approved route to ${objective.name}`).not.toBeNull();
expect(route!.length).toBeGreaterThan(1);
origin = objective.position;
}
});
it("rejects the disconnected Serpentis transition instead of authorizing a wall shortcut", () => {
const skum = PARTY_BOSS_OBJECTIVES[4];
const serpentis = PARTY_BOSS_OBJECTIVES[5];
expect(findPartyNavigationPath(graph, skum.position, serpentis.position, {
maxStartSnapDistance: 3,
maxEndSnapDistance: 3,
simplifyTolerance: 0.01,
})).toBeNull();
});
});