Update Healer Man server

This commit is contained in:
phenom committed 2026-09-20 14:13:38 -04:00
1 parent daa417ec17
commit 8b9c95b24f
3 files changed
+48 -5

No files matched your search

+3 -2
View File
@@ -34,8 +34,9 @@ describe("bite-sized Stormwind Stockades", () => {
expect(biteSized.objectiveOrder).toEqual(biteSized.bosses);
expect(biteSized.staticSpawns.filter((spawn) => spawn.id === bossId)).toHaveLength(1);
expect(biteSized.staticSpawns.every((spawn) => Math.abs(spawn.position[2]) <= 15)).toBe(true);
expect(biteSized.navigationLinks.map((link) => link.id)).toEqual(["entrance-to-central-stairs"]);
expect(biteSized.traversalRamps?.map((ramp) => ramp.id)).toEqual(["entrance-stair-ramp"]);
expect(biteSized.navigationLinks).toEqual(source.navigationLinks);
expect(biteSized.traversalRamps).toEqual(source.traversalRamps);
expect(biteSized.roamingPacks).toEqual([]);
});
it("seals both side wings with visible movement and sight blockers", () => {
+5 -3
View File
@@ -133,8 +133,10 @@ export function createBiteSizedStockadesDefinition(
roamingPacks: [],
bosses: [boss],
objectiveOrder: [boss],
navigationLinks: source.navigationLinks.filter((link) => link.id === "entrance-to-central-stairs"),
traversalRamps: source.traversalRamps?.filter((ramp) => ramp.id === "entrance-stair-ramp"),
// Keep the complete reviewed Stockades navigation graph connected. The
// physical branch walls, not navmesh pruning, close the unused wings.
navigationLinks: source.navigationLinks,
traversalRamps: source.traversalRamps,
encounters: [{
id: `${BITE_SIZED_STOCKADES_DUNGEON_ID}:boss-progression`,
dungeonId: BITE_SIZED_STOCKADES_DUNGEON_ID,
@@ -148,7 +150,7 @@ export function createBiteSizedStockadesDefinition(
reviewedBehavior: "A single seeded Stockades boss completes the bite-sized run in the first boss room.",
adaptations: [
"Each seeded run retains the selected source boss objective ID so saves, loot, guides, and leaderboards report the correct variant.",
"The two side-wing stairs are permanently sealed and omitted from party navigation.",
"The side-wing stairs retain their navigation bridges so the central route stays on the retained navmesh component; physical walls seal both unused wings.",
],
remainingGaps: [],
},
+40
View File
@@ -29,7 +29,9 @@ const STOCKADES_NAVIGATION = "public/assets/game/manastorm/34-stormwindjail/navi
const TANK_RANGE = Object.freeze({ minimum: 0, preferred: 2.9, maximum: 3.15 });
const stockades = requireDungeonDefinition("stormwind-stockade");
const biteSizedStockades = requireDungeonDefinition("bite-sized-stormwind-stockade");
let graph: PartyNavigationGraph;
let biteSizedGraph: PartyNavigationGraph;
let sourceCollision: Group;
beforeAll(async () => {
@@ -64,6 +66,10 @@ beforeAll(async () => {
graph = preparePartyNavigationGraph(
"/test/stockades-navigation.glb", scene, stockades.navigationLinks, stockades.traversalRamps,
);
biteSizedGraph = preparePartyNavigationGraph(
"/test/bite-sized-stockades-navigation.glb", scene,
biteSizedStockades.navigationLinks, biteSizedStockades.traversalRamps,
);
geometry.dispose();
const collisionDocument = await new NodeIO()
@@ -256,6 +262,40 @@ describe("Stormwind Stockade party navigation", () => {
expect(graph.triangleNodes?.length).toBe(graph.triangles?.length);
});
it("keeps the bite-sized entrance and boss room on the retained navigation surface", () => {
const world = collisionWorld();
try {
for (const wall of biteSizedStockades.serverGameObjects.filter((object) => object.id.startsWith("bite-sized-"))) {
world.createCollider(RAPIER.ColliderDesc.cuboid(...wall.halfExtents)
.setTranslation(...wall.position)
.setCollisionGroups(DUNGEON_PHYSICAL_COLLISION_GROUPS));
}
world.step();
const entrance = findNearestPartyNavigationSurface(
biteSizedGraph,
biteSizedStockades.entrance.footPosition,
6,
);
const boss = findNearestPartyNavigationSurface(
biteSizedGraph,
biteSizedStockades.bosses[0].position,
15,
);
expect(entrance).not.toBeNull();
expect(boss).not.toBeNull();
expect(findPartyNavigationPath(
biteSizedGraph,
entrance!.position,
boss!.position,
{
maxStartSnapDistance: 0.1,
maxEndSnapDistance: 0.1,
routeSegmentAllowed: createPartyRouteSegmentValidator(RAPIER, world),
},
)).not.toBeNull();
} finally { world.free(); }
});
it("projects every entrance formation slot onto the retained walkable surface", () => {
const slots = derivePartySpawnSlots({
footPosition: stockades.entrance.footPosition,