import { describe, expect, it } from "vitest"; // The production Recast policy is a Node pipeline module outside the browser TypeScript project. // @ts-expect-error There is intentionally no browser-facing declaration for this Node-only module. import * as recastPolicy from "../../scripts/dungeon-pipeline/recast-policy.mjs"; const { nextTiledNavMeshSize, planNavigationBuild, shouldRetryFailedTiledBuildWithMonotone, shouldUseMonotonePartition, validateTiledBuild, } = recastPolicy; describe("dungeon Recast build policy", () => { it("keeps ordinary dungeon bounds on the solo builder", () => { expect(planNavigationBuild([[0, -10, 0], [600, 20, 500]], 0.2)).toMatchObject({ mode: "solo", gridWidth: 3000, gridHeight: 2500, tileCount: 1, }); }); it("tiles continent-scale dungeon heightfields without changing voxel resolution", () => { expect(planNavigationBuild( [[-573.862, -141.836, -1151.037], [386.687, 889.868, 115.299]], 0.2, )).toMatchObject({ mode: "tiled", gridWidth: 4803, gridHeight: 6332, gridCells: 30_412_596, tileSize: 512, tileColumns: 10, tileRows: 13, tileCount: 130, }); }); it("can explicitly tile compact but contour-complex source geometry", () => { expect(planNavigationBuild([[0, 0, 0], [40, 10, 40]], 0.2, { tileSize: 64, maxSoloAxis: 0, maxSoloCells: 0, })).toMatchObject({ mode: "tiled", tileSize: 64, tileColumns: 4, tileRows: 4 }); }); it("accepts only confirmed empty-tile packing failures", () => { const intermediates = { buildContext: { logs: [ { category: 1, msg: "Building tile at x: 0, y: 0" }, { category: 3, msg: "Failed to create Detour navmesh data" }, { category: 1, msg: "Building tile at x: 1, y: 0" }, { category: 3, msg: "Failed to create Detour navmesh data" }, ], }, tileIntermediates: [ { x: 0, y: 0, contourSet: { nconts: () => 0 }, polyMesh: { npolys: () => 0 }, }, { x: 1, y: 0, contourSet: { nconts: () => 2 }, polyMesh: { npolys: () => 4 }, }, ], }; expect(validateTiledBuild(intermediates)).toMatchObject({ valid: false, populatedTileCount: 1, ignoredEmptyTilePackingFailures: [{ x: 0, y: 0 }], failures: [{ x: 1, y: 0 }], }); }); it("rejects a tile that Detour failed to add", () => { const intermediates = { buildContext: { logs: [ { category: 1, msg: "Building tile at x: 2, y: 3" }, { category: 2, msg: "Failed to add tile to nav mesh tx: 2, ty: 3" }, ], }, tileIntermediates: [{ x: 2, y: 3, contourSet: { nconts: () => 1 }, polyMesh: { npolys: () => 3 }, }], }; expect(validateTiledBuild(intermediates)).toMatchObject({ valid: false, failures: [{ x: 2, y: 3 }], }); }); it("validates released intermediates against populated Detour tiles", () => { const intermediates = { buildContext: { logs: [ { category: 1, msg: "Building tile at x: 0, y: 0" }, { category: 3, msg: "Failed to create Detour navmesh data" }, { category: 1, msg: "Building tile at x: 1, y: 0" }, ], }, tileIntermediates: [ { x: 0, y: 0 }, { x: 1, y: 0 }, ], }; expect(validateTiledBuild(intermediates, { intermediateRetention: "released", populatedTileCount: 1, })).toMatchObject({ valid: true, populatedTileCount: 1, intermediateRetention: "released", ignoredEmptyTilePackingFailures: [{ x: 0, y: 0, verification: "released" }], failures: [], }); }); it("retiles only contour-outline failures at a finer tile size", () => { expect(nextTiledNavMeshSize({ failures: [{ message: "rcBuildContours: Bad outline for region 7, contour simplification is likely too aggressive.", }], }, 512)).toBe(256); expect(nextTiledNavMeshSize({ failures: [{ message: "rcBuildRegions: 1 overlapping regions.", }], }, 256)).toBe(128); expect(nextTiledNavMeshSize({ failures: [{ message: "Failed to add tile to nav mesh tx: 2, ty: 3", }], }, 512)).toBeNull(); expect(nextTiledNavMeshSize({ failures: [{ message: "rcBuildContours: Bad outline for region 7, contour simplification is likely too aggressive.", }], }, 128)).toBeNull(); }); it("uses monotone partition only when watershed overlap is the sole failure", () => { expect(shouldUseMonotonePartition({ failures: [ { message: "rcBuildRegions: 1 overlapping regions." }, { message: "rcBuildRegions: 2 overlapping regions." }, ], })).toBe(true); expect(shouldUseMonotonePartition({ failures: [ { message: "rcBuildRegions: 1 overlapping regions." }, { message: "rcBuildContours: Bad outline for region 7, contour simplification is likely too aggressive." }, ], })).toBe(false); }); it("retries watershed contour triangulation failures with monotone partition only", () => { expect(shouldRetryFailedTiledBuildWithMonotone("Failed to triangulate contours", "watershed")).toBe(true); expect(shouldRetryFailedTiledBuildWithMonotone("Failed to triangulate contours", "monotone")).toBe(false); expect(shouldRetryFailedTiledBuildWithMonotone("Out of memory", "watershed")).toBe(false); }); });