import test from "node:test"; import assert from "node:assert/strict"; import { arenaFloorHeight, arenaGeometryLineClear, arenaNavigationPath } from "./arena-geometry.mjs"; import { ARENA_STAGES } from "./arena-stages.mjs"; import { ArenaSimulation } from "./arena-simulation.mjs"; test("every arena spawn is supported by its shipped navigation surface", () => { for (const stage of ARENA_STAGES) for (const spawn of stage.teamSpawns) { const floor = arenaFloorHeight(stage.id, spawn[0], spawn[2], spawn[1], 2); assert.notEqual(floor, null, `${stage.id} ${spawn}`); assert.ok(Math.abs(floor - spawn[1]) < 2, `${stage.id} floor=${floor}, spawn=${spawn[1]}`); } }); test("characters can move away from both starting anchors into each arena", () => { for (const stage of ARENA_STAGES) for (const team of [0, 1]) { const sim = new ArenaSimulation({ id: "traversal", stage, bracket: "2v2", mode: "practice", ruleset: "wow", startedAt: 1000, participants: [{ actorId: "player", name: "Player", team, role: "healer", isBot: false }] }); sim.phase = "active"; sim.activeStartedAt = 1000; const actor = sim.actor("player"), start = [...actor.position], gate = stage.preparationGates[team]; const dx = gate[0] - start[0], dz = gate[2] - start[2], length = Math.hypot(dx, dz); actor.movement = [dx / length, dz / length]; for (let tick = 0; tick < 50; tick++) sim.moveActor(actor, .05); assert.ok(Math.hypot(actor.position[0] - start[0], actor.position[2] - start[2]) > 3, `${stage.id} team ${team}: ${actor.position} from ${start}`); assert.ok(actor.position.every(Number.isFinite)); } }); test("collision geometry blocks arena walls and preserves clear short sight lines", () => { const stage = ARENA_STAGES.find((entry) => entry.id === "nagrand-arena"); const spawn = stage.teamSpawns[0]; assert.equal(arenaGeometryLineClear(stage.id, [spawn[0], spawn[1] + 1.2, spawn[2]], [spawn[0] + .05, spawn[1] + 1.2, spawn[2]]), true); assert.equal(arenaGeometryLineClear(stage.id, [-60, 14.5, -42], [-35, 14.5, -42]), false); }); test("bots can traverse each complete arena between both teams without invisible walls", () => { for (const stage of ARENA_STAGES) for (const team of [0, 1]) { const sim = new ArenaSimulation({ id: "path", stage, bracket: "3v3", mode: "practice", ruleset: "wow", startedAt: 1000, participants: [0, 1, 2].map((index) => ({ actorId: `p${index}`, name: "Player", team, role: "damage", isBot: false })) }); sim.phase = "active"; sim.activeStartedAt = 1000; for (const actor of sim.actors.values()) assert.notEqual(arenaFloorHeight(stage.id, actor.position[0], actor.position[2], actor.position[1], 2), null, `${stage.id} team ${team} ${actor.id} spawn`); const actor = sim.actor("p0"), destination = stage.teamSpawns[1 - team]; actor.navigationPath = arenaNavigationPath(stage.id, actor.position, destination); assert.ok(actor.navigationPath.length, `${stage.id} has a route`); for (let tick = 0; tick < 2400; tick++) { sim.tickNumber++; sim.followBotPath(actor); sim.moveActor(actor, .05); if (Math.hypot(actor.position[0] - destination[0], actor.position[2] - destination[2]) < 2) break; } assert.ok(Math.hypot(actor.position[0] - destination[0], actor.position[2] - destination[2]) < 2, `${stage.id} team ${team} stuck at ${actor.position}`); } });