Release Healer Man 0.1.11

This commit is contained in:
phenom committed 2026-08-31 14:12:51 -04:00
1 parent 5f1a14d8de
commit 13970d03c8
18 files changed
+514 -19

No files matched your search

+145
View File
@@ -0,0 +1,145 @@
import { readFileSync } from "node:fs";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { DUNGEON_DEFINITIONS, dungeonCanEnter } from "./dungeonRegistry";
import manastormRegistry from "./generated/manastormAssetPackages.json";
import type { ManastormStageAssetPackage } from "./manastormStageAssets";
import {
MAX_SYNCHRONOUS_PARTY_NAVIGATION_TRIANGLES,
shouldLoadPartyNavigation,
} from "./partyNavigationPolicy";
interface GlbPrimitive {
readonly attributes?: Readonly<Record<string, number>>;
readonly indices?: number;
readonly mode?: number;
}
interface GlbJson {
readonly accessors?: readonly { readonly count?: number }[];
readonly meshes?: readonly { readonly primitives?: readonly GlbPrimitive[] }[];
}
const GLB_MAGIC = 0x46546c67;
const GLB_JSON_CHUNK = 0x4e4f534a;
function readGlbJson(filePath: string): GlbJson {
const glb = readFileSync(filePath);
expect(glb.readUInt32LE(0), filePath).toBe(GLB_MAGIC);
expect(glb.readUInt32LE(4), filePath).toBe(2);
expect(glb.readUInt32LE(16), filePath).toBe(GLB_JSON_CHUNK);
const jsonLength = glb.readUInt32LE(12);
return JSON.parse(glb.toString("utf8", 20, 20 + jsonLength)) as GlbJson;
}
/** Mirrors PartyPopulation's selection of the mesh with the most positions. */
function runtimeSelectedTriangleCount(filePath: string): number {
const glb = readGlbJson(filePath);
let largestVertexCount = 0;
let triangleCount = 0;
for (const mesh of glb.meshes ?? []) {
for (const primitive of mesh.primitives ?? []) {
if (primitive.mode !== undefined && primitive.mode !== 4) continue;
const positionAccessor = primitive.attributes?.POSITION;
const vertexCount = positionAccessor === undefined
? 0
: glb.accessors?.[positionAccessor]?.count ?? 0;
if (vertexCount <= largestVertexCount) continue;
largestVertexCount = vertexCount;
const elementCount = primitive.indices === undefined
? vertexCount
: glb.accessors?.[primitive.indices]?.count ?? 0;
expect(elementCount % 3, filePath).toBe(0);
triangleCount = elementCount / 3;
}
}
expect(triangleCount, filePath).toBeGreaterThan(0);
return triangleCount;
}
function regularNavigationPath(
dungeonId: string,
navigation: { readonly fileName: string; readonly url?: string },
): string {
if (navigation.url?.startsWith("/")) {
return path.join(process.cwd(), "public", ...navigation.url.split("/").filter(Boolean));
}
return path.join(
process.cwd(),
"src",
"assets",
"game",
"dungeons",
dungeonId,
navigation.fileName,
);
}
function manastormNavigationPath(url: string): string {
return path.join(process.cwd(), "public", ...url.split("/").filter(Boolean));
}
describe("party navigation fleet safety", () => {
it("covers every regular dungeon and keeps actual synchronous geometry under budget", () => {
const enabled = [];
const fallback = [];
expect(DUNGEON_DEFINITIONS).toHaveLength(84);
for (const dungeon of DUNGEON_DEFINITIONS) {
expect(dungeonCanEnter(dungeon.id), dungeon.id).toBe(true);
const navigation = dungeon.assets.navigation;
expect(navigation, dungeon.id).toBeDefined();
expect(Number.isInteger(navigation?.triangleCount), dungeon.id).toBe(true);
expect(navigation?.triangleCount, dungeon.id).toBeGreaterThan(0);
const actualTriangles = runtimeSelectedTriangleCount(regularNavigationPath(
dungeon.id,
navigation!,
));
// Metadata may conservatively count multiple primitives, but must never
// under-report the runtime-selected primitive used by the graph builder.
expect(navigation!.triangleCount, dungeon.id).toBeGreaterThanOrEqual(actualTriangles);
if (shouldLoadPartyNavigation("dungeon", dungeon, null)) {
enabled.push(dungeon.id);
expect(dungeon.validation.status, dungeon.id).toBe("green");
expect(actualTriangles, dungeon.id)
.toBeLessThanOrEqual(MAX_SYNCHRONOUS_PARTY_NAVIGATION_TRIANGLES);
} else {
fallback.push(dungeon.id);
}
}
expect(enabled).toHaveLength(31);
expect(fallback).toHaveLength(53);
});
it("covers every Manastorm package and keeps actual synchronous geometry under budget", () => {
const packages = manastormRegistry.packages as unknown as readonly ManastormStageAssetPackage[];
const fallbackDungeon = DUNGEON_DEFINITIONS[0];
const enabled = [];
const fallback = [];
expect(packages).toHaveLength(75);
for (const assetPackage of packages) {
const declaredTriangles = assetPackage.navigation.triangleCount;
expect(Number.isInteger(declaredTriangles), assetPackage.id).toBe(true);
expect(declaredTriangles, assetPackage.id).toBeGreaterThan(0);
const actualTriangles = runtimeSelectedTriangleCount(
manastormNavigationPath(assetPackage.navigation.url),
);
expect(declaredTriangles, assetPackage.id).toBe(actualTriangles);
if (shouldLoadPartyNavigation("manastorm", fallbackDungeon, assetPackage)) {
enabled.push(assetPackage.id);
expect(actualTriangles, assetPackage.id)
.toBeLessThanOrEqual(MAX_SYNCHRONOUS_PARTY_NAVIGATION_TRIANGLES);
} else {
fallback.push(assetPackage.id);
}
}
expect(enabled).toHaveLength(41);
expect(fallback).toHaveLength(34);
});
});