68 lines
2.5 KiB
TypeScript
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();
|
|
});
|
|
});
|