diff --git a/PLAYTEST_NOTES.md b/PLAYTEST_NOTES.md index 1bcaf66b..bffe968f 100644 --- a/PLAYTEST_NOTES.md +++ b/PLAYTEST_NOTES.md @@ -2762,3 +2762,120 @@ Vite's existing large-chunk advisory remains informational. passed, along with the native vocation-import regression. The production TypeScript/Vite build and release-bundle verification passed. The final release candidate check includes the new tests and generated catalogs. + +## RoM dungeon enemy durability — September 13, 2026 + +- Reproduced Barren Caves' low health with a RoM Priest and the four shipped + companions. Tested 108 combinations: 12 party seeds at levels 1, 2, 4, 8, 9, + 20, 40, 70, and 80. The old fallback had 94 health at level 1 and 1,832 at + level 80; kills took 0–1.9 seconds from the first damaging NPC hit. +- Normal RoM dungeons now use a party-health baseline of + `1000 + 100 * enemyLevel + 8 * enemyLevel * enemyLevel`, with authored + creature/boss multipliers applied separately. This is explicit Healer Man + balancing because native RoM creature health tables are not packaged. + Barren Caves' Anglo retains his 7.5 multiplier and one-level advantage. +- Loaded RoM creatures receive the new health when the character levels up. + Re-registration preserves wounds as a health percentage and keeps corpses + dead. Enemy attack damage also rebases on a level change instead of retaining + its original low-level value. Regression checks cover these transitions. +- With the new health, the 108 isolated party-damage pulls lasted 3–17.5 + seconds. A separate pull exercised actual enemy attacks and Priest healing + before the enemy died, with all companions surviving. These are opening + pulls with baseline equipment, rather than an audit of every geared build. +- Live PC check at 1280×720: created the offline Human Priest Barrenaudit and + entered Barren Caves as Healer. A level-1 Cave Sand Bug had 1,108 health. + At 10 seconds it still had 374 health, with 734 party damage on the meter. + The completed first fight showed 14 seconds and 1,108 total damage: + Morgha 370, Ilyen 369, Maerwen 289, and Cael 80. Urgent Heal then restored + Maerwen to 407/407 while the next pull was active. No browser errors were + captured during that bounded combat check. +- Concurrent navigation edits triggered development hot reloads around the + playtest; the captures above were taken during uninterrupted combat. This + check did not complete the dungeon or use physical Ayn Thor hardware. + Restored the original Hudtest roster selection, closed the test tab, and + stopped the temporary development servers. +- Evidence: `artifacts/rom-dungeon-health/report.json`, + `barren-caves-health.png`, `barren-caves-combat.png`, `barren-caves-healed.png`, + `live-combat-dom.txt`, and `browser-errors.json`. +- Validation: all 407 focused combat, party, CoA, RoM, DPS-meter, stat, and + dungeon-scaling tests passed across 52 files. The final production + TypeScript/Vite build and release-bundle verification passed. + +## 2026-09-13 - WoW dungeon lighting and stair fleet pass + +- Applied environment-specific readable lighting to all 54 WoW dungeon entries, + using the reviewed Stockades ambient/hemisphere fill as the baseline. Stone, + cavern, fire, arcane, crypt, and outdoor profiles preserve distinct coloration. + Stockades' existing presentation/three ramps and RuneWaker lighting are unchanged. +- Scanned every shipped WoW collision package, including authored Wailing + Caverns' actual source assets. Added 347 verified walking proxies across 22 + other dungeons and eight filled navigation portal bridges in Uldaman. Rendered + steps and source visual/collision/navigation GLBs remain unchanged. +- Added package-identity gates and a compiler-independent shared runtime adapter. + Collision preparation spatially indexes flights; rejected candidates cannot + supply geometry needed by accepted proxies. Existing navigation validation, + content, encounter, and availability gates were not promoted to green. +- Shipping-asset regressions independently pass all 347 new proxies through + prepared collision and the live NPC segment validator uphill/downhill at the + center and two interior lateral lanes. Exact portal matching and all existing + Stockades player/NPC/wall tests pass. +- The game-playtest visual viewer uses shipped meshes and runtime lighting and + material policies without touching saved character/party state. At 1280x720, + Uldaman's old/new-fill comparison showed clearer stonework; Wailing Caverns + retained green, readable rock; Mechanar retained cool arcane interiors; + Scholomance's visible entrance steps/masonry remained intact and readable. + Blood Furnace's dark source textures remained visible at its wall-facing + entrance viewpoint. Durnholde's visual load completed without console errors. +- Zul'Farrak entrance/boss views and Durnholde's exterior boss view showed + geometry/terrain/viewpoint gaps, recorded for separate investigation. This is + not exhaustive staircase certification: curved/short/heavily-decimated flights, + rejected candidates, outer stair edges/landing lips, complete dungeon routes, + full clears, and physical Android QA still require individual review. +- Evidence/coverage: `dungeon-pipeline/WOW_ENVIRONMENT_REVIEW.md`, + `src/game/generated/wowDungeonStairs.generated.json`, and screenshots in + `artifacts/wow-dungeon-environment/`. No hosted release was published. +- Validation: 247 Vitest files passed, 1,599 tests passed, six existing tests + skipped. Production TypeScript/Vite build, texture/performance audits, bundle + compression/verification, and release candidate check passed. Existing physics + initialization and bundle-size/import advisories remain. + +## 2026-09-13 - Dungeon completion results and run leaderboards + +- Fixed missing RuneWaker completion bookkeeping for the 21 dungeons with + configured bosses. Barren Caves now awards Anglo's objective on his actual + death and announces the completed run. Bossless placeholder entries do not + fabricate a completion. +- Run timing starts when the dungeon is playable, survives wipes, pauses, and + saved resumes, and freezes at the final-boss objective. Older checkpoints + without a recorded start time still announce completion but remain unranked. +- The results dialog shows the final-boss time, final-clear and full-clear + placement, party classes/levels, and defeated bosses. Stay and loot, reopening + results, and Return to main menu were verified through the live UI. Returning + clears the completed checkpoint. +- Replaced the dungeon picker's minimap with persistent completion-time + leaderboards. Final Boss clears includes all qualifying runs; Full dungeon + clears requires every available boss for the selected difficulty. Expanded + entries show each party member and boss status. Normal/heroic rankings are + separate; disconnected results use an explicitly labeled device leaderboard. +- An actual combat integration check fought Barren Caves' level-2 Anglo with + 9,240 health using NPC ability selection, enemy attacks, and Priest healing. + NPCs dealt 9,239 damage after a one-damage player tag; healing totaled 441. + The final death awarded completion at 84.1 seconds with all companions alive. +- Manual browser combat confirmed NPC damage and healing on Barren Caves + trash. The live results check restored the integration fight's completed + checkpoint through an isolated local QA account: 01:24.1 ranked #1 of three + seeded local-server entries in both filters. This was not a manual browser + full clear and no production leaderboard entries were published. +- PC results and expanded final/full leaderboard layouts were visually + reviewed at 1280x720. The built-in Thor preview displayed the leaderboard, + filters, and role-selection control within its 960x540 main surface. Compact + 480x360 DOM bounds kept the dialog footer inside the viewport; the in-app + browser's viewport-override screenshot scaled incorrectly, so that capture + does not establish compact visual quality. Physical Thor hardware was not used. +- Evidence: `artifacts/dungeon-runs/combat-completion.json`, `results-world.png`, + `leaderboard-final-expanded.png`, `leaderboard-full-expanded.png`, and + `leaderboard-thor.png`. The bounded final browser check captured no errors. +- Validation: 70 completion, checkpoint, encounter, catalog, client, and UI + tests plus the real-combat integration test passed. The 432-test focused + combat/party/class/health/input regression suite and 18 server tests passed. + Production build, final TypeScript check, and release candidate check passed. diff --git a/dungeon-pipeline/WOW_ENVIRONMENT_REVIEW.md b/dungeon-pipeline/WOW_ENVIRONMENT_REVIEW.md new file mode 100644 index 00000000..d354f4d5 --- /dev/null +++ b/dungeon-pipeline/WOW_ENVIRONMENT_REVIEW.md @@ -0,0 +1,119 @@ +# WoW dungeon lighting and stair review - 2026-09-13 + +## Implemented coverage + +All 54 campaign WoW dungeons now receive environment-specific lighting using +Stockades' reviewed ambient/hemisphere fill as the baseline. Stone, cavern, +fire, arcane, crypt, and outdoor profiles preserve atmosphere without applying +the old dark expansion palettes. Stockades' existing presentation and three +reviewed stair proxies remain unchanged. RuneWaker environments are excluded. + +The fleet scan produced 347 additional collision-verified walking proxies in +22 dungeons, and eight filled Recast boundary bridges in Uldaman. This makes +350 walking proxies including the three existing Stockades flights. Visual +stairs remain steps: source visual/collision/navigation GLBs were not modified. +The shared prepared collision is used by players, companions, and enemies. + +| Dungeon | Additional walking proxies | Additional navigation bridges | +| --- | ---: | ---: | +| Shadowfang Keep | 41 | 0 | +| Blackfathom Deeps | 5 | 0 | +| Uldaman | 5 | 8 | +| Gnomeregan | 4 | 0 | +| Sunken Temple | 2 | 0 | +| Scarlet Monastery | 37 | 0 | +| Zul'Farrak | 1 | 0 | +| Scholomance | 36 | 0 | +| Stratholme | 12 | 0 | +| Blood Furnace | 1 | 0 | +| Shadow Labyrinth | 1 | 0 | +| Mana-Tombs | 3 | 0 | +| Auchenai Crypts | 5 | 0 | +| Magister's Terrace | 10 | 0 | +| Escape From Durnholde | 120 | 0 | +| Utgarde Keep | 8 | 0 | +| Utgarde Pinnacle | 32 | 0 | +| Culling of Stratholme | 11 | 0 | +| Halls of Stone | 2 | 0 | +| Halls of Lightning | 1 | 0 | +| Pit of Saron | 1 | 0 | +| Halls of Reflection | 9 | 0 | + +The machine-readable `src/game/generated/wowDungeonStairs.generated.json` +records a scan for every WoW dungeon, including entries with no accepted new +proxy, collision package identities, candidate/rejection counts, and exact +navigation portals. Authored Wailing Caverns is scanned against its actual +source assets, rather than the campaign's separate canonical package. + +## Safety and verification + +- Require repeated short risers with consistent rise/run and overlapping width. +- Merge compatible continuous flights; independently verify short fallbacks + when a merged flight is unsafe. +- Rebuild after rejected proxies are removed, so accepted paths do not depend + on rejected geometry edits. +- Check real terrain support and capsule sweeps uphill/downhill along the center + and two interior lateral lanes at approximately 0.35m intervals. +- Fill only separate existing boundary portals wholly within a verified flight. + Do not invent off-mesh links across unrelated floors or walls. +- Gate generated coordinates by collision package URL/checksum identity, and + navigation identity where exact portal bridges are present. +- Keep existing validation/availability gates unchanged. Stair checks do not + certify boss routes, encounters, resets, checkpoints, or complete runs. +- Spatially index ramp footprints during preparation to avoid checking every + ramp against every vertex in large, multi-chunk environments. + +The shipping-asset regression tests independently exercise all 347 new proxies +through `prepareCollision` and the live NPC direct-segment validator in both +directions and all three tested lanes. All generated portals match the actual +shipping navigation vertices. Existing Stockades player/NPC/wall regressions pass. + +Full validation: 247 Vitest files passed, 1,599 tests passed, six existing tests +skipped. Production TypeScript/Vite build, texture/performance audits, bundle +compression/verification, and release candidate checking passed. Existing +physics initialization and bundle-size/import advisories remain. + +## Visual evidence and remaining QA + +The local visual viewer uses the runtime definitions/material policy, shipped +GLBs, Meshopt/KTX2 decoding, ACES tone mapping, and 1280x720 screenshots. It does +not modify saved characters or party state. Evidence is under +`artifacts/wow-dungeon-environment/` (ignored QA artifacts). + +Uldaman's new/old-fill comparison shows clearer stone walls, columns, and floor +tiles. Wailing Caverns retains green coloration with readable rock formations. +Mechanar retains cool purple/blue interiors and unchanged visible steps. +Scholomance's entrance stairs and masonry remain visible with the crypt profile. +Blood Furnace's dark source textures remain atmospheric; the entrance viewpoint +faces a nearby wall, so this is not a full-room traversal review. + +Zul'Farrak showed a geometry/viewpoint gap in the standalone entrance and boss +views. Do not treat its lighting profile or passing isolated proxy sweep as an +end-to-end visual/playability pass. Durnholde's large package completed loading +without console errors; its cavern entrance and outdoor fort masonry/trees were +readable, but the boss viewpoint also showed missing terrain/viewpoint coverage. +That environment needs a separate terrain/spawn review and live traversal check. + +This is not an exhaustive certification that every staircase is repaired. +Conservative repeated-riser detection can miss curved flights, one/two-step +transitions, and heavily decimated source geometry. A rejected candidate may be +decorative, lack headroom/ground, or require an individually reviewed footprint. +An entry with no new proxy is not proof that it contains no stairs. Full-dungeon +actor traversal, entrance/exit lips, outer stair edges, manual review of rejected +or undetected flights, full clears, and physical Android QA remain open. + +## Repeating the checks + +Requires the project's Node 24 runtime for native TypeScript pipeline execution. + +```text +npm run dungeon:wow:stairs:audit +npm run dungeon:wow:stairs:refresh +npm run dungeon:wow:stairs:audit -- scholomance --diagnostics +node scripts/dungeon-pipeline/audit-wow-stairs.ts wailing-caverns --update +npm test -- --run src/game/wowDungeonStairAssets.test.ts +``` + +The audit is read-only by default. Refresh writes the full generated catalog; +`--update` replaces only the named entry while retaining the other entries. +Campaign regeneration does not erase the runtime lighting/stair adapter. diff --git a/package-lock.json b/package-lock.json index 00401d37..218b6d94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "healer-man", - "version": "0.1.38", + "version": "0.1.39", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "healer-man", - "version": "0.1.38", + "version": "0.1.39", "dependencies": { "@capacitor/android": "8.4.1", "@capacitor/core": "8.4.1", diff --git a/package.json b/package.json index 45920583..cdf18184 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "healer-man", "private": true, - "version": "0.1.38", + "version": "0.1.39", "type": "module", "scripts": { "dev": "vite --host 0.0.0.0", @@ -56,6 +56,8 @@ "dungeon:five-player:verify": "node scripts/dungeon-pipeline/verify-five-player-dungeon-imports.mjs", "dungeon:wow:audit": "vite-node scripts/dungeon-pipeline/audit-wow-dungeon-content.mjs", "dungeon:wow:functionality:audit": "vite-node scripts/dungeon-pipeline/audit-wow-dungeon-functionality.ts", + "dungeon:wow:stairs:audit": "node --max-old-space-size=8192 scripts/dungeon-pipeline/audit-wow-stairs.ts", + "dungeon:wow:stairs:refresh": "node --max-old-space-size=8192 scripts/dungeon-pipeline/audit-wow-stairs.ts --write", "dungeon:wow:test": "vitest run src/game/wowDungeonContent.test.ts", "dungeon:wow:export-tool:test": "node --test scripts/dungeon-pipeline/wow-export-tool.test.mjs", "dungeon:ascension:recovery:validate": "node scripts/dungeon-pipeline/validate-ascension-custom-dungeon-recovery.mjs --skip-dbc", diff --git a/release-version.json b/release-version.json index 675ec314..0e5751c1 100644 --- a/release-version.json +++ b/release-version.json @@ -1,4 +1,4 @@ { - "versionName": "0.1.38", - "versionCode": 1038 + "versionName": "0.1.39", + "versionCode": 1039 } diff --git a/scripts/dungeon-pipeline/audit-wow-stairs.ts b/scripts/dungeon-pipeline/audit-wow-stairs.ts new file mode 100644 index 00000000..9c4d83ff --- /dev/null +++ b/scripts/dungeon-pipeline/audit-wow-stairs.ts @@ -0,0 +1,142 @@ +import { readFileSync, writeFileSync } from "node:fs"; +import { createHash } from "node:crypto"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import type { DungeonDefinition } from "../../src/game/dungeonTypes"; +import { detectDungeonStairRamps, dungeonStairNavigationBridges, dungeonStairRisers, readDungeonGeometry } from "./wow-stair-geometry.ts"; +import RAPIER from "@dimforge/rapier3d-compat"; +import { dungeonRampHeight, smoothDungeonStairGeometry } from "../../src/game/dungeonTraversalRamps.ts"; +import type { DungeonTraversalRamp } from "../../src/game/dungeonTypes"; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); +const catalog = JSON.parse(readFileSync(path.join(root, "src/game/generated/dungeonCampaignCatalog.json"), "utf8")); +const outputFile = path.join(root, "src/game/generated/wowDungeonStairs.generated.json"); +const output: Record = process.argv.includes("--update") + ? JSON.parse(readFileSync(outputFile, "utf8")).dungeons : {}; +const requested = process.argv.slice(2).find((arg) => !arg.startsWith("--")); +if (requested && !catalog.definitions.some((dungeon: DungeonDefinition) => dungeon.id === requested)) { + throw new Error(`Unknown WoW dungeon: ${requested}`); +} +await RAPIER.init(); + +function rampTraversable(world: RAPIER.World, ramp: DungeonTraversalRamp): boolean { + const rejected = (reason: string, detail: unknown) => { + if (process.argv.includes("--diagnostics")) console.log(ramp.id, reason, JSON.stringify(detail)); + return false; + }; + const dx = ramp.end[0] - ramp.start[0]; + const dz = ramp.end[2] - ramp.start[2]; + const length = Math.hypot(dx, dz); + const shape = new RAPIER.Capsule(0.32, 0.34); + const rotation = { x: 0, y: 0, z: 0, w: 1 }; + // Test both directions across the center and two capsule-safe lateral lanes. + const lateral = Math.max(0, Math.min(ramp.width / 4, ramp.width / 2 - 0.45)); + for (const lane of [0, -lateral, lateral]) { + const count = Math.max(3, Math.ceil(length / 0.35) + 1); + const points = Array.from({ length: count }, (_, index) => { + const t = index / (count - 1); + return { + x: ramp.start[0] + dx * t - dz / length * lane, + y: ramp.start[1] + (ramp.end[1] - ramp.start[1]) * t, + z: ramp.start[2] + dz * t + dx / length * lane, + }; + }); + for (const point of points) { + const floor = world.castRayAndGetNormal(new RAPIER.Ray({ ...point, y: point.y + 1.25 }, { x: 0, y: -1, z: 0 }), 2, true); + if (!floor || floor.normal.y < 0.65 || Math.abs(1.25 - floor.timeOfImpact) > 0.55) return rejected("floor", { lane, point, normal: floor?.normal, heightDelta: floor ? 1.25 - floor.timeOfImpact : null }); + } + for (let index = 1; index < points.length; index += 1) { + for (const [start, end] of [[points[index - 1], points[index]], [points[index], points[index - 1]]]) { + const delta = { x: end.x - start.x, y: end.y - start.y, z: end.z - start.z }; + const distance = Math.hypot(delta.x, delta.y, delta.z); + const hit = world.castShape({ ...start, y: start.y + 1.02 }, rotation, delta, shape, 0.025, 1, false); + if (hit && hit.time_of_impact < 1 - 0.04 / distance) return rejected("sweep", { lane, start, end, hit: hit.time_of_impact }); + } + } + } + return true; +} + +for (const dungeon of catalog.definitions as DungeonDefinition[]) { + if (requested && dungeon.id !== requested) continue; + if (dungeon.id === "stormwind-stockade") { + output[dungeon.id] = { collision: dungeon.assets.collision, ramps: [], reviewedOverride: true }; + console.log(`${dungeon.id}: preserving three reviewed ramps`); + continue; + } + const risers = []; + const geometries = []; + // Wailing Caverns retains its authored environment rather than the campaign's + // canonical Manastorm package. Audit the assets actually used by that entry. + const collisionAssets = dungeon.id === "wailing-caverns" ? [{ + fileName: "wailing-caverns-collision.glb", url: undefined, + checksum: createHash("sha256").update(readFileSync(path.join(root, "src/assets/game/dungeons/wailing-caverns/wailing-caverns-collision.glb"))).digest("hex"), + }] : dungeon.assets.collision; + const assetFile = (asset: { url?: string; fileName: string }) => asset.url + ? path.join(root, "public", asset.url) + : path.join(root, "src/assets/game/dungeons", dungeon.id, asset.fileName); + const navigationAsset = dungeon.id === "wailing-caverns" ? { + fileName: "wailing-caverns-navigation.glb", url: undefined, + checksum: createHash("sha256").update(readFileSync(path.join(root, "src/assets/game/dungeons/wailing-caverns/wailing-caverns-navigation.glb"))).digest("hex"), + } : dungeon.assets.navigation; + for (const asset of collisionAssets) { + for (const geometry of await readDungeonGeometry(assetFile(asset))) { + risers.push(...dungeonStairRisers(geometry)); + geometries.push(geometry); + } + } + const candidates = detectDungeonStairRamps(risers); + const verify = (input: DungeonTraversalRamp[]): DungeonTraversalRamp[] => { + let ramps = input; + // Rebuild after rejection: accepted ramps must not depend on rejected geometry changes. + while (ramps.length) { + const world = new RAPIER.World({ x: 0, y: -18, z: 0 }); + try { + for (const source of geometries) { + const geometry = source.clone(); + smoothDungeonStairGeometry(geometry, ramps); + world.createCollider(RAPIER.ColliderDesc.trimesh( + Float32Array.from(geometry.getAttribute("position").array), + Uint32Array.from(geometry.index?.array ?? Array.from({ length: geometry.getAttribute("position").count }, (_, index) => index)), + )); + geometry.dispose(); + } + world.step(); + const accepted = ramps.filter((ramp) => rampTraversable(world, ramp)); + if (accepted.length === ramps.length) break; + ramps = accepted; + } finally { world.free(); } + } + return ramps; + }; + let ramps = verify(candidates); + // If a long merged flight was unsafe, retain independently verified shorter + // flights instead. Do not let fallback fragments reshape an accepted plane. + const fallback = detectDungeonStairRamps(risers, false).filter((ramp) => !ramps.some((accepted) => { + const within = (a: DungeonTraversalRamp, b: DungeonTraversalRamp) => [b.start, b.end, + b.start.map((v, i) => (v + b.end[i]) / 2)].some((point) => { + const height = dungeonRampHeight(a, point[0], point[2]); + return height !== null && Math.abs(point[1] - height) <= 0.85; + }); + return within(accepted, ramp) || within(ramp, accepted); + })).map((ramp, index) => ({ ...ramp, id: `stair-fallback-${index + 1}` })); + ramps = verify([...ramps, ...fallback]); + output[dungeon.id] = { + collision: collisionAssets.map(({ fileName, url, checksum }) => ({ fileName, url, checksum })), + navigation: navigationAsset, + ramps, candidateCount: candidates.length + fallback.length, + rejectedCount: candidates.length + fallback.length - ramps.length, + }; + if (ramps.length && navigationAsset) { + const navigation = await readDungeonGeometry(assetFile(navigationAsset)); + const largest = navigation.sort((a, b) => b.getAttribute("position").count - a.getAttribute("position").count)[0]; + (output[dungeon.id] as { ramps: DungeonTraversalRamp[] }).ramps = dungeonStairNavigationBridges(largest, ramps); + navigation.forEach((geometry) => geometry.dispose()); + } + console.log(`${dungeon.id}: ${ramps.length}/${candidates.length + fallback.length} collision-verified stair proxies`); + geometries.forEach((geometry) => geometry.dispose()); +} +if (process.argv.includes("--write") || process.argv.includes("--update")) { + if (requested && !process.argv.includes("--update")) throw new Error("Refusing to replace the fleet catalog with a single-dungeon scan."); + writeFileSync(outputFile, `${JSON.stringify({ schemaVersion: 1, dungeons: output }, null, 2)}\n`); +} diff --git a/scripts/dungeon-pipeline/wow-stair-geometry.ts b/scripts/dungeon-pipeline/wow-stair-geometry.ts new file mode 100644 index 00000000..08dda72d --- /dev/null +++ b/scripts/dungeon-pipeline/wow-stair-geometry.ts @@ -0,0 +1,249 @@ +import { NodeIO } from "@gltf-transform/core"; +import { ALL_EXTENSIONS } from "@gltf-transform/extensions"; +import { MeshoptDecoder } from "meshoptimizer"; +import { BufferAttribute, BufferGeometry, Float32BufferAttribute, Matrix4 } from "three"; +import type { DungeonTraversalRamp } from "../../src/game/dungeonTypes"; +import { dungeonRampHeight } from "../../src/game/dungeonTraversalRamps.ts"; + +const io = new NodeIO().registerExtensions(ALL_EXTENSIONS) + .registerDependencies({ "meshopt.decoder": MeshoptDecoder }); + +/** Decode exactly as GLTFLoader does, expanding quantized coordinates before baking transforms. */ +export async function readDungeonGeometry(file: string): Promise { + await MeshoptDecoder.ready; + const document = await io.read(file); + const geometries: BufferGeometry[] = []; + for (const node of document.getRoot().listNodes()) { + for (const primitive of node.getMesh()?.listPrimitives() ?? []) { + const accessor = primitive.getAttribute("POSITION"); + if (!accessor?.getArray()) continue; + const source = new BufferAttribute(accessor.getArray()!, 3, accessor.getNormalized()); + const positions = new Float32Array(source.count * 3); + for (let index = 0; index < source.count; index += 1) { + positions.set([source.getX(index), source.getY(index), source.getZ(index)], index * 3); + } + const geometry = new BufferGeometry(); + geometry.setAttribute("position", new Float32BufferAttribute(positions, 3)); + const indices = primitive.getIndices()?.getArray(); + if (indices) geometry.setIndex(Array.from(indices)); + geometry.applyMatrix4(new Matrix4().fromArray(node.getWorldMatrix())); + geometries.push(geometry); + } + } + return geometries; +} + +interface Riser { + direction: number; + along: number; + low: number; + high: number; + left: number; + right: number; +} + +/** Repeated short vertical faces, not arbitrary slopes or nearby disconnected floors. */ +export function dungeonStairRisers(geometry: BufferGeometry): Riser[] { + const p = geometry.getAttribute("position"); + const indices = geometry.index?.array ?? Array.from({ length: p.count }, (_, i) => i); + const faces = new Map(); + for (let offset = 0; offset + 2 < indices.length; offset += 3) { + const vertices = [indices[offset], indices[offset + 1], indices[offset + 2]]; + const points = vertices.map((i) => [p.getX(i), p.getY(i), p.getZ(i)]); + const [a, b, c] = points; + const ab = b.map((v, i) => v - a[i]); + const ac = c.map((v, i) => v - a[i]); + const nx = ab[1] * ac[2] - ab[2] * ac[1]; + const ny = ab[2] * ac[0] - ab[0] * ac[2]; + const nz = ab[0] * ac[1] - ab[1] * ac[0]; + if (Math.hypot(nx, nz) < 0.1 || Math.abs(ny) / Math.hypot(nx, ny, nz) > 0.025) continue; + const low = Math.min(...points.map((v) => v[1])); + const high = Math.max(...points.map((v) => v[1])); + if (high - low < 0.08 || high - low > 0.82) continue; + // Opposite winding still describes the same plane. Quantization permits ~1 degree noise. + const direction = ((Math.round(Math.atan2(nz, nx) / Math.PI * 180) % 180) + 180) % 180; + const angle = direction * Math.PI / 180; + const along = points.reduce((sum, v) => sum + v[0] * Math.cos(angle) + v[2] * Math.sin(angle), 0) / 3; + const across = points.map((v) => -v[0] * Math.sin(angle) + v[2] * Math.cos(angle)); + const left = Math.min(...across); + const right = Math.max(...across); + if (right - left < 0.9) continue; + const key = [direction, Math.round(along / 0.04), Math.round(low / 0.04), Math.round(high / 0.04)].join(":"); + const previous = faces.get(key); + if (previous && Math.min(previous.right, right) - Math.max(previous.left, left) >= -0.04) { + previous.left = Math.min(previous.left, left); + previous.right = Math.max(previous.right, right); + } else faces.set(`${key}${previous ? `:${Math.round(left / 0.04)}` : ""}`, { direction, along, low, high, left, right }); + } + return [...faces.values()]; +} + +export function detectDungeonStairRamps(risers: Riser[], merge = true): DungeonTraversalRamp[] { + const ramps: DungeonTraversalRamp[] = []; + const used = new Set(); + const sorted = [...risers].sort((a, b) => a.direction - b.direction || a.along - b.along || a.low - b.low); + for (const first of sorted) { + if (used.has(first)) continue; + const neighbors = sorted.filter((r) => Math.min(Math.abs(r.direction - first.direction), 180 - Math.abs(r.direction - first.direction)) <= 2) + .map((source) => { + const difference = (source.direction - first.direction) * Math.PI / 180; + const across = (source.left + source.right) / 2; + const halfWidth = (source.right - source.left) / 2 * Math.abs(Math.cos(difference)); + const center = source.along * Math.sin(difference) + across * Math.cos(difference); + return { source, ...source, along: source.along * Math.cos(difference) - across * Math.sin(difference), left: center - halfWidth, right: center + halfWidth }; + }).sort((a, b) => a.along - b.along || a.low - b.low); + const chain = [first]; + const chainSources = [first]; + let sign = 0; + while (true) { + const current = chain[chain.length - 1]; + const next = neighbors.find((candidate) => { + if (used.has(candidate.source)) return false; + const run = candidate.along - current.along; + const rise = candidate.low - current.low; + const overlap = Math.min(...chain.map((r) => r.right), candidate.right) + - Math.max(...chain.map((r) => r.left), candidate.left); + return run > 0.12 && run < 2.5 && Math.abs(rise) >= 0.08 && Math.abs(rise) <= 0.82 + && Math.abs(rise) / run < 0.85 && overlap >= 1 + && (!sign || Math.sign(rise) === sign) + && Math.min(Math.abs(candidate.low - current.high), Math.abs(candidate.high - current.low)) < 0.22; + }); + if (!next) break; + if (chain.length > 1) { + const run = chain[1].along - first.along; + const rise = chain[1].low - first.low; + if (Math.abs(next.low - (first.low + rise / run * (next.along - first.along))) > 0.1) break; + } + chain.push(next); + chainSources.push(next.source); + sign = Math.sign(next.low - current.low); + } + if (chain.length < 3) continue; + const last = chain[chain.length - 1]; + const run = (last.along - first.along) / (chain.length - 1); + const rise = (last.low - first.low) / (chain.length - 1); + const left = Math.max(...chain.map((r) => r.left)); + const right = Math.min(...chain.map((r) => r.right)); + const across = (left + right) / 2; + const angle = first.direction * Math.PI / 180; + const point = (along: number, y: number): [number, number, number] => [ + along * Math.cos(angle) - across * Math.sin(angle), y, + along * Math.sin(angle) + across * Math.cos(angle), + ]; + // Meet the lower/upper landings, including the first/last riser, without a lip. + ramps.push({ + id: `stair-flight-${ramps.length + 1}`, + start: point(first.along - (sign < 0 ? run : 0.04), sign < 0 ? first.high : first.low), + end: point(last.along + (sign > 0 ? run : 0.04), sign > 0 ? last.high : last.low), + width: right - left + 0.02, + }); + chainSources.forEach((r) => used.add(r)); + } + return merge ? mergeDungeonStairRamps(ramps) : ramps; +} + +/** Keep one plane per continuous flight; independent overlapping fragments introduce new lips. */ +export function mergeDungeonStairRamps(input: DungeonTraversalRamp[]): DungeonTraversalRamp[] { + const ramps = [...input].sort((a, b) => Math.hypot(b.end[0] - b.start[0], b.end[2] - b.start[2]) + - Math.hypot(a.end[0] - a.start[0], a.end[2] - a.start[2])); + const result: DungeonTraversalRamp[] = []; + for (const ramp of ramps) { + let combined = false; + for (let index = 0; index < result.length; index += 1) { + const previous = result[index]; + const dx = previous.end[0] - previous.start[0]; const dz = previous.end[2] - previous.start[2]; + const length = Math.hypot(dx, dz); + const rdX = ramp.end[0] - ramp.start[0]; const rdZ = ramp.end[2] - ramp.start[2]; + const rLength = Math.hypot(rdX, rdZ); + if ((dx * rdX + dz * rdZ) / length / rLength < 0.999) continue; + const along = (p: readonly number[]) => ((p[0] - previous.start[0]) * dx + (p[2] - previous.start[2]) * dz) / length; + const across = (p: readonly number[]) => (-(p[0] - previous.start[0]) * dz + (p[2] - previous.start[2]) * dx) / length; + const start = along(ramp.start); const end = along(ramp.end); + const acrossOffset = (across(ramp.start) + across(ramp.end)) / 2; + const left = Math.max(-previous.width / 2, acrossOffset - ramp.width / 2); + const right = Math.min(previous.width / 2, acrossOffset + ramp.width / 2); + if (right - left < 1 || start > length + 1.5 || end < -1.5) continue; + const slope = (previous.end[1] - previous.start[1]) / length; + const rSlope = (ramp.end[1] - ramp.start[1]) / rLength; + if (Math.abs(slope - rSlope) > 0.12 + || Math.abs(ramp.start[1] - (previous.start[1] + start * slope)) > 0.32 + || Math.abs(ramp.end[1] - (previous.start[1] + end * slope)) > 0.32) continue; + const low = Math.min(0, start); const high = Math.max(length, end); + const center = (left + right) / 2; + const point = (t: number, y: number): [number, number, number] => [ + previous.start[0] + dx / length * t - dz / length * center, y, + previous.start[2] + dz / length * t + dx / length * center, + ]; + result[index] = { + id: previous.id, + start: point(low, start < 0 ? ramp.start[1] : previous.start[1]), + end: point(high, end > length ? ramp.end[1] : previous.end[1]), + width: right - left, + }; + combined = true; + break; + } + if (!combined) result.push(ramp); + } + return result.map((ramp, index) => ({ ...ramp, id: `stair-flight-${index + 1}` })); +} + +/** Fill only separate Recast boundary portals wholly inside a verified stair footprint. */ +export function dungeonStairNavigationBridges(geometry: BufferGeometry, ramps: DungeonTraversalRamp[]): DungeonTraversalRamp[] { + const p = geometry.getAttribute("position"); + const indices = geometry.index?.array ?? Array.from({ length: p.count }, (_, i) => i); + const parents = Array.from({ length: p.count }, (_, i) => i); + const root = (i: number): number => { + while (parents[i] !== i) { parents[i] = parents[parents[i]]; i = parents[i]; } + return i; + }; + const weld = new Map(); + const point = (i: number): [number, number, number] => [p.getX(i), p.getY(i), p.getZ(i)]; + for (let i = 0; i < p.count; i += 1) { + const key = point(i).map((v) => Math.round(v / 0.002)).join(":"); + const previous = weld.get(key); + if (previous !== undefined) parents[i] = previous; + else weld.set(key, i); + } + const edges = new Map(); + for (let offset = 0; offset + 2 < indices.length; offset += 3) { + const triangle = [indices[offset], indices[offset + 1], indices[offset + 2]]; + for (let side = 0; side < 3; side += 1) { + const a = triangle[side]; const b = triangle[(side + 1) % 3]; + const wa = weld.get(point(a).map((v) => Math.round(v / 0.002)).join(":"))!; + const wb = weld.get(point(b).map((v) => Math.round(v / 0.002)).join(":"))!; + parents[root(a)] = root(b); + const key = wa < wb ? `${wa}:${wb}` : `${wb}:${wa}`; + const edge = edges.get(key); + if (edge) edge.count += 1; + else edges.set(key, { a, b, count: 1 }); + } + } + const boundary = [...edges.values()].filter((e) => e.count === 1); + return ramps.map((ramp) => { + const dx = ramp.end[0] - ramp.start[0]; const dz = ramp.end[2] - ramp.start[2]; + const length = Math.hypot(dx, dz); + const along = (v: number[]) => ((v[0] - ramp.start[0]) * dx + (v[2] - ramp.start[2]) * dz) / length; + const across = (v: number[]) => (-(v[0] - ramp.start[0]) * dz + (v[2] - ramp.start[2]) * dx) / length; + const portals = boundary.flatMap((e) => { + const points = [point(e.a), point(e.b)].sort((a, b) => across(a) - across(b)); + if (points.some((v) => { + const y = dungeonRampHeight(ramp, v[0], v[2]); + return y === null || Math.abs(v[1] - y) > 0.8; + })) return []; + if (Math.abs(along(points[0]) - along(points[1])) > 0.15 || across(points[1]) - across(points[0]) < 0.7) return []; + return [{ ...e, points: points as [[number, number, number], [number, number, number]], along: (along(points[0]) + along(points[1])) / 2 }]; + }).sort((a, b) => a.along - b.along); + const bridges = []; + for (let index = 1; index < portals.length; index += 1) { + const a = portals[index - 1]; const b = portals[index]; + const gap = b.along - a.along; + if (gap < 0.15 || gap > 2.5 || root(a.a) === root(b.a)) continue; + const overlap = Math.min(across(a.points[1]), across(b.points[1])) - Math.max(across(a.points[0]), across(b.points[0])); + if (overlap < 0.7) continue; + bridges.push({ linkId: `${ramp.id}-seam-${bridges.length + 1}`, startPortal: a.points, endPortal: b.points }); + parents[root(a.a)] = root(b.a); + } + return bridges.length ? { ...ramp, navigationBridges: bridges } : ramp; + }); +} diff --git a/server/database.mjs b/server/database.mjs index 8f7edbd6..9db238c4 100644 --- a/server/database.mjs +++ b/server/database.mjs @@ -11,6 +11,7 @@ import { import { arenaRatingBand, calculateArenaRating, clampArenaRating } from "./arena-rules.mjs"; import { arenaRoleForSpecialization } from "./arena-specializations.mjs"; import { createDungeonReportRepository } from "./dungeon-reports.mjs"; +import { createDungeonLeaderboardRepository } from "./dungeon-leaderboards.mjs"; const scrypt = promisify(scryptCallback); @@ -897,6 +898,7 @@ export function openGameDatabase(options = {}) { return { databasePath, ...createDungeonReportRepository(database, { isGameMaster, ErrorType: GameDatabaseError }), + ...createDungeonLeaderboardRepository(database, { ErrorType: GameDatabaseError }), register, login, authenticate, diff --git a/server/dungeon-leaderboards.mjs b/server/dungeon-leaderboards.mjs new file mode 100644 index 00000000..70986d69 --- /dev/null +++ b/server/dungeon-leaderboards.mjs @@ -0,0 +1,112 @@ +import catalog from "../src/game/generated/dungeonLeaderboardCatalog.generated.json" with { type: "json" }; + +const dungeons = new Map(catalog.dungeons.map((entry) => [entry.id, entry])); +const classes = new Set(catalog.classes.map((entry) => entry.id)); + +export function createDungeonLeaderboardRepository(database, { ErrorType }) { + database.exec(` + CREATE TABLE IF NOT EXISTS dungeon_runs ( + id TEXT PRIMARY KEY, + dungeon_id TEXT NOT NULL, + difficulty_id TEXT NOT NULL, + duration_ms INTEGER NOT NULL CHECK (duration_ms > 0), + full_clear INTEGER NOT NULL CHECK (full_clear IN (0, 1)), + finished_at INTEGER NOT NULL, + party_json TEXT NOT NULL, + bosses_json TEXT NOT NULL, + submitted_by_account_id TEXT REFERENCES accounts(id) ON DELETE SET NULL, + received_at INTEGER NOT NULL + ) STRICT; + CREATE INDEX IF NOT EXISTS dungeon_runs_times_idx + ON dungeon_runs(dungeon_id, difficulty_id, duration_ms, finished_at, id); + CREATE INDEX IF NOT EXISTS dungeon_runs_full_times_idx + ON dungeon_runs(dungeon_id, difficulty_id, full_clear, duration_ms); + `); + const byId = database.prepare("SELECT * FROM dungeon_runs WHERE id = ?"); + const insert = database.prepare(`INSERT INTO dungeon_runs + (id, dungeon_id, difficulty_id, duration_ms, full_clear, finished_at, party_json, bosses_json, submitted_by_account_id, received_at) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`); + const fail = (message) => { throw new ErrorType(message, 400, "invalid_dungeon_run"); }; + const record = (row) => ({ id: row.id, dungeonId: row.dungeon_id, difficultyId: row.difficulty_id, + durationMs: Number(row.duration_ms), finishedAt: Number(row.finished_at), fullClear: row.full_clear === 1, + party: JSON.parse(row.party_json), killedBossIds: JSON.parse(row.bosses_json) }); + function selection(input) { + const dungeon = dungeons.get(input.dungeonId); + if (!dungeon) fail("Choose a known dungeon."); + const difficultyId = input.difficultyId || dungeon.defaultDifficultyId; + if (!dungeon.difficulties.some((entry) => entry.id === difficultyId)) fail("Choose a supported dungeon difficulty."); + const difficulty = dungeon.difficulties.find((entry) => entry.id === difficultyId); + return { dungeon, difficultyId, difficulty }; + } + function placement(run) { + const counts = database.prepare(`SELECT COUNT(*) AS total, + COALESCE(SUM(CASE WHEN duration_ms < ? THEN 1 ELSE 0 END), 0) AS faster, + COALESCE(SUM(full_clear), 0) AS full_total, + COALESCE(SUM(CASE WHEN full_clear = 1 AND duration_ms < ? THEN 1 ELSE 0 END), 0) AS full_faster + FROM dungeon_runs WHERE dungeon_id = ? AND difficulty_id = ?`) + .get(run.durationMs, run.durationMs, run.dungeonId, run.difficultyId); + return { finalRank: Number(counts.faster) + 1, finalTotal: Number(counts.total), + fullRank: run.fullClear ? Number(counts.full_faster) + 1 : null, fullTotal: Number(counts.full_total) }; + } + return { + submitDungeonRun(account, input) { + if (!input || typeof input !== "object" || Array.isArray(input)) fail("Provide a completed dungeon run."); + const { dungeon, difficultyId, difficulty } = selection(input); + if (!difficulty.finalBossId) fail("This dungeon does not have a configured final boss yet."); + if (typeof input.id !== "string" || !/^[A-Za-z0-9_-]{8,100}$/.test(input.id)) fail("Provide a valid run id."); + if (!Number.isSafeInteger(input.durationMs) || input.durationMs < 1 || input.durationMs > 7 * 24 * 60 * 60 * 1000) + fail("Provide a valid completion time."); + if (!Number.isSafeInteger(input.finishedAt) || input.finishedAt < 0 || input.finishedAt > Date.now() + 60_000) + fail("Provide a valid finish timestamp."); + if (!Array.isArray(input.killedBossIds) || input.killedBossIds.length > 100 + || input.killedBossIds.some((id) => !difficulty.bossIds.includes(id))) fail("Provide bosses from this dungeon and difficulty."); + const killedBossIds = [...new Set(input.killedBossIds)].sort(); + if (!killedBossIds.includes(difficulty.finalBossId)) fail("The final boss must be defeated before recording a run."); + if (!Array.isArray(input.party) || input.party.length < 1 || input.party.length > 5) fail("Provide the run's party."); + const party = input.party.map((member) => { + if (!member || typeof member !== "object" || typeof member.name !== "string" || !member.name.trim() + || member.name.length > 40 || !classes.has(member.classId) + || !Number.isInteger(member.level) || member.level < 1 || member.level > 80 + || !["tank", "healer", "damage"].includes(member.role) || typeof member.npc !== "boolean" + || (member.secondaryClassId !== undefined && !classes.has(member.secondaryClassId))) fail("Provide valid party classes and levels."); + return { name: member.name.trim(), classId: member.classId, level: member.level, + role: member.role, npc: member.npc, + ...(member.secondaryClassId ? { secondaryClassId: member.secondaryClassId } : {}) }; + }); + const fullClear = difficulty.bossIds.every((id) => killedBossIds.includes(id)); + const run = { id: input.id, dungeonId: dungeon.id, difficultyId, durationMs: input.durationMs, + finishedAt: input.finishedAt, killedBossIds, party, fullClear }; + const existing = byId.get(run.id); + if (existing) { + if (JSON.stringify(record(existing)) !== JSON.stringify({ id: run.id, dungeonId: run.dungeonId, + difficultyId: run.difficultyId, durationMs: run.durationMs, finishedAt: run.finishedAt, + fullClear: run.fullClear, party: run.party, killedBossIds: run.killedBossIds })) + throw new ErrorType("That run id already belongs to a different result.", 409, "run_conflict"); + } else insert.run(run.id, run.dungeonId, run.difficultyId, run.durationMs, fullClear ? 1 : 0, + run.finishedAt, JSON.stringify(party), JSON.stringify(killedBossIds), account?.id ?? null, Date.now()); + return { run, placement: placement(run) }; + }, + getDungeonRun(id) { + const row = byId.get(id); + if (!row) throw new ErrorType("That dungeon run is not recorded yet.", 404, "run_not_found"); + const run = record(row); + return { run, placement: placement(run) }; + }, + listDungeonLeaderboard(input) { + const { dungeon, difficultyId } = selection(input); + const filter = input.clear || "final"; + if (!["full", "final"].includes(filter)) fail("Choose full dungeon clears or final boss clears."); + const offset = Number(input.offset ?? 0); + const limit = Number(input.limit ?? 20); + if (!Number.isSafeInteger(offset) || offset < 0 || offset > 1_000_000 + || !Number.isSafeInteger(limit) || limit < 1 || limit > 100) fail("Choose a valid leaderboard page."); + const condition = filter === "full" ? "AND full_clear = 1" : ""; + const rows = database.prepare(`SELECT *, RANK() OVER (ORDER BY duration_ms) AS place FROM dungeon_runs + WHERE dungeon_id = ? AND difficulty_id = ? ${condition} + ORDER BY duration_ms, finished_at, id LIMIT ? OFFSET ?`).all(dungeon.id, difficultyId, limit, offset); + const total = database.prepare(`SELECT COUNT(*) AS total FROM dungeon_runs + WHERE dungeon_id = ? AND difficulty_id = ? ${condition}`).get(dungeon.id, difficultyId).total; + return { entries: rows.map((row) => ({ ...record(row), rank: Number(row.place) })), total: Number(total) }; + }, + }; +} diff --git a/server/dungeon-leaderboards.test.mjs b/server/dungeon-leaderboards.test.mjs new file mode 100644 index 00000000..6b146f01 --- /dev/null +++ b/server/dungeon-leaderboards.test.mjs @@ -0,0 +1,89 @@ +import assert from "node:assert/strict"; +import { mkdtempSync, rmSync } from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { openGameDatabase } from "./database.mjs"; +import { createGameServer } from "./server.mjs"; +import catalog from "../src/game/generated/dungeonLeaderboardCatalog.generated.json" with { type: "json" }; + +function run(id, durationMs, fullClear = true, dungeonId = "deadmines", difficultyId) { + const dungeon = catalog.dungeons.find((entry) => entry.id === dungeonId); + return { id, dungeonId, difficultyId: difficultyId ?? dungeon.defaultDifficultyId, durationMs, + finishedAt: 1700000000000, killedBossIds: fullClear ? dungeon.bosses.map((boss) => boss.id) : [dungeon.finalBossId], + party: [{ name: "Tester", classId: "priest", level: 20, role: "healer", npc: false }, + { name: "Garrick", classId: "warrior", level: 20, role: "tank", npc: true }] }; +} +async function withDatabase(callback) { + const directory = mkdtempSync(path.join(os.tmpdir(), "healer-man-leaderboard-test-")); + let database = openGameDatabase({ dataDir: directory }); + try { await callback(database, () => { database.close(); database = openGameDatabase({ dataDir: directory }); return database; }); } + finally { database.close(); rmSync(directory, { recursive: true, force: true }); } +} +test("persists ranked party/boss records, derives all-boss eligibility, handles ties, and retries without duplicates", () => withDatabase((database, reopen) => { + database.submitDungeonRun(null, run("run-fastest", 1000, false)); + database.submitDungeonRun(null, run("run-full-one", 2000)); + database.submitDungeonRun(null, run("run-full-tie", 2000)); + const last = run("run-slowest", 3000); + const result = database.submitDungeonRun(null, last); + assert.deepEqual(result.placement, { finalRank: 4, finalTotal: 4, fullRank: 3, fullTotal: 3 }); + assert.deepEqual(database.submitDungeonRun(null, last), result); + assert.throws(() => database.submitDungeonRun(null, { ...last, durationMs: 5 }), { status: 409 }); + const final = database.listDungeonLeaderboard({ dungeonId: "deadmines", clear: "final" }); + assert.deepEqual(final.entries.map((entry) => entry.rank), [1, 2, 2, 4]); + const full = database.listDungeonLeaderboard({ dungeonId: "deadmines", clear: "full" }); + assert.deepEqual(full.entries.map((entry) => entry.rank), [1, 1, 3]); + assert.deepEqual(full.entries[0].party, last.party); + assert.equal(full.entries[0].killedBossIds.length, last.killedBossIds.length); + const page = database.listDungeonLeaderboard({ dungeonId: "deadmines", clear: "final", limit: 2, offset: 2 }); + assert.equal(page.total, 4); assert.deepEqual(page.entries.map((entry) => entry.rank), [2, 4]); + database = reopen(); + assert.deepEqual(database.getDungeonRun(last.id), result); +})); +test("validates final boss, dungeon/difficulty, duration and party without trusting client full-clear flags", () => withDatabase((database) => { + const valid = run("valid-run-id", 1000, false); + for (const invalid of [{ ...valid, durationMs: 0 }, { ...valid, durationMs: Infinity }, + { ...valid, killedBossIds: [] }, { ...valid, killedBossIds: ["fake-boss"] }, + { ...valid, dungeonId: "unknown" }, { ...valid, difficultyId: "fake" }, + { ...valid, party: [] }, { ...valid, party: [{ ...valid.party[0], classId: "fake" }] }, + { ...valid, party: [{ ...valid.party[0], level: 81 }] }]) + assert.throws(() => database.submitDungeonRun(null, invalid), { code: "invalid_dungeon_run" }); + assert.equal(database.submitDungeonRun(null, { ...valid, fullClear: true }).run.fullClear, false); + const caves = run("barren-caves-test", 2000, true, "barren-caves"); + caves.party[0].classId = "rom-priest"; + assert.equal(database.submitDungeonRun(null, caves).placement.fullRank, 1); + assert.equal(database.listDungeonLeaderboard({ dungeonId: "deadmines" }).total, 1); + assert.throws(() => database.listDungeonLeaderboard({ dungeonId: "deadmines", clear: "bad" }), { status: 400 }); + assert.throws(() => database.listDungeonLeaderboard({ dungeonId: "deadmines", offset: -1 }), { status: 400 }); +})); +test("public HTTP leaderboards accept completion records and return results with rank while rejecting unfinished runs", async () => { + const directory = mkdtempSync(path.join(os.tmpdir(), "healer-man-leaderboard-http-")); + const { server } = createGameServer({ dataDir: directory, enableShelvedModes: false }); + try { + await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); + const base = `http://127.0.0.1:${server.address().port}`; + const payload = run("http-caves-clear", 15555, true, "barren-caves"); + const response = await fetch(`${base}/api/dungeon-runs`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }); + assert.equal(response.status, 201); + const result = await response.json(); assert.equal(result.placement.finalRank, 1); + const leaderboard = await fetch(`${base}/api/dungeon-leaderboards?dungeon=barren-caves&clear=full`); + assert.equal(leaderboard.status, 200); assert.equal((await leaderboard.json()).entries[0].durationMs, 15555); + assert.deepEqual(await (await fetch(`${base}/api/dungeon-runs/${payload.id}`)).json(), result); + const bad = await fetch(`${base}/api/dungeon-runs`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...payload, id: "unfinished-run", killedBossIds: [] }) }); + assert.equal(bad.status, 400); + const unauthorized = await fetch(`${base}/api/dungeon-runs`, { method: "POST", headers: { Authorization: "Bearer invalid", "Content-Type": "application/json" }, body: JSON.stringify(payload) }); + assert.equal(unauthorized.status, 401); + } finally { await new Promise((resolve) => server.close(resolve)); rmSync(directory, { recursive: true, force: true }); } +}); + +test("separates normal and heroic times and counts only bosses that exist on each difficulty", () => withDatabase((database) => { + const dungeon = catalog.dungeons.find((entry) => entry.difficulties.length > 1 && entry.difficulties.every((difficulty) => difficulty.finalBossId)); + for (const difficulty of dungeon.difficulties) { + const payload = run(`difficulty-${difficulty.id}-test`, 1000, true, dungeon.id, difficulty.id); + payload.killedBossIds = difficulty.bossIds; + const result = database.submitDungeonRun(null, payload); + assert.equal(result.run.fullClear, true); + assert.equal(result.placement.finalRank, 1); assert.equal(result.placement.finalTotal, 1); + assert.equal(database.listDungeonLeaderboard({ dungeonId: dungeon.id, difficultyId: difficulty.id, clear: "full" }).total, 1); + } +})); diff --git a/server/server.mjs b/server/server.mjs index d973c891..02de3449 100644 --- a/server/server.mjs +++ b/server/server.mjs @@ -182,6 +182,7 @@ export function createGameServer(options = {}) { const arenaService = createArenaService({ database: gameDatabase, autoPump: enableShelvedModes }); const allowAuthRequest = createAuthLimiter(); const allowReportRequest = createAuthLimiter(); + const allowDungeonRunRequest = createAuthLimiter(); gameDatabase.pruneExpiredSessions(); const server = createServer(async (request, response) => { @@ -240,6 +241,25 @@ export function createGameServer(options = {}) { json(response, 200, { account: gameDatabase.authenticate(bearerToken(request)) }); return; } + if (url.pathname === "/api/dungeon-leaderboards" && request.method === "GET") { + json(response, 200, gameDatabase.listDungeonLeaderboard({ dungeonId: url.searchParams.get("dungeon"), + difficultyId: url.searchParams.get("difficulty"), clear: url.searchParams.get("clear"), + offset: url.searchParams.get("offset"), limit: url.searchParams.get("limit") })); + return; + } + if (url.pathname === "/api/dungeon-runs" && request.method === "POST") { + const token = bearerToken(request); + const account = token ? gameDatabase.authenticate(token) : null; + if (!allowDungeonRunRequest(account?.id ?? request.socket.remoteAddress)) + throw new GameDatabaseError("Too many run submissions. Please try again shortly.", 429, "rate_limited"); + json(response, 201, gameDatabase.submitDungeonRun(account, await readJson(request, 32 * 1024))); + return; + } + const dungeonRunMatch = /^\/api\/dungeon-runs\/([A-Za-z0-9_-]{8,100})$/.exec(url.pathname); + if (dungeonRunMatch && request.method === "GET") { + json(response, 200, gameDatabase.getDungeonRun(dungeonRunMatch[1])); + return; + } if (url.pathname === "/api/dungeon-reports" && request.method === "GET") { json(response, 200, gameDatabase.listDungeonReports({ status: url.searchParams.get("status"), diff --git a/src/GameRuntime.tsx b/src/GameRuntime.tsx index ff65fa62..dc823ebc 100644 --- a/src/GameRuntime.tsx +++ b/src/GameRuntime.tsx @@ -20,6 +20,8 @@ import { PauseMenu } from "./ui/PauseMenu"; import { SceneErrorBoundary } from "./ui/SceneErrorBoundary"; import { OnlineSessionBridge } from "./game/OnlineSessionBridge"; import { DungeonRunCheckpointBridge } from "./game/DungeonRunCheckpointBridge"; +import { DungeonRunBridge } from "./game/DungeonRunBridge"; +import { DungeonRunResults } from "./ui/DungeonRunResults"; import { DungeonEncounterEffectBridge } from "./game/DungeonEncounterEffectBridge"; import { interactWithActiveDungeonObject } from "./game/dungeonWorldInteraction"; import { DungeonInteractionPrompt } from "./ui/DungeonInteractionPrompt"; @@ -37,7 +39,7 @@ function InputBridge() { useEffect(() => installInput({ desktopActionBarsActive: () => currentUiDevice() === "pc" && activeUiLayout().wowBars, - inputSuspended: () => ["ui-layout", "keybinds"].includes(useGameStore.getState().overlay ?? ""), + inputSuspended: () => ["ui-layout", "keybinds", "dungeon-results"].includes(useGameStore.getState().overlay ?? ""), getBindings: (device) => { const preferences = useUiLayoutStore.getState().preferences; const bindings = profileInputBindings(preferences, device); @@ -115,6 +117,7 @@ export default function GameRuntime() { + @@ -122,6 +125,7 @@ export default function GameRuntime() { + diff --git a/src/app/dungeonLeaderboardClient.test.ts b/src/app/dungeonLeaderboardClient.test.ts new file mode 100644 index 00000000..aec5e37a --- /dev/null +++ b/src/app/dungeonLeaderboardClient.test.ts @@ -0,0 +1,44 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { localDungeonLeaderboard, localDungeonRunPlacement, saveLocalDungeonRun, submitDungeonRun } from "./dungeonLeaderboardClient"; +import type { DungeonRunRecord } from "../game/dungeonRunTypes"; +import catalog from "../game/generated/dungeonLeaderboardCatalog.generated.json"; + +const dungeon = catalog.dungeons.find((entry) => entry.id === "deadmines")!; +const record = (id: string, durationMs: number, fullClear = true): DungeonRunRecord => ({ + id, dungeonId: dungeon.id, difficultyId: dungeon.defaultDifficultyId, durationMs, finishedAt: 100000, + party: [{ name: "Tester", classId: "priest", level: 20, role: "healer", npc: false }], + killedBossIds: fullClear ? dungeon.bosses.map((boss) => boss.id) : [dungeon.finalBossId], fullClear, +}); +beforeEach(() => { + const values = new Map(); + vi.stubGlobal("localStorage", { getItem: (key: string) => values.get(key) ?? null, + setItem: (key: string, value: string) => values.set(key, value) }); +}); +afterEach(() => vi.unstubAllGlobals()); + +describe("offline dungeon leaderboard", () => { + it("persists full/final filters, ties, pages, and the placement of a run outside the first page", () => { + for (let index = 0; index < 25; index += 1) saveLocalDungeonRun(record(`offline-${index}`, 1000 + index * 100)); + const fastest = record("offline-fastest", 500, false); + saveLocalDungeonRun(fastest); saveLocalDungeonRun(fastest); + saveLocalDungeonRun(record("offline-tie", 1000)); + const all = localDungeonLeaderboard(dungeon.id, dungeon.defaultDifficultyId, "final"); + expect(all.total).toBe(27); expect(all.entries[0].id).toBe(fastest.id); + expect(all.entries.slice(0, 3).map((entry) => entry.rank)).toEqual([1, 2, 2]); + expect(localDungeonLeaderboard(dungeon.id, dungeon.defaultDifficultyId, "full").total).toBe(26); + const page = localDungeonLeaderboard(dungeon.id, dungeon.defaultDifficultyId, "final", 20); + expect(page.entries).toHaveLength(7); + expect(localDungeonRunPlacement(record("offline-24", 3400))).toEqual({ finalRank: 27, finalTotal: 27, fullRank: 26, fullTotal: 26 }); + }); + it("retains the local time after a failed upload and uses the same run id when retried", async () => { + const run = record("offline-retry-test", 4321); + saveLocalDungeonRun(run); + const fetch = vi.fn().mockRejectedValueOnce(new Error("offline")) + .mockResolvedValueOnce(new Response(JSON.stringify({ placement: { finalRank: 2, finalTotal: 3, fullRank: 1, fullTotal: 2 } }), { status: 201 })); + vi.stubGlobal("fetch", fetch); + await expect(submitDungeonRun(run, null)).rejects.toThrow(); + expect(localDungeonLeaderboard(dungeon.id, dungeon.defaultDifficultyId, "final").entries[0].durationMs).toBe(4321); + await expect(submitDungeonRun(run, null)).resolves.toMatchObject({ finalRank: 2 }); + expect(fetch.mock.calls.map((call) => JSON.parse(call[1].body).id)).toEqual([run.id, run.id]); + }); +}); diff --git a/src/app/dungeonLeaderboardClient.ts b/src/app/dungeonLeaderboardClient.ts new file mode 100644 index 00000000..f4cfb00b --- /dev/null +++ b/src/app/dungeonLeaderboardClient.ts @@ -0,0 +1,102 @@ +import { requestOnlineJson } from "./onlineAccountClient"; +import type { PlayerSession } from "./types"; +import catalog from "../game/generated/dungeonLeaderboardCatalog.generated.json"; +import { normalizeDungeonRunProgress, type DungeonClearFilter, type DungeonLeaderboard, + type DungeonRunPlacement, type DungeonRunRecord } from "../game/dungeonRunTypes"; + +const STORAGE_KEY = "healer-man:dungeon-runs:v1"; +const listeners = new Set<() => void>(); +let memory: DungeonRunRecord[] = []; +export const DUNGEON_LEADERBOARD_PAGE_SIZE = 20; +export type LeaderboardScope = "world" | "device"; + +export function subscribeDungeonLeaderboard(listener: () => void) { + listeners.add(listener); + return () => { listeners.delete(listener); }; +} + +function localRuns(): DungeonRunRecord[] { + try { + const input: unknown = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? "[]"); + if (Array.isArray(input)) memory = input.flatMap((value: DungeonRunRecord) => { + if (!value || typeof value !== "object") return []; + const progress = normalizeDungeonRunProgress({ ...value, startedAt: value.finishedAt - value.durationMs, timed: true }); + const dungeon = catalog.dungeons.find((entry) => entry.id === progress?.dungeonId); + const difficulty = dungeon?.difficulties.find((entry) => entry.id === progress?.difficultyId); + if (!progress || progress.finishedAt === null || progress.durationMs === null || !dungeon + || !difficulty || !progress.killedBossIds.includes(difficulty.finalBossId) + || progress.killedBossIds.some((id) => !difficulty.bossIds.includes(id))) return []; + return [{ ...value, ...progress, durationMs: progress.durationMs, finishedAt: progress.finishedAt, + fullClear: difficulty.bossIds.every((id) => progress.killedBossIds.includes(id)) }]; + }); + } catch { /* In-memory results remain available if storage is disabled. */ } + return memory; +} + +export function saveLocalDungeonRun(record: DungeonRunRecord): void { + memory = [record, ...localRuns().filter((entry) => entry.id !== record.id)] + .sort((a, b) => b.finishedAt - a.finishedAt).slice(0, 1000); + try { localStorage.setItem(STORAGE_KEY, JSON.stringify(memory)); } catch { /* Keep the current session's results. */ } + listeners.forEach((listener) => listener()); +} + +export function localDungeonLeaderboard(dungeonId: string, difficultyId: string, + filter: DungeonClearFilter, offset = 0): DungeonLeaderboard { + const runs = localRuns().filter((entry) => entry.dungeonId === dungeonId && entry.difficultyId === difficultyId + && (filter === "final" || entry.fullClear)).sort((a, b) => a.durationMs - b.durationMs + || a.finishedAt - b.finishedAt || a.id.localeCompare(b.id)); + return { total: runs.length, entries: runs.slice(offset, offset + DUNGEON_LEADERBOARD_PAGE_SIZE) + .map((entry) => ({ ...entry, rank: 1 + runs.filter((other) => other.durationMs < entry.durationMs).length })) }; +} + +export function localDungeonRunPlacement(record: DungeonRunRecord): DungeonRunPlacement { + const final = localDungeonLeaderboard(record.dungeonId, record.difficultyId, "final"); + const full = localDungeonLeaderboard(record.dungeonId, record.difficultyId, "full"); + const runs = localRuns().filter((entry) => entry.dungeonId === record.dungeonId && entry.difficultyId === record.difficultyId); + return { finalRank: 1 + runs.filter((entry) => entry.durationMs < record.durationMs).length, + finalTotal: final.total, fullTotal: full.total, + fullRank: record.fullClear ? 1 + runs.filter((entry) => entry.fullClear && entry.durationMs < record.durationMs).length : null }; +} + +export async function fetchDungeonLeaderboard(dungeonId: string, difficultyId: string, + filter: DungeonClearFilter, offset: number, signal?: AbortSignal): Promise { + const query = new URLSearchParams({ dungeon: dungeonId, difficulty: difficultyId, clear: filter, + offset: String(offset), limit: String(DUNGEON_LEADERBOARD_PAGE_SIZE) }); + return requestOnlineJson(`/api/dungeon-leaderboards?${query}`, { signal }); +} + +const submissions = new Map>(); +export function submitDungeonRun(record: DungeonRunRecord, session: PlayerSession | null): Promise { + const existing = submissions.get(record.id); + if (existing) return existing; + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 8000); + const submission = requestOnlineJson<{ placement: DungeonRunPlacement }>("/api/dungeon-runs", { + method: "POST", body: JSON.stringify(record), signal: controller.signal, + }, session?.accessToken).then((result) => { + listeners.forEach((listener) => listener()); + return result.placement; + }).catch((error: unknown) => { submissions.delete(record.id); throw error; }) + .finally(() => clearTimeout(timeout)); + submissions.set(record.id, submission); + return submission; +} + +/** Group followers read the leader's single shared run instead of submitting duplicate entries. */ +export async function awaitSharedDungeonRunPlacement(id: string): Promise { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 8000); + try { + for (let attempt = 0; attempt < 8; attempt += 1) { + try { + const response = await requestOnlineJson<{ placement: DungeonRunPlacement }>(`/api/dungeon-runs/${encodeURIComponent(id)}`, + { signal: controller.signal }); + return response.placement; + } catch (error) { + if (attempt === 7 || controller.signal.aborted) throw error; + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + } + throw new Error("The shared run is not recorded yet."); + } finally { clearTimeout(timeout); } +} diff --git a/src/app/dungeonShellRuntime.ts b/src/app/dungeonShellRuntime.ts index e46a8c8e..26319bdb 100644 --- a/src/app/dungeonShellRuntime.ts +++ b/src/app/dungeonShellRuntime.ts @@ -61,6 +61,8 @@ function enterDungeon(dungeonId: string, requestedRole?: PartyRole): boolean { if (!activateDungeonSession(dungeonId, resume?.difficultyId, { spawn: resume?.spawn, encounterSnapshots: resume?.encounters, + runProgress: resume?.run, + legacyResume: Boolean(resume && !resume.run), })) { useShellStore.setState({ notice: dungeon.availabilityReason ?? "That dungeon failed its latest validation gate.", diff --git a/src/game/DungeonRunBridge.tsx b/src/game/DungeonRunBridge.tsx new file mode 100644 index 00000000..8bb5d032 --- /dev/null +++ b/src/game/DungeonRunBridge.tsx @@ -0,0 +1,66 @@ +import { useEffect } from "react"; +import { useShellStore } from "../app/shellStore"; +import { useOnlineSessionStore } from "../app/onlineSessionStore"; +import { useOnlineGroupStore } from "../app/onlineGroupStore"; +import { activeDungeonCompletedObjectiveIds, subscribeDungeonEncounterSnapshots } from "./dungeonEncounterRuntime"; +import { useCombatStore } from "./combatStore"; +import { usePartyStore } from "./partyStore"; +import { useGameStore } from "./store"; +import { useDungeonRunStore } from "./dungeonRunStore"; +import type { DungeonRunMember } from "./dungeonRunTypes"; + +function currentRunParty(): readonly DungeonRunMember[] { + const shell = useShellStore.getState(); + const character = shell.activeCharacter; + if (!character) return []; + const members: DungeonRunMember[] = [{ name: character.name, classId: character.classId, + level: useCombatStore.getState().level, role: shell.activeDungeonRole, npc: false, + ...(character.secondaryClassId ? { secondaryClassId: character.secondaryClassId } : {}) }]; + const online = useOnlineSessionStore.getState().snapshot; + for (const player of online?.players ?? []) { + if (player.accountId === shell.session?.ownerId || !player.character) continue; + members.push({ name: player.character.name, classId: player.character.classId, + level: player.character.level, role: player.role ?? "damage", npc: false }); + } + for (const member of usePartyStore.getState().members) { + members.push({ name: member.name, classId: member.classId, level: member.level, role: member.role, npc: true }); + } + return members.slice(0, 5); +} + +/** Wall-clock run timing survives wipes, pauses, checkpoints, and renderer retries. */ +export function DungeonRunBridge() { + useEffect(() => { + let announcedId: string | null = null; + const advance = () => { + const game = useGameStore.getState(); + const shell = useShellStore.getState(); + if (shell.phase !== "game" || game.gameMode !== "dungeon" || game.gmTestMode + || !["ready", "proxy"].includes(game.assetStatus)) return; + const run = useDungeonRunStore.getState(); + if (run.progress?.dungeonId !== game.activeDungeonId) return; + const party = currentRunParty(); + const activity = useOnlineGroupStore.getState().activeActivity; + if (activity?.type === "dungeon" && activity.contentId === game.activeDungeonId + && run.progress.finishedAt === null && run.progress.id !== activity.id) { + useDungeonRunStore.setState({ progress: { ...run.progress, id: activity.id, + startedAt: activity.startedAt, party } }); + } + run.start(Date.now(), party); + run.advance(activeDungeonCompletedObjectiveIds(game.activeDungeonId), + Object.values(useCombatStore.getState().mobs).filter((mob) => mob.dead && !mob.despawned).map((mob) => mob.id), + Date.now(), party); + const progress = useDungeonRunStore.getState().progress; + if (progress?.finishedAt !== null && progress && announcedId !== progress.id) { + announcedId = progress.id; + useGameStore.getState().openOverlay("dungeon-results"); + } + }; + const unsubscribeEncounters = subscribeDungeonEncounterSnapshots(advance); + const unsubscribeGame = useGameStore.subscribe(advance); + const interval = setInterval(advance, 250); + advance(); + return () => { unsubscribeEncounters(); unsubscribeGame(); clearInterval(interval); }; + }, []); + return null; +} diff --git a/src/game/DungeonRunCheckpointBridge.tsx b/src/game/DungeonRunCheckpointBridge.tsx index b746d368..5a5bbeb9 100644 --- a/src/game/DungeonRunCheckpointBridge.tsx +++ b/src/game/DungeonRunCheckpointBridge.tsx @@ -6,6 +6,7 @@ import { } from "./dungeonEncounterRuntime"; import { DUNGEON_RUN_CHECKPOINT_VERSION } from "./dungeonRunCheckpoint"; import { useGameStore } from "./store"; +import { useDungeonRunStore } from "./dungeonRunStore"; function checkpointSignature(dungeonId: string): string { const game = useGameStore.getState(); @@ -15,6 +16,7 @@ function checkpointSignature(dungeonId: string): string { difficultyId: game.activeDifficultyId, spawn: game.activeSpawn, encounters, + run: useDungeonRunStore.getState().progress, }); } @@ -41,10 +43,13 @@ export function DungeonRunCheckpointBridge() { spawn: game.activeSpawn, encounters, savedAt: Date.now(), + run: useDungeonRunStore.getState().progress ?? undefined, }); }; save(); - return subscribeDungeonEncounterSnapshots(save); + const unsubscribeEncounters = subscribeDungeonEncounterSnapshots(save); + const unsubscribeRun = useDungeonRunStore.subscribe(save); + return () => { unsubscribeEncounters(); unsubscribeRun(); }; }, [activeCharacterId, phase]); return null; diff --git a/src/game/combatStore.ts b/src/game/combatStore.ts index 4490b2dd..d7d1f16e 100644 --- a/src/game/combatStore.ts +++ b/src/game/combatStore.ts @@ -3589,11 +3589,11 @@ export const useCombatStore = create((set, get) => ({ 0, registration.moveSpeed ?? existing?.moveSpeed ?? (boss ? 3.15 : 3.5), ); - const attackDamage = existing && registration.damageMultiplier !== undefined + const attackDamage = existing && level === existing.level && registration.damageMultiplier !== undefined ? Math.max(1, Math.round( existing.attackDamage * (damageMultiplier / Math.max(0.01, existing.damageMultiplier)), )) - : existing?.attackDamage ?? Math.max(1, Math.round( + : (existing && level === existing.level ? existing.attackDamage : undefined) ?? Math.max(1, Math.round( defaults.attackDamage * damageMultiplier, )); const mob: MobCombatState = existing @@ -3621,7 +3621,9 @@ export const useCombatStore = create((set, get) => ({ leashRange, moveSpeed, attackDamage, - health: Math.min(existing.health, maxHealth), + health: existing.dead || existing.health <= 0 + ? 0 + : clamp(Math.round(existing.health / Math.max(1, existing.maxHealth) * maxHealth), 1, maxHealth), xpReward, hasLoot, } diff --git a/src/game/dungeonLeaderboardCatalog.test.ts b/src/game/dungeonLeaderboardCatalog.test.ts new file mode 100644 index 00000000..b27455b3 --- /dev/null +++ b/src/game/dungeonLeaderboardCatalog.test.ts @@ -0,0 +1,26 @@ +import { readFileSync, writeFileSync } from "node:fs"; +import { describe, expect, it } from "vitest"; +import { CLASSES } from "../app/characterCatalog"; +import { DUNGEON_DEFINITIONS } from "./dungeonRegistry"; +import { dungeonBossesForDifficulty, finalDungeonBoss } from "./dungeonRunStore"; + +describe("dungeon leaderboard catalog", () => { + it("keeps the server's final bosses, boss lists, difficulties, and classes aligned with the game", () => { + const catalog = { + dungeons: DUNGEON_DEFINITIONS.map((dungeon) => ({ id: dungeon.id, title: dungeon.title, + finalBossId: finalDungeonBoss(dungeon)?.id ?? "", + bosses: dungeon.bosses.map((boss) => ({ id: boss.id, name: boss.name })), + difficulties: dungeon.difficultyVariants.map((difficulty) => ({ id: difficulty.id, name: difficulty.difficulty, + bossIds: dungeonBossesForDifficulty(dungeon, difficulty.id).map((boss) => boss.id), + finalBossId: finalDungeonBoss(dungeon, difficulty.id)?.id ?? "" })), + defaultDifficultyId: dungeon.defaultDifficultyId, + })).sort((a, b) => a.id.localeCompare(b.id)), + classes: CLASSES.map((entry) => ({ id: entry.id, name: entry.name })), + }; + expect(catalog.dungeons).toHaveLength(84); + expect(catalog.dungeons.filter((dungeon) => dungeon.bosses.length > 0 && !dungeon.finalBossId)).toEqual([]); + const path = "src/game/generated/dungeonLeaderboardCatalog.generated.json"; + if (process.env.WRITE_DUNGEON_LEADERBOARD_CATALOG === "1") writeFileSync(path, `${JSON.stringify(catalog, null, 2)}\n`); + expect(JSON.parse(readFileSync(path, "utf8"))).toEqual(catalog); + }); +}); diff --git a/src/game/dungeonLevelScaling.test.ts b/src/game/dungeonLevelScaling.test.ts index 40ffe06e..d8bb87e8 100644 --- a/src/game/dungeonLevelScaling.test.ts +++ b/src/game/dungeonLevelScaling.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { scaledDungeonEnemyLevel } from "./dungeonLevelScaling"; +import { dungeonEnemyHealthBaseline, scaledDungeonEnemyLevel } from "./dungeonLevelScaling"; describe("normal dungeon level scaling", () => { it("scales opening-tier trash to the character and gives bosses a one-level edge", () => { @@ -13,4 +13,19 @@ describe("normal dungeon level scaling", () => { expect(scaledDungeonEnemyLevel(80, false)).toBe(80); expect(scaledDungeonEnemyLevel(80, true)).toBe(80); }); + + it("grows RoM health with ability ranks and clamps invalid levels", () => { + expect(dungeonEnemyHealthBaseline(1, "runewaker")).toBeGreaterThan(1_000); + expect(dungeonEnemyHealthBaseline(20, "runewaker")).toBeGreaterThan(dungeonEnemyHealthBaseline(8, "runewaker")!); + expect(dungeonEnemyHealthBaseline(70, "runewaker")).toBeGreaterThan(dungeonEnemyHealthBaseline(20, "runewaker")! * 5); + expect(dungeonEnemyHealthBaseline(0, "runewaker")).toBe(dungeonEnemyHealthBaseline(1, "runewaker")); + expect(dungeonEnemyHealthBaseline(100, "runewaker")).toBe(dungeonEnemyHealthBaseline(80, "runewaker")); + expect(dungeonEnemyHealthBaseline(Number.NaN, "runewaker")).toBe(dungeonEnemyHealthBaseline(1, "runewaker")); + }); + + it("retains native WoW creature health across its three expansions", () => { + for (const expansion of ["classic", "burning-crusade", "wrath"] as const) { + expect(dungeonEnemyHealthBaseline(20, expansion)).toBeUndefined(); + } + }); }); diff --git a/src/game/dungeonLevelScaling.ts b/src/game/dungeonLevelScaling.ts index 6ca0b144..23e1dc12 100644 --- a/src/game/dungeonLevelScaling.ts +++ b/src/game/dungeonLevelScaling.ts @@ -1,4 +1,5 @@ import { clampPlayerLevel } from "./progression"; +import type { DungeonExpansion } from "./dungeonTypes"; /** * Normal campaign dungeons scale to the character instead of retaining the @@ -8,3 +9,15 @@ import { clampPlayerLevel } from "./progression"; export function scaledDungeonEnemyLevel(characterLevel: number, boss: boolean): number { return clampPlayerLevel(characterLevel + (boss ? 1 : 0)); } + +/** + * RoM creatures do not have packaged native health tables. Give their dungeon + * baseline enough growth for a full party's rank-scaled abilities; the old + * 72 + level * 22 solo-target fallback was overwhelmed by the opening volley. + * Authored creature and boss health multipliers are applied separately. + */ +export function dungeonEnemyHealthBaseline(level: number, expansion: DungeonExpansion): number | undefined { + if (expansion !== "runewaker") return undefined; + const safeLevel = clampPlayerLevel(level); + return 1_000 + safeLevel * 100 + safeLevel * safeLevel * 8; +} diff --git a/src/game/dungeonRegistry.ts b/src/game/dungeonRegistry.ts index 733f2c84..36c0b9cd 100644 --- a/src/game/dungeonRegistry.ts +++ b/src/game/dungeonRegistry.ts @@ -7,6 +7,7 @@ import compressedCampaign from "./generated/dungeonCampaignCatalog.compressed"; import { gunzipSync, strFromU8 } from "fflate"; import { DEFAULT_DUNGEON_ID as DEFAULT_DUNGEON_KEY } from "./dungeonDefaults"; import { withRunewakerEncounterContent } from "./runewakerEncounterCatalog"; +import { withRunewakerCompletion } from "./runewakerCompletion"; import { withWowEncounterContent } from "./wowEncounterContentCatalog"; import { withWowProgressionObjectContent } from "./wowProgressionObjectContentCatalog"; import { withAscensionCustomEncounterContent } from "./ascensionCustomEncounterCatalog"; @@ -19,6 +20,7 @@ import { withWowVerifiedNoOpInstanceContent } from "./wowVerifiedNoOpInstanceCat import { withWowClassicEncounterBatchTwo } from "./wowClassicEncounterBatchTwoCatalog"; import { withWowClassicEncounterBatchThree } from "./wowClassicEncounterBatchThreeCatalog"; import { withWowTbcEncounterBatchTwoContent } from "./wowTbcEncounterBatchTwoCatalog"; +import { withWowDungeonEnvironment } from "./wowDungeonEnvironment"; export { DEFAULT_DUNGEON_ID } from "./dungeonDefaults"; @@ -151,6 +153,7 @@ if (authoredWailingCaverns.id !== DEFAULT_DUNGEON_KEY) { const DUNGEON_CONTENT_ADAPTERS: readonly ( (definition: DungeonDefinition) => DungeonDefinition )[] = Object.freeze([ + withWowDungeonEnvironment, withWowEncounterContent, withWowClassicEncounterContent, withWowClassicEncounterBatchTwo, @@ -164,6 +167,7 @@ const DUNGEON_CONTENT_ADAPTERS: readonly ( withWowTbcEncounterBatchTwoContent, withAscensionCustomEncounterContent, withRunewakerEncounterContent, + withRunewakerCompletion, ]); function applyDungeonContentAdapters(definition: DungeonDefinition): DungeonDefinition { diff --git a/src/game/dungeonRunCheckpoint.test.ts b/src/game/dungeonRunCheckpoint.test.ts index 3daa6639..c1deed93 100644 --- a/src/game/dungeonRunCheckpoint.test.ts +++ b/src/game/dungeonRunCheckpoint.test.ts @@ -14,6 +14,14 @@ const definition: DungeonEncounterDefinition = { }; describe("dungeon run checkpoints", () => { + it("round-trips a saved timer and rejects a timer belonging to another dungeon", () => { + const input = { version: DUNGEON_RUN_CHECKPOINT_VERSION, dungeonId: "test", difficultyId: "normal", + checkpointId: "entrance", spawn: { footPosition: [1, 2, 3], yaw: 0.5 }, encounters: [], savedAt: 123, + run: { id: "checkpoint-timer", dungeonId: "test", difficultyId: "normal", startedAt: 100, + finishedAt: null, durationMs: null, timed: true, killedBossIds: [], party: [] } }; + expect(normalizeDungeonRunCheckpoint(JSON.parse(JSON.stringify(input)))?.run).toEqual(input.run); + expect(normalizeDungeonRunCheckpoint({ ...input, run: { ...input.run, dungeonId: "other" } })).toBeNull(); + }); it("normalizes JSON-safe encounter progress and world spawn", () => { const result = normalizeDungeonRunCheckpoint({ version: DUNGEON_RUN_CHECKPOINT_VERSION, diff --git a/src/game/dungeonRunCheckpoint.ts b/src/game/dungeonRunCheckpoint.ts index 86070f98..c9a263e5 100644 --- a/src/game/dungeonRunCheckpoint.ts +++ b/src/game/dungeonRunCheckpoint.ts @@ -1,6 +1,7 @@ import type { DungeonEncounterSnapshot } from "./dungeonEncounterTypes"; import { DUNGEON_ENCOUNTER_SNAPSHOT_SCHEMA_VERSION } from "./dungeonEncounterTypes"; import type { Vector3Tuple, WorldSpawn } from "./dungeonTypes"; +import { normalizeDungeonRunProgress, type DungeonRunProgress } from "./dungeonRunTypes"; // Version 1 snapshots sorted consumed action ids and cannot be migrated // reliably when multiple durable actions write the same state slot. Failing @@ -15,6 +16,7 @@ export interface DungeonRunCheckpoint { readonly spawn: WorldSpawn; readonly encounters: readonly DungeonEncounterSnapshot[]; readonly savedAt: number; + readonly run?: DungeonRunProgress; } function finiteVector(value: unknown): Vector3Tuple | null { @@ -115,6 +117,8 @@ export function normalizeDungeonRunCheckpoint(value: unknown): DungeonRunCheckpo ) return null; const encounters = input.encounters.map(normalizeSnapshot); if (encounters.some((entry) => entry === null)) return null; + const run = input.run === undefined ? undefined : normalizeDungeonRunProgress(input.run); + if (run === null || (run && (run.dungeonId !== input.dungeonId || run.difficultyId !== input.difficultyId))) return null; return Object.freeze({ version: DUNGEON_RUN_CHECKPOINT_VERSION, dungeonId: input.dungeonId, @@ -123,5 +127,6 @@ export function normalizeDungeonRunCheckpoint(value: unknown): DungeonRunCheckpo spawn: { footPosition: position, yaw }, encounters: Object.freeze(encounters as DungeonEncounterSnapshot[]), savedAt: input.savedAt, + ...(run ? { run } : {}), }); } diff --git a/src/game/dungeonRunCombatIntegration.test.ts b/src/game/dungeonRunCombatIntegration.test.ts new file mode 100644 index 00000000..b83d2579 --- /dev/null +++ b/src/game/dungeonRunCombatIntegration.test.ts @@ -0,0 +1,95 @@ +import { mkdirSync, writeFileSync } from "node:fs"; +import { expect, it } from "vitest"; +import { createCharacter } from "../app/accountRepository"; +import { randomAppearance } from "../app/characterCatalog"; +import { abilitiesForClass } from "./abilityCatalog"; +import { useCombatStore } from "./combatStore"; +import { subscribeCombatEvents } from "./combatEvents"; +import { deriveDungeonEncounterCombatEvents, type DungeonEncounterMobCursor } from "./dungeonEncounterCombatEvents"; +import { activateDungeonEncounterDefinitions, activeDungeonCompletedObjectiveIds, activeDungeonRunIsComplete, + advanceDungeonEncounters, dispatchActiveDungeonEntityEvent, exportDungeonEncounterCheckpointSnapshots, resetDungeonEncounters } from "./dungeonEncounterRuntime"; +import { requireDungeonDefinition } from "./dungeonRegistry"; +import { dungeonEnemyHealthBaseline, scaledDungeonEnemyLevel } from "./dungeonLevelScaling"; +import { completedDungeonRun, finalDungeonBoss, useDungeonRunStore } from "./dungeonRunStore"; +import { clearMobRuntimeRegistry, setMobPosition } from "./mobRuntimeRegistry"; +import { usePartyStore } from "./partyStore"; +import { advanceDungeonPartyCombat, partyCombatRangeBand, resetPartyRuntimeClock } from "./partyRuntime"; +import { clearPartyRuntimeRegistry, registerPartyRuntimePosition, updatePartyRuntimeTargetVisibility } from "./partyRuntimeRegistry"; +import { useGameStore } from "./store"; + +it("turns an actual RoM party's lethal damage to Anglo into one timed completion and a resumable result", () => { + const dungeon = requireDungeonDefinition("barren-caves"); + const boss = finalDungeonBoss(dungeon)!; + const spawn = dungeon.staticSpawns.find((entry) => entry.id === boss.id)!; + const entity = dungeon.entities[spawn.entityId]; + const bossCombat = entity.combat; + if (!bossCombat) throw new Error("Anglo must have a combat definition"); + const healthMultiplier = bossCombat.healthMultiplier ?? 1; + clearMobRuntimeRegistry(); clearPartyRuntimeRegistry(); resetPartyRuntimeClock(); + useGameStore.setState({ activeDungeonId: dungeon.id, activeDifficultyId: "normal", gameMode: "dungeon", gmTestMode: false }); + activateDungeonEncounterDefinitions(dungeon.id, dungeon.encounters ?? [], "normal"); + resetDungeonEncounters("dungeon", dungeon.id); + const combat = useCombatStore.getState(); + combat.initializeCharacter({ classId: "rom-priest", raceId: "rom-human", level: 1 }); + combat.setPlayerPosition([-18, 0, 0]); + usePartyStore.getState().initializeParty("completion-run", 1, useCombatStore.getState().health, "healer", 5, "rom"); + combat.synchronizeActors(); + const bossLevel = scaledDungeonEnemyLevel(1, true); + combat.registerMob(boss.id, { name: entity.name, boss: true, level: bossLevel, xpReward: 0, + maxHealth: dungeonEnemyHealthBaseline(bossLevel, "runewaker")! * healthMultiplier, + healthMultiplier, attacks: bossCombat.attacks }); + setMobPosition(boss.id, [0, 0, 0]); + for (const member of usePartyStore.getState().members) { + registerPartyRuntimePosition(member.id, [-partyCombatRangeBand(member).preferred, 0, 0]); + updatePartyRuntimeTargetVisibility(member.id, boss.id, true); + } + const party = () => [{ name: "Runview", classId: "rom-priest", level: 1, role: "healer", npc: false }, + ...usePartyStore.getState().members.map((member) => ({ name: member.name, classId: member.classId, + level: member.level, role: member.role, npc: true }))]; + const epoch = Date.now() - 181000; + const run = useDungeonRunStore.getState(); run.begin(dungeon.id, "normal"); run.start(epoch, party()); + combat.damageMob(boss.id, 1, epoch); + const heal = abilitiesForClass("rom-priest").find((ability) => ability.name === "Urgent Heal")!; + let cursor: DungeonEncounterMobCursor = {}; + let npcDamage = 0; let healing = 0; let deaths = 0; + const stop = subscribeCombatEvents((event) => { + if (event.kind === "damage" && event.targetActorId === boss.id && event.sourceActorId !== "player") npcDamage += event.effectiveAmount; + if (event.kind === "healing") healing += event.effectiveAmount; + }); + try { + for (let now = epoch + 100; now <= epoch + 181000; now += 100) { + combat.tick(.1, now); advanceDungeonPartyCombat(now); combat.advanceMobCombat(now); + const tank = usePartyStore.getState().members.find((member) => member.role === "tank")!; + if (tank.health < tank.maxHealth * .65 && !useCombatStore.getState().activeCast) { + usePartyStore.getState().selectMember(tank.id); combat.castAbility(heal.id, undefined, now); + } + const mobs = useCombatStore.getState().mobs; + const events = deriveDungeonEncounterCombatEvents(cursor, mobs, now); cursor = events.cursor; + for (const event of events.events) { dispatchActiveDungeonEntityEvent(event); if (event.kind === "entity-death") deaths += 1; } + const dead = Object.values(mobs).filter((mob) => mob.dead).map((mob) => mob.id); + advanceDungeonEncounters(dungeon.id, { kind: "tick", now, facts: { registeredEntityIds: Object.keys(mobs), deadEntityIds: dead } }); + run.advance(activeDungeonCompletedObjectiveIds(dungeon.id), dead, now, party()); + if (useDungeonRunStore.getState().progress?.finishedAt !== null) break; + } + const progress = useDungeonRunStore.getState().progress!; + expect(useCombatStore.getState().mobs[boss.id].dead).toBe(true); + expect(activeDungeonRunIsComplete(dungeon.id)).toBe(true); + expect(npcDamage).toBeGreaterThan(0); expect(healing).toBeGreaterThan(0); expect(deaths).toBe(1); + expect(completedDungeonRun(progress)).toMatchObject({ fullClear: true, killedBossIds: [boss.id] }); + expect(progress.durationMs).toBeGreaterThan(20000); + expect(usePartyStore.getState().members.every((member) => member.health > 0)).toBe(true); + const snapshots = exportDungeonEncounterCheckpointSnapshots(dungeon.id, 0); + if (process.env.DUNGEON_RUN_REPORT === "1") { + const values = new Map(); + const character = createCharacter({ ownerId: "qa-owner", name: "Runview", raceId: "rom-human", classId: "rom-priest", + categoryId: "rom", gender: "female", appearance: randomAppearance("rom-human", "female", () => 0) }, + { getItem: (key) => values.get(key) ?? null, setItem: (key, value) => { values.set(key, value); }, + removeItem: (key) => { values.delete(key); } }).character!; + mkdirSync("artifacts/dungeon-runs", { recursive: true }); + writeFileSync("artifacts/dungeon-runs/combat-completion.json", `${JSON.stringify({ npcDamage, healing, deaths, progress, + character: { ...character, dungeonRunCheckpoint: { version: 2, dungeonId: dungeon.id, difficultyId: "normal", + checkpointId: "entrance", spawn: { footPosition: dungeon.entrance.footPosition, yaw: dungeon.entrance.yaw }, + encounters: snapshots, savedAt: Date.now(), run: progress } } }, null, 2)}\n`); + } + } finally { stop(); clearMobRuntimeRegistry(); clearPartyRuntimeRegistry(); } +}); diff --git a/src/game/dungeonRunStore.test.ts b/src/game/dungeonRunStore.test.ts new file mode 100644 index 00000000..ca519b57 --- /dev/null +++ b/src/game/dungeonRunStore.test.ts @@ -0,0 +1,96 @@ +import { beforeEach, describe, expect, it } from "vitest"; +import { DUNGEON_DEFINITIONS, requireDungeonDefinition } from "./dungeonRegistry"; +import { completedDungeonRun, finalDungeonBoss, useDungeonRunStore } from "./dungeonRunStore"; +import { normalizeDungeonRunProgress, formatDungeonDuration, type DungeonRunMember } from "./dungeonRunTypes"; +import { activateDungeonEncounterDefinitions, activeDungeonCompletedObjectiveIds, activeDungeonRunIsComplete, + advanceDungeonEncounters, createDungeonEncounterSnapshot, exportDungeonEncounterCheckpointSnapshots, + importDungeonEncounterSnapshots, reduceDungeonEncounter, resetDungeonEncounters } from "./dungeonEncounterRuntime"; + +const party: readonly DungeonRunMember[] = [{ name: "Tester", classId: "rom-priest", level: 1, role: "healer", npc: false }]; +beforeEach(() => { useDungeonRunStore.setState({ progress: null }); }); + +describe("dungeon completion and run timing", () => { + it("completes Barren Caves when Anglo dies, records its real spawn id, and survives checkpoint hydration", () => { + const dungeon = requireDungeonDefinition("barren-caves"); + activateDungeonEncounterDefinitions(dungeon.id, dungeon.encounters ?? [], "normal"); + resetDungeonEncounters("dungeon", dungeon.id); + const run = useDungeonRunStore.getState(); + run.begin(dungeon.id, "normal"); run.start(1000, party); + advanceDungeonEncounters(dungeon.id, { kind: "tick", now: 1000, facts: { registeredEntityIds: [], deadEntityIds: [] } }); + expect(activeDungeonRunIsComplete(dungeon.id)).toBe(false); + const boss = finalDungeonBoss(dungeon)!; + expect(boss.name).toBe("Anglo"); + advanceDungeonEncounters(dungeon.id, { kind: "tick", now: 12456, + facts: { registeredEntityIds: [boss.id], deadEntityIds: [boss.id] } }); + run.advance(activeDungeonCompletedObjectiveIds(dungeon.id), [boss.id], 12456, party); + expect(activeDungeonRunIsComplete(dungeon.id)).toBe(true); + expect(completedDungeonRun(useDungeonRunStore.getState().progress!)).toMatchObject({ + durationMs: 11456, fullClear: true, killedBossIds: [boss.id], party, + }); + const snapshots = exportDungeonEncounterCheckpointSnapshots(dungeon.id, 0); + resetDungeonEncounters("dungeon", dungeon.id); + importDungeonEncounterSnapshots(dungeon.id, snapshots, { authoritative: true }); + expect(activeDungeonRunIsComplete(dungeon.id)).toBe(true); + const result = useDungeonRunStore.getState().progress; + run.advance([boss.id], [boss.id], 99999, []); run.start(99999, []); + expect(useDungeonRunStore.getState().progress).toBe(result); + }); + + it("adds durable completion bookkeeping to every RoM dungeon with configured bosses, without awarding missing bosses", () => { + for (const dungeon of DUNGEON_DEFINITIONS.filter((entry) => entry.expansion === "runewaker" && entry.bosses.length > 0)) { + const baseline = dungeon.encounters!.find((entry) => entry.id === `${dungeon.id}:completion-baseline`)!; + expect(baseline, dungeon.id).toBeDefined(); + let snapshot = createDungeonEncounterSnapshot(baseline, 0); + snapshot = reduceDungeonEncounter(baseline, snapshot, { kind: "tick", now: 10, + facts: { registeredEntityIds: [], deadEntityIds: [] } }).snapshot; + expect(snapshot.flags["dungeon-complete"], dungeon.id).toBe(false); + const ordinary = [...new Set([...dungeon.objectiveOrder, ...dungeon.bosses] + .filter((boss) => !boss.eventOnly).map((boss) => boss.id))]; + snapshot = reduceDungeonEncounter(baseline, snapshot, { kind: "tick", now: 20, + facts: { registeredEntityIds: ordinary, deadEntityIds: ordinary } }).snapshot; + expect(snapshot.completedObjectiveIds, dungeon.id).toEqual(expect.arrayContaining(ordinary)); + if (!dungeon.objectiveOrder.some((boss) => !boss.optional && boss.eventOnly)) { + expect(snapshot.flags["dungeon-complete"], dungeon.id).toBe(true); + } + } + }); + + it("finishes a final boss clear even when earlier bosses were skipped, but keeps it out of full clears", () => { + const dungeon = requireDungeonDefinition("deadmines"); + const final = finalDungeonBoss(dungeon)!; + const run = useDungeonRunStore.getState(); + run.begin(dungeon.id, dungeon.defaultDifficultyId); run.start(1000, party); + run.advance([], [final.id], 2000, party); + expect(useDungeonRunStore.getState().progress?.finishedAt).toBeNull(); + run.advance([final.id], [final.id], 3000, party); + expect(completedDungeonRun(useDungeonRunStore.getState().progress!)).toMatchObject({ durationMs: 2000, fullClear: false }); + }); + + it("keeps the original wall-clock timer and boss history through a wipe and a saved resume", () => { + const dungeon = requireDungeonDefinition("deadmines"); + const run = useDungeonRunStore.getState(); + run.begin(dungeon.id, "normal"); run.start(1000, party); + run.advance([], [dungeon.bosses[0].id], 2000, party); + const saved = normalizeDungeonRunProgress(JSON.parse(JSON.stringify(useDungeonRunStore.getState().progress)))!; + run.begin(dungeon.id, "normal", saved); run.start(5000, party); + run.advance([finalDungeonBoss(dungeon)!.id], [], 9000, party); + expect(useDungeonRunStore.getState().progress).toMatchObject({ id: saved.id, startedAt: 1000, + durationMs: 8000, killedBossIds: expect.arrayContaining([dungeon.bosses[0].id]) }); + }); + + it("announces legacy resumed runs without inventing a leaderboard time", () => { + const run = useDungeonRunStore.getState(); run.begin("barren-caves", "normal", undefined, true); + run.start(1000, party); run.advance([finalDungeonBoss(requireDungeonDefinition("barren-caves"))!.id], [], 2000, party); + expect(useDungeonRunStore.getState().progress?.finishedAt).toBe(2000); + expect(completedDungeonRun(useDungeonRunStore.getState().progress!)).toBeNull(); + }); + + it("rejects corrupted saved timers and impossible party levels", () => { + useDungeonRunStore.getState().begin("barren-caves", "normal"); + const progress = useDungeonRunStore.getState().progress!; + expect(normalizeDungeonRunProgress({ ...progress, startedAt: NaN })).toBeNull(); + expect(normalizeDungeonRunProgress({ ...progress, startedAt: 100, finishedAt: 50, durationMs: 10 })).toBeNull(); + expect(normalizeDungeonRunProgress({ ...progress, party: [{ ...party[0], level: 81 }] })).toBeNull(); + expect(formatDungeonDuration(123456)).toBe("02:03.4"); + }); +}); diff --git a/src/game/dungeonRunStore.ts b/src/game/dungeonRunStore.ts new file mode 100644 index 00000000..baa44e1f --- /dev/null +++ b/src/game/dungeonRunStore.ts @@ -0,0 +1,67 @@ +import { create } from "zustand"; +import { dungeonDefinitionById } from "./dungeonRegistry"; +import type { DungeonDefinition } from "./dungeonTypes"; +import type { DungeonRunMember, DungeonRunProgress, DungeonRunRecord } from "./dungeonRunTypes"; + +export function dungeonBossesForDifficulty(definition: DungeonDefinition, difficultyId = definition.defaultDifficultyId) { + const difficulty = definition.difficultyVariants.find((variant) => variant.id === difficultyId); + return definition.bosses.filter((boss) => { + const spawn = definition.staticSpawns.find((entry) => entry.id === boss.id); + return !spawn || (((spawn.spawnMask ?? 1) & (difficulty?.spawnMask ?? 1)) !== 0); + }); +} + +export function finalDungeonBoss(definition: DungeonDefinition, difficultyId = definition.defaultDifficultyId) { + const bosses = dungeonBossesForDifficulty(definition, difficultyId); + const bossIds = new Set(bosses.map((boss) => boss.id)); + return definition.objectiveOrder.filter((objective) => !objective.optional && bossIds.has(objective.id)).at(-1) + ?? bosses.filter((boss) => !boss.optional).at(-1); +} + +export function completedDungeonRun(progress: DungeonRunProgress): DungeonRunRecord | null { + const definition = dungeonDefinitionById(progress.dungeonId); + if (!definition || !progress.timed || progress.finishedAt === null || progress.durationMs === null) return null; + return { id: progress.id, dungeonId: progress.dungeonId, difficultyId: progress.difficultyId, + finishedAt: progress.finishedAt, durationMs: progress.durationMs, killedBossIds: progress.killedBossIds, + party: progress.party, fullClear: dungeonBossesForDifficulty(definition, progress.difficultyId) + .every((boss) => progress.killedBossIds.includes(boss.id)) }; +} + +interface DungeonRunState { + readonly progress: DungeonRunProgress | null; + begin: (dungeonId: string, difficultyId: string, resume?: DungeonRunProgress, legacyResume?: boolean) => void; + start: (now: number, party: readonly DungeonRunMember[]) => void; + advance: (completedObjectiveIds: readonly string[], deadBossIds: readonly string[], now: number, + party: readonly DungeonRunMember[]) => void; +} + +export const useDungeonRunStore = create((set, get) => ({ + progress: null, + begin: (dungeonId, difficultyId, resume, legacyResume = false) => set({ + progress: resume?.dungeonId === dungeonId && resume.difficultyId === difficultyId ? resume : { + id: globalThis.crypto.randomUUID(), dungeonId, difficultyId, startedAt: null, + finishedAt: null, durationMs: null, killedBossIds: [], party: [], timed: !legacyResume, + }, + }), + start: (now, party) => { + const progress = get().progress; + if (!progress || progress.startedAt !== null || party.length === 0) return; + set({ progress: { ...progress, startedAt: now, party: [...party] } }); + }, + advance: (completedObjectiveIds, deadBossIds, now, party) => { + const progress = get().progress; + if (!progress || progress.startedAt === null || progress.finishedAt !== null) return; + const definition = dungeonDefinitionById(progress.dungeonId); + if (!definition) return; + const bossIds = new Set(definition.bosses.map((boss) => boss.id)); + const killedBossIds = [...new Set([...progress.killedBossIds, ...completedObjectiveIds, ...deadBossIds])] + .filter((id) => bossIds.has(id)); + const finalBoss = finalDungeonBoss(definition, progress.difficultyId); + // A scripted final boss must award its objective; despawns and raw corpse states cannot complete it. + const finished = finalBoss !== undefined && completedObjectiveIds.includes(finalBoss.id); + if (!finished && killedBossIds.length === progress.killedBossIds.length) return; + set({ progress: { ...progress, killedBossIds, + ...(finished ? { finishedAt: Math.max(now, progress.startedAt), durationMs: Math.max(1, Math.round(now - progress.startedAt)), + party: [...party] } : {}) } }); + }, +})); diff --git a/src/game/dungeonRunTypes.ts b/src/game/dungeonRunTypes.ts new file mode 100644 index 00000000..a3bbe617 --- /dev/null +++ b/src/game/dungeonRunTypes.ts @@ -0,0 +1,78 @@ +export interface DungeonRunMember { + readonly name: string; + readonly classId: string; + readonly level: number; + readonly secondaryClassId?: string; + readonly role: string; + readonly npc: boolean; +} + +export interface DungeonRunProgress { + readonly id: string; + readonly dungeonId: string; + readonly difficultyId: string; + readonly startedAt: number | null; + readonly finishedAt: number | null; + readonly durationMs: number | null; + readonly timed: boolean; + readonly killedBossIds: readonly string[]; + readonly party: readonly DungeonRunMember[]; +} + +export interface DungeonRunRecord { + readonly id: string; + readonly dungeonId: string; + readonly difficultyId: string; + readonly durationMs: number; + readonly finishedAt: number; + readonly killedBossIds: readonly string[]; + readonly party: readonly DungeonRunMember[]; + readonly fullClear: boolean; +} + +export type DungeonClearFilter = "full" | "final"; +export interface RankedDungeonRun extends DungeonRunRecord { readonly rank: number } +export interface DungeonLeaderboard { + readonly entries: readonly RankedDungeonRun[]; + readonly total: number; +} +export interface DungeonRunPlacement { + readonly finalRank: number; + readonly finalTotal: number; + readonly fullRank: number | null; + readonly fullTotal: number; +} + +export function formatDungeonDuration(durationMs: number): string { + const tenths = Math.floor(Math.max(0, durationMs) / 100); + const seconds = Math.floor(tenths / 10); + const minutes = Math.floor(seconds / 60); + return `${String(minutes).padStart(2, "0")}:${String(seconds % 60).padStart(2, "0")}.${tenths % 10}`; +} + +/** Untrusted checkpoint/local-storage data must never reset or invent a ranked timer. */ +export function normalizeDungeonRunProgress(value: unknown): DungeonRunProgress | null { + if (!value || typeof value !== "object") return null; + const input = value as DungeonRunProgress; + const text = (entry: unknown) => typeof entry === "string" && entry.length > 0 && entry.length <= 100; + const time = (entry: unknown) => entry === null || (typeof entry === "number" && Number.isFinite(entry) && entry >= 0); + if (!text(input.id) || !text(input.dungeonId) || !text(input.difficultyId) + || !time(input.startedAt) || !time(input.finishedAt) || !time(input.durationMs) + || typeof input.timed !== "boolean" || !Array.isArray(input.killedBossIds) + || input.killedBossIds.length > 100 || !input.killedBossIds.every(text) + || !Array.isArray(input.party) || input.party.length > 5 + || !input.party.every((member) => member && text(member.name) && text(member.classId) + && Number.isInteger(member.level) && member.level >= 1 && member.level <= 80 + && ["tank", "healer", "damage"].includes(member.role) && typeof member.npc === "boolean" + && (member.secondaryClassId === undefined || text(member.secondaryClassId))) + || (input.finishedAt !== null && (input.startedAt === null || input.finishedAt < input.startedAt)) + || (input.durationMs !== null && (input.finishedAt === null || input.durationMs < 1)) + || (input.finishedAt !== null && (input.durationMs === null || input.party.length === 0 + || input.durationMs !== Math.max(1, Math.round(input.finishedAt - input.startedAt!))))) return null; + return { id: input.id, dungeonId: input.dungeonId, difficultyId: input.difficultyId, + startedAt: input.startedAt, finishedAt: input.finishedAt, durationMs: input.durationMs, + timed: input.timed, killedBossIds: [...new Set(input.killedBossIds)], + party: input.party.map((member) => ({ name: member.name, classId: member.classId, + level: member.level, role: member.role, npc: member.npc, + ...(member.secondaryClassId ? { secondaryClassId: member.secondaryClassId } : {}) })) }; +} diff --git a/src/game/dungeonSession.ts b/src/game/dungeonSession.ts index 039255d6..b434456d 100644 --- a/src/game/dungeonSession.ts +++ b/src/game/dungeonSession.ts @@ -18,6 +18,8 @@ import { } from "./dungeonEncounterRuntime"; import type { DungeonEncounterSnapshot } from "./dungeonEncounterTypes"; import { dungeonContentIsUsable } from "../content/contentManager"; +import { useDungeonRunStore } from "./dungeonRunStore"; +import type { DungeonRunProgress } from "./dungeonRunTypes"; export interface DungeonSessionOptions { readonly gameMode?: GameMode; @@ -28,6 +30,8 @@ export interface DungeonSessionOptions { readonly preserveCharacter?: boolean; /** Hydrates save-safe state after definitions have been activated and reset. */ readonly encounterSnapshots?: readonly DungeonEncounterSnapshot[]; + readonly runProgress?: DungeonRunProgress; + readonly legacyResume?: boolean; } /** @@ -47,6 +51,10 @@ export function activateDungeonSession( const previousDefinition = dungeonDefinitionById(useGameStore.getState().activeDungeonId); if (previousDefinition) releaseDungeonGltfCache(previousDefinition); + if (!options.preserveCharacter) { + useDungeonRunStore.getState().begin(dungeonId, difficultyId ?? definition.defaultDifficultyId, + options.runProgress, options.legacyResume); + } activateDungeonEncounterDefinitions( dungeonId, definition.encounters ?? [], diff --git a/src/game/dungeonTraversalRamps.test.ts b/src/game/dungeonTraversalRamps.test.ts index 1c9ac0c0..2afea815 100644 --- a/src/game/dungeonTraversalRamps.test.ts +++ b/src/game/dungeonTraversalRamps.test.ts @@ -37,8 +37,8 @@ describe("reviewed dungeon stair proxies", () => { it("bridges exactly the existing reviewed portal vertices and rejects stale asset coordinates", () => { const geometry = new BufferGeometry(); geometry.setAttribute("position", new Float32BufferAttribute([ - ...RAMP.navigationBridge.startPortal.flat(), - ...RAMP.navigationBridge.endPortal.flat(), + ...RAMP.navigationBridge!.startPortal.flat(), + ...RAMP.navigationBridge!.endPortal.flat(), ], 3)); geometry.setIndex([]); smoothDungeonStairGeometry(geometry, [RAMP], 0.05); diff --git a/src/game/dungeonTraversalRamps.ts b/src/game/dungeonTraversalRamps.ts index a24049d9..2f90fd5f 100644 --- a/src/game/dungeonTraversalRamps.ts +++ b/src/game/dungeonTraversalRamps.ts @@ -6,7 +6,7 @@ import type { DungeonTraversalRamp, Vector3Tuple } from "./dungeonTypes"; const MAX_STAIR_SURFACE_DELTA = 0.85; const PORTAL_VERTEX_TOLERANCE = 0.002; -function rampHeight( +export function dungeonRampHeight( ramp: DungeonTraversalRamp, x: number, z: number, @@ -36,10 +36,38 @@ export function smoothDungeonStairGeometry( surfaceClearance = 0, ): void { const position = geometry.getAttribute("position"); + // Fleet packages can contain many short flights and hundreds of thousands of + // vertices. Index their footprints once rather than checking every flight + // (and validating/recomputing its direction) for every collision vertex. + const cellSize = 16; + const cells = new Map(); + for (const ramp of ramps) { + dungeonRampHeight(ramp, ramp.start[0], ramp.start[2]); + const dx = ramp.end[0] - ramp.start[0]; + const dz = ramp.end[2] - ramp.start[2]; + const length = Math.hypot(dx, dz); + const xPadding = Math.abs(dz) / length * ramp.width / 2; + const zPadding = Math.abs(dx) / length * ramp.width / 2; + for (let x = Math.floor((Math.min(ramp.start[0], ramp.end[0]) - xPadding) / cellSize); + x <= Math.floor((Math.max(ramp.start[0], ramp.end[0]) + xPadding) / cellSize); x += 1) { + for (let z = Math.floor((Math.min(ramp.start[2], ramp.end[2]) - zPadding) / cellSize); + z <= Math.floor((Math.max(ramp.start[2], ramp.end[2]) + zPadding) / cellSize); z += 1) { + const key = `${x}:${z}`; + const bucket = cells.get(key) ?? []; + bucket.push({ ramp, dx, dz, length }); + cells.set(key, bucket); + } + } + } for (let vertex = 0; vertex < position.count; vertex += 1) { - for (const ramp of ramps) { - const height = rampHeight(ramp, position.getX(vertex), position.getZ(vertex)); - if (height === null || Math.abs(position.getY(vertex) - height) > MAX_STAIR_SURFACE_DELTA) continue; + const x = position.getX(vertex); const z = position.getZ(vertex); + for (const { ramp, dx, dz, length } of cells.get(`${Math.floor(x / cellSize)}:${Math.floor(z / cellSize)}`) ?? []) { + const offsetX = x - ramp.start[0]; const offsetZ = z - ramp.start[2]; + const along = (offsetX * dx + offsetZ * dz) / length; + const across = (offsetX * dz - offsetZ * dx) / length; + if (along < 0 || along > length || Math.abs(across) > ramp.width / 2) continue; + const height = ramp.start[1] + (ramp.end[1] - ramp.start[1]) * along / length; + if (Math.abs(position.getY(vertex) - height) > MAX_STAIR_SURFACE_DELTA) continue; position.setY(vertex, height + surfaceClearance); break; } @@ -57,7 +85,7 @@ export function bridgeDungeonStairNavigation( ? Array.from(geometry.index.array) : Array.from({ length: position.count }, (_, vertex) => vertex); const portalVertex = (ramp: DungeonTraversalRamp, point: Vector3Tuple): number => { - const height = rampHeight(ramp, point[0], point[2]); + const height = dungeonRampHeight(ramp, point[0], point[2]); if (height === null) throw new Error(`${ramp.id}: navigation portal is outside the stair ramp.`); for (let vertex = 0; vertex < position.count; vertex += 1) { if (Math.hypot( @@ -69,9 +97,15 @@ export function bridgeDungeonStairNavigation( throw new Error(`${ramp.id}: reviewed navigation portal no longer matches the source GLB.`); }; for (const ramp of ramps) { - const [a, b] = ramp.navigationBridge.startPortal.map((point) => portalVertex(ramp, point)); - const [c, d] = ramp.navigationBridge.endPortal.map((point) => portalVertex(ramp, point)); - indices.push(a, b, d, a, d, c); + for (const bridge of dungeonRampNavigationBridges(ramp)) { + const [a, b] = bridge.startPortal.map((point) => portalVertex(ramp, point)); + const [c, d] = bridge.endPortal.map((point) => portalVertex(ramp, point)); + indices.push(a, b, d, a, d, c); + } } geometry.setIndex(indices); } + +export function dungeonRampNavigationBridges(ramp: DungeonTraversalRamp) { + return [...(ramp.navigationBridge ? [ramp.navigationBridge] : []), ...(ramp.navigationBridges ?? [])]; +} diff --git a/src/game/dungeonTypes.ts b/src/game/dungeonTypes.ts index 0c110eca..e4a21937 100644 --- a/src/game/dungeonTypes.ts +++ b/src/game/dungeonTypes.ts @@ -162,11 +162,16 @@ export interface DungeonTraversalRamp { readonly start: Vector3Tuple; readonly end: Vector3Tuple; readonly width: number; - readonly navigationBridge: { + readonly navigationBridge?: { readonly linkId: string; readonly startPortal: readonly [Vector3Tuple, Vector3Tuple]; readonly endPortal: readonly [Vector3Tuple, Vector3Tuple]; }; + readonly navigationBridges?: readonly { + readonly linkId: string; + readonly startPortal: readonly [Vector3Tuple, Vector3Tuple]; + readonly endPortal: readonly [Vector3Tuple, Vector3Tuple]; + }[]; } export interface DungeonEncounterSpawnGroupDefinition { diff --git a/src/game/generated/dungeonLeaderboardCatalog.generated.json b/src/game/generated/dungeonLeaderboardCatalog.generated.json new file mode 100644 index 00000000..1d5d52f5 --- /dev/null +++ b/src/game/generated/dungeonLeaderboardCatalog.generated.json @@ -0,0 +1,3598 @@ +{ + "dungeons": [ + { + "id": "aeternal-circle", + "title": "Aeternal Circle", + "finalBossId": "", + "bosses": [], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [], + "finalBossId": "" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "ahnkahet-old-kingdom", + "title": "Ahn'kahet: The Old Kingdom", + "finalBossId": "creature-132298", + "bosses": [ + { + "id": "creature-131952", + "name": "Elder Nadox" + }, + { + "id": "creature-131951", + "name": "Prince Taldaram" + }, + { + "id": "creature-131953", + "name": "Jedoga Shadowseeker" + }, + { + "id": "creature-132298", + "name": "Herald Volazj" + }, + { + "id": "creature-202410", + "name": "Amanitar" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-131952", + "creature-131951", + "creature-131953", + "creature-132298" + ], + "finalBossId": "creature-132298" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-131952", + "creature-131951", + "creature-131953", + "creature-132298", + "creature-202410" + ], + "finalBossId": "creature-132298" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "arcatraz", + "title": "Tempest Keep: The Arcatraz", + "finalBossId": "encounter-500", + "bosses": [ + { + "id": "creature-138936", + "name": "Zereketh the Unbound" + }, + { + "id": "creature-138950", + "name": "Dalliah the Doomsayer" + }, + { + "id": "creature-138951", + "name": "Wrath-Scryer Soccothrates" + }, + { + "id": "encounter-500", + "name": "Harbinger Skyriss" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-138936", + "creature-138950", + "creature-138951", + "encounter-500" + ], + "finalBossId": "encounter-500" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-138936", + "creature-138950", + "creature-138951", + "encounter-500" + ], + "finalBossId": "encounter-500" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "auchenai-crypts", + "title": "Auchindoun: Auchenai Crypts", + "finalBossId": "creature-83453", + "bosses": [ + { + "id": "creature-83388", + "name": "Shirrak the Dead Watcher" + }, + { + "id": "creature-83453", + "name": "Exarch Maladaar" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-83388", + "creature-83453" + ], + "finalBossId": "creature-83453" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-83388", + "creature-83453" + ], + "finalBossId": "creature-83453" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "azjol-nerub", + "title": "Azjol-Nerub", + "finalBossId": "creature-132273", + "bosses": [ + { + "id": "creature-127214", + "name": "Krik'thir the Gatewatcher" + }, + { + "id": "creature-127401", + "name": "Hadronox" + }, + { + "id": "creature-132273", + "name": "Anub'arak" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-127214", + "creature-127401", + "creature-132273" + ], + "finalBossId": "creature-132273" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-127214", + "creature-127401", + "creature-132273" + ], + "finalBossId": "creature-132273" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "barren-caves", + "title": "Barren Caves", + "finalBossId": "rw-barren-caves-spawn-5951180", + "bosses": [ + { + "id": "rw-barren-caves-spawn-5951180", + "name": "Anglo" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-barren-caves-spawn-5951180" + ], + "finalBossId": "rw-barren-caves-spawn-5951180" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "black-morass", + "title": "The Black Morass", + "finalBossId": "encounter-291", + "bosses": [ + { + "id": "encounter-287", + "name": "Chrono Lord Deja" + }, + { + "id": "encounter-289", + "name": "Temporus" + }, + { + "id": "encounter-291", + "name": "Aeonus" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "encounter-287", + "encounter-289", + "encounter-291" + ], + "finalBossId": "encounter-291" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "encounter-287", + "encounter-289", + "encounter-291" + ], + "finalBossId": "encounter-291" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "blackfathom-deeps", + "title": "Blackfathom Deeps", + "finalBossId": "creature-27433", + "bosses": [ + { + "id": "creature-25732", + "name": "Ghamoo-ra" + }, + { + "id": "creature-26129", + "name": "Lady Sarevess" + }, + { + "id": "creature-18743", + "name": "Gelihast" + }, + { + "id": "creature-26173", + "name": "Lorgus Jett" + }, + { + "id": "creature-27424", + "name": "Twilight Lord Kelris" + }, + { + "id": "creature-27433", + "name": "Aku'mai" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-25732", + "creature-26129", + "creature-18743", + "creature-26173", + "creature-27424", + "creature-27433" + ], + "finalBossId": "creature-27433" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "blackrock-depths", + "title": "Blackrock Depths", + "finalBossId": "creature-47613", + "bosses": [ + { + "id": "creature-47250", + "name": "High Interrogator Gerstahn" + }, + { + "id": "creature-47304", + "name": "Lord Roccor" + }, + { + "id": "creature-46284", + "name": "Houndmaster Grebmar" + }, + { + "id": "creature-47771", + "name": "Pyromancer Loregrain" + }, + { + "id": "creature-47733", + "name": "Lord Incendius" + }, + { + "id": "creature-47774", + "name": "Warder Stilgiss" + }, + { + "id": "creature-47631", + "name": "Fineous Darkvire" + }, + { + "id": "creature-45885", + "name": "Bael'Gar" + }, + { + "id": "creature-45954", + "name": "General Angerforge" + }, + { + "id": "creature-45931", + "name": "Golem Lord Argelmach" + }, + { + "id": "creature-47778", + "name": "Ambassador Flamelash" + }, + { + "id": "creature-47477", + "name": "Magmus" + }, + { + "id": "creature-47613", + "name": "Emperor Dagran Thaurissan" + }, + { + "id": "blackrock-depths:ring-of-law-gorosh", + "name": "Gorosh the Dervish" + }, + { + "id": "blackrock-depths:watchman-doomgrip", + "name": "Watchman Doomgrip" + }, + { + "id": "blackrock-depths:tomb-of-seven", + "name": "The Seven" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-47250", + "creature-47304", + "creature-46284", + "creature-47771", + "creature-47733", + "creature-47774", + "creature-47631", + "creature-45885", + "creature-45954", + "creature-45931", + "creature-47778", + "creature-47477", + "creature-47613", + "blackrock-depths:ring-of-law-gorosh", + "blackrock-depths:watchman-doomgrip", + "blackrock-depths:tomb-of-seven" + ], + "finalBossId": "creature-47613" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "blackrock-spire", + "title": "Blackrock Spire", + "finalBossId": "creature-137971", + "bosses": [ + { + "id": "creature-45808", + "name": "Burning Felguard" + }, + { + "id": "creature-44422", + "name": "Crystal Fang" + }, + { + "id": "creature-43519", + "name": "Spirestone Battle Lord" + }, + { + "id": "creature-43523", + "name": "Spirestone Butcher" + }, + { + "id": "creature-43522", + "name": "Shadow Hunter Vosh'gajin" + }, + { + "id": "creature-45840", + "name": "War Master Voone" + }, + { + "id": "creature-44342", + "name": "Mother Smolderweb" + }, + { + "id": "creature-45749", + "name": "Quartermaster Zigris" + }, + { + "id": "creature-40245", + "name": "Halycon" + }, + { + "id": "creature-45757", + "name": "Overlord Wyrmthalak" + }, + { + "id": "creature-137817", + "name": "Pyroguard Emberseer" + }, + { + "id": "encounter-277", + "name": "Solakar Flamewreath" + }, + { + "id": "creature-137968", + "name": "Warchief Rend Blackhand" + }, + { + "id": "creature-137969", + "name": "The Beast" + }, + { + "id": "creature-137971", + "name": "General Drakkisath" + }, + { + "id": "blackrock-spire:batch-three:urok-doomhowl", + "name": "Urok Doomhowl" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-45808", + "creature-44422", + "creature-43519", + "creature-43523", + "creature-43522", + "creature-45840", + "creature-44342", + "creature-45749", + "creature-40245", + "creature-45757", + "creature-137817", + "encounter-277", + "creature-137968", + "creature-137969", + "creature-137971", + "blackrock-spire:batch-three:urok-doomhowl" + ], + "finalBossId": "creature-137971" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "blood-furnace", + "title": "Hellfire Citadel: The Blood Furnace", + "finalBossId": "creature-138524", + "bosses": [ + { + "id": "creature-138123", + "name": "The Maker" + }, + { + "id": "creature-138115", + "name": "Broggok" + }, + { + "id": "creature-138524", + "name": "Keli'dan the Breaker" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-138123", + "creature-138115", + "creature-138524" + ], + "finalBossId": "creature-138524" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-138123", + "creature-138115", + "creature-138524" + ], + "finalBossId": "creature-138524" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "botanica", + "title": "Tempest Keep: The Botanica", + "finalBossId": "creature-147002", + "bosses": [ + { + "id": "creature-147001", + "name": "Commander Sarannis" + }, + { + "id": "creature-147000", + "name": "High Botanist Freywinn" + }, + { + "id": "creature-147003", + "name": "Thorngrin the Tender" + }, + { + "id": "creature-147004", + "name": "Laj" + }, + { + "id": "creature-147002", + "name": "Warp Splinter" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-147001", + "creature-147000", + "creature-147003", + "creature-147004", + "creature-147002" + ], + "finalBossId": "creature-147002" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-147001", + "creature-147000", + "creature-147003", + "creature-147004", + "creature-147002" + ], + "finalBossId": "creature-147002" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "cave-water-dragon", + "title": "Cave Of The Water Dragon", + "finalBossId": "rw-cave-water-dragon-spawn-41840247", + "bosses": [ + { + "id": "rw-cave-water-dragon-spawn-41877089", + "name": "Template 104161" + }, + { + "id": "rw-cave-water-dragon-spawn-41840247", + "name": "Lytfir" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-cave-water-dragon-spawn-41877089", + "rw-cave-water-dragon-spawn-41840247" + ], + "finalBossId": "rw-cave-water-dragon-spawn-41840247" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "culling-of-stratholme", + "title": "The Culling of Stratholme", + "finalBossId": "encounter-296", + "bosses": [ + { + "id": "encounter-293", + "name": "Meathook" + }, + { + "id": "encounter-294", + "name": "Salramm the Fleshcrafter" + }, + { + "id": "encounter-295", + "name": "Chrono-Lord Epoch" + }, + { + "id": "encounter-296", + "name": "Mal'Ganis" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "encounter-293", + "encounter-294", + "encounter-295", + "encounter-296" + ], + "finalBossId": "encounter-296" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "encounter-293", + "encounter-294", + "encounter-295", + "encounter-296" + ], + "finalBossId": "encounter-296" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "deadmines", + "title": "The Deadmines", + "finalBossId": "creature-79336", + "bosses": [ + { + "id": "creature-79168", + "name": "Rhahk'zor" + }, + { + "id": "creature-79223", + "name": "Sneed" + }, + { + "id": "creature-79206", + "name": "Gilnid" + }, + { + "id": "creature-79337", + "name": "Mr. Smite" + }, + { + "id": "creature-79344", + "name": "Cookie" + }, + { + "id": "creature-79333", + "name": "Captain Greenskin" + }, + { + "id": "creature-79336", + "name": "Edwin VanCleef" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-79168", + "creature-79223", + "creature-79206", + "creature-79337", + "creature-79344", + "creature-79333", + "creature-79336" + ], + "finalBossId": "creature-79336" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "demon-stronghold", + "title": "Demon Stronghold", + "finalBossId": "rw-demon-stronghold-spawn-41854665", + "bosses": [ + { + "id": "rw-demon-stronghold-spawn-41854665", + "name": "Naos" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-demon-stronghold-spawn-41854665" + ], + "finalBossId": "rw-demon-stronghold-spawn-41854665" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "demon-stronghold-125", + "title": "Demon Stronghold (125)", + "finalBossId": "", + "bosses": [], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [], + "finalBossId": "" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "dire-maul", + "title": "Dire Maul", + "finalBossId": "creature-248076", + "bosses": [ + { + "id": "encounter-156", + "name": "Lord Hel'nurath" + }, + { + "id": "creature-248429", + "name": "Zevrim Thornhoof" + }, + { + "id": "creature-248418", + "name": "Hydrospawn" + }, + { + "id": "creature-248299", + "name": "Lethtendris" + }, + { + "id": "creature-248034", + "name": "Illyanna Ravenoak" + }, + { + "id": "creature-248025", + "name": "Magister Kalendris" + }, + { + "id": "creature-248037", + "name": "Immol'thar" + }, + { + "id": "creature-248077", + "name": "Guard Mol'dar" + }, + { + "id": "creature-248079", + "name": "Guard Fengus" + }, + { + "id": "creature-248080", + "name": "Guard Slip'kik" + }, + { + "id": "creature-248081", + "name": "Captain Kromcrush" + }, + { + "id": "creature-248076", + "name": "King Gordok" + }, + { + "id": "dire-maul:batch-three:alzzin", + "name": "Alzzin the Wildshaper" + }, + { + "id": "creature-248022", + "name": "Prince Tortheldrin" + }, + { + "id": "creature-248082", + "name": "Cho'Rush the Observer" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "encounter-156", + "creature-248429", + "creature-248418", + "creature-248299", + "creature-248034", + "creature-248025", + "creature-248037", + "creature-248077", + "creature-248079", + "creature-248080", + "creature-248081", + "creature-248076", + "dire-maul:batch-three:alzzin", + "creature-248022", + "creature-248082" + ], + "finalBossId": "creature-248076" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "draktharon-keep", + "title": "Drak'Tharon Keep", + "finalBossId": "creature-200143", + "bosses": [ + { + "id": "creature-127422", + "name": "Trollgore" + }, + { + "id": "creature-127424", + "name": "Novos the Summoner" + }, + { + "id": "creature-127507", + "name": "King Dred" + }, + { + "id": "creature-200143", + "name": "The Prophet Tharon'ja" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-127422", + "creature-127424", + "creature-127507", + "creature-200143" + ], + "finalBossId": "creature-200143" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-127422", + "creature-127424", + "creature-127507", + "creature-200143" + ], + "finalBossId": "creature-200143" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "dungeon-of-dalanis", + "title": "Dungeon Of Dalanis", + "finalBossId": "", + "bosses": [], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [], + "finalBossId": "" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "escape-from-durnholde", + "title": "The Escape From Durnholde", + "finalBossId": "encounter-281", + "bosses": [ + { + "id": "encounter-285", + "name": "Lieutenant Drake" + }, + { + "id": "encounter-283", + "name": "Captain Skarloc" + }, + { + "id": "encounter-281", + "name": "Epoch Hunter" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "encounter-285", + "encounter-283", + "encounter-281" + ], + "finalBossId": "encounter-281" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "encounter-285", + "encounter-283", + "encounter-281" + ], + "finalBossId": "encounter-281" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "forge-of-souls", + "title": "The Forge of Souls", + "finalBossId": "creature-201736", + "bosses": [ + { + "id": "creature-201707", + "name": "Bronjahm" + }, + { + "id": "creature-201736", + "name": "Devourer of Souls" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-201707", + "creature-201736" + ], + "finalBossId": "creature-201736" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-201707", + "creature-201736" + ], + "finalBossId": "creature-201736" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "forgotten-mine", + "title": "Forgotten Mine", + "finalBossId": "encounter-5069", + "bosses": [ + { + "id": "encounter-5069", + "name": "Geolord Vek" + } + ], + "difficulties": [ + { + "id": "manastorm", + "name": "manastorm", + "bossIds": [ + "encounter-5069" + ], + "finalBossId": "encounter-5069" + } + ], + "defaultDifficultyId": "manastorm" + }, + { + "id": "forsaken-abbey", + "title": "Forsaken Abbey", + "finalBossId": "rw-fa-spawn-7854", + "bosses": [ + { + "id": "rw-fa-spawn-7384", + "name": "Necromage Keeper 1" + }, + { + "id": "rw-fa-spawn-7426", + "name": "Necromage Keeper 2" + }, + { + "id": "rw-fa-spawn-7379", + "name": "Necromage Keeper 3" + }, + { + "id": "rw-fa-spawn-7425", + "name": "Necromage Keeper 4" + }, + { + "id": "rw-fa-spawn-7472", + "name": "Flowing Chaos" + }, + { + "id": "rw-fa-spawn-7755", + "name": "Cruel Eater" + }, + { + "id": "rw-fa-spawn-7844", + "name": "Hollow Shell" + }, + { + "id": "rw-fa-spawn-7854", + "name": "Demon Witch Ancalon" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-fa-spawn-7384", + "rw-fa-spawn-7426", + "rw-fa-spawn-7379", + "rw-fa-spawn-7425", + "rw-fa-spawn-7472", + "rw-fa-spawn-7755", + "rw-fa-spawn-7844", + "rw-fa-spawn-7854" + ], + "finalBossId": "rw-fa-spawn-7854" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "frozen-reach", + "title": "Frozen Reach", + "finalBossId": "encounter-2168", + "bosses": [ + { + "id": "encounter-2168", + "name": "Alva" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "encounter-2168" + ], + "finalBossId": "encounter-2168" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "gnomeregan", + "title": "Gnomeregan", + "finalBossId": "gnomeregan:batch-three:grubbis", + "bosses": [ + { + "id": "creature-30137", + "name": "Viscous Fallout" + }, + { + "id": "creature-30133", + "name": "Electrocutioner 6000" + }, + { + "id": "creature-30121", + "name": "Crowd Pummeler 9-60" + }, + { + "id": "creature-30139", + "name": "Mekgineer Thermaplugg" + }, + { + "id": "gnomeregan:batch-three:grubbis", + "name": "Grubbis" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-30137", + "creature-30133", + "creature-30121", + "creature-30139", + "gnomeregan:batch-three:grubbis" + ], + "finalBossId": "gnomeregan:batch-three:grubbis" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "grafu-castle", + "title": "Grafu Castle", + "finalBossId": "rw-grafu-castle-scripted-larvanger-barkud", + "bosses": [ + { + "id": "rw-grafu-castle-scripted-cayus", + "name": "Cayus" + }, + { + "id": "rw-grafu-castle-scripted-ardmond", + "name": "Ardmond and Issac" + }, + { + "id": "rw-grafu-castle-scripted-boro-boro-brothers", + "name": "Boro-Boro Brothers" + }, + { + "id": "rw-grafu-castle-scripted-conjurer-chapeaunoir", + "name": "Conjurer Chapeaunoir" + }, + { + "id": "rw-grafu-castle-scripted-larvanger-barkud", + "name": "Larvanger Barkud" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-grafu-castle-scripted-cayus", + "rw-grafu-castle-scripted-ardmond", + "rw-grafu-castle-scripted-boro-boro-brothers", + "rw-grafu-castle-scripted-conjurer-chapeaunoir", + "rw-grafu-castle-scripted-larvanger-barkud" + ], + "finalBossId": "rw-grafu-castle-scripted-larvanger-barkud" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "gundrak", + "title": "Gundrak", + "finalBossId": "creature-127044", + "bosses": [ + { + "id": "creature-127042", + "name": "Slad'ran" + }, + { + "id": "encounter-385", + "name": "Drakkari Colossus" + }, + { + "id": "creature-127043", + "name": "Moorabi" + }, + { + "id": "creature-127044", + "name": "Gal'darah" + }, + { + "id": "encounter-389", + "name": "Eck the Ferocious" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-127042", + "encounter-385", + "creature-127043", + "creature-127044", + "encounter-389" + ], + "finalBossId": "creature-127044" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-127042", + "encounter-385", + "creature-127043", + "creature-127044", + "encounter-389" + ], + "finalBossId": "creature-127044" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "hall-of-survivors", + "title": "Hall Of Survivors", + "finalBossId": "rw-hall-of-survivors-spawn-5995182", + "bosses": [ + { + "id": "rw-hall-of-survivors-spawn-5991781", + "name": "Andaphelmor" + }, + { + "id": "rw-hall-of-survivors-spawn-41850487", + "name": "Andriol" + }, + { + "id": "rw-hall-of-survivors-spawn-11962818", + "name": "Divine Punishment Pursuer" + }, + { + "id": "rw-hall-of-survivors-spawn-11962813", + "name": "Prison Pursuer" + }, + { + "id": "rw-hall-of-survivors-spawn-11962804", + "name": "Horror Seeker" + }, + { + "id": "rw-hall-of-survivors-spawn-5995573", + "name": "Glamo" + }, + { + "id": "rw-hall-of-survivors-spawn-11962041", + "name": "Mantarick" + }, + { + "id": "rw-hall-of-survivors-spawn-11962386", + "name": "Guldamor" + }, + { + "id": "rw-hall-of-survivors-spawn-5995182", + "name": "Zygro" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-hall-of-survivors-spawn-5991781", + "rw-hall-of-survivors-spawn-41850487", + "rw-hall-of-survivors-spawn-11962818", + "rw-hall-of-survivors-spawn-11962813", + "rw-hall-of-survivors-spawn-11962804", + "rw-hall-of-survivors-spawn-5995573", + "rw-hall-of-survivors-spawn-11962041", + "rw-hall-of-survivors-spawn-11962386", + "rw-hall-of-survivors-spawn-5995182" + ], + "finalBossId": "rw-hall-of-survivors-spawn-5995182" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "halls-of-lightning", + "title": "Halls of Lightning", + "finalBossId": "creature-126985", + "bosses": [ + { + "id": "creature-126981", + "name": "General Bjarngrim" + }, + { + "id": "creature-126982", + "name": "Volkhan" + }, + { + "id": "creature-126873", + "name": "Ionar" + }, + { + "id": "creature-126985", + "name": "Loken" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-126981", + "creature-126982", + "creature-126873", + "creature-126985" + ], + "finalBossId": "creature-126985" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-126981", + "creature-126982", + "creature-126873", + "creature-126985" + ], + "finalBossId": "creature-126985" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "halls-of-reflection", + "title": "Halls of Reflection", + "finalBossId": "halls-of-reflection:escape-objective", + "bosses": [ + { + "id": "creature-1971983", + "name": "Falric" + }, + { + "id": "creature-1971984", + "name": "Marwyn" + }, + { + "id": "halls-of-reflection:escape-objective", + "name": "Escape from the Lich King" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-1971983", + "creature-1971984", + "halls-of-reflection:escape-objective" + ], + "finalBossId": "halls-of-reflection:escape-objective" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-1971983", + "creature-1971984", + "halls-of-reflection:escape-objective" + ], + "finalBossId": "halls-of-reflection:escape-objective" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "halls-of-stone", + "title": "Halls of Stone", + "finalBossId": "creature-126792", + "bosses": [ + { + "id": "creature-126790", + "name": "Krystallus" + }, + { + "id": "creature-126789", + "name": "Maiden of Grief" + }, + { + "id": "encounter-567", + "name": "Tribunal of Ages" + }, + { + "id": "creature-126792", + "name": "Sjonnir the Ironshaper" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-126790", + "creature-126789", + "encounter-567", + "creature-126792" + ], + "finalBossId": "creature-126792" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-126790", + "creature-126789", + "encounter-567", + "creature-126792" + ], + "finalBossId": "creature-126792" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "hellfire-ramparts", + "title": "Hellfire Citadel: Ramparts", + "finalBossId": "hellfire-ramparts:vazruden-and-nazan", + "bosses": [ + { + "id": "creature-202692", + "name": "Watchkeeper Gargolmar" + }, + { + "id": "creature-202694", + "name": "Omor the Unscarred" + }, + { + "id": "hellfire-ramparts:vazruden-and-nazan", + "name": "Vazruden and Nazan" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-202692", + "creature-202694", + "hellfire-ramparts:vazruden-and-nazan" + ], + "finalBossId": "hellfire-ramparts:vazruden-and-nazan" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-202692", + "creature-202694", + "hellfire-ramparts:vazruden-and-nazan" + ], + "finalBossId": "hellfire-ramparts:vazruden-and-nazan" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "hoto", + "title": "HOTO", + "finalBossId": "rw-hoto-spawn-5979747", + "bosses": [ + { + "id": "rw-hoto-spawn-5979062", + "name": "Medusa" + }, + { + "id": "rw-hoto-spawn-5979861", + "name": "Geba" + }, + { + "id": "rw-hoto-spawn-5979036", + "name": "Bloodthirsty Claw" + }, + { + "id": "rw-hoto-spawn-5979022", + "name": "Jiasha" + }, + { + "id": "rw-hoto-spawn-5979747", + "name": "Teeth of the Reef" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-hoto-spawn-5979062", + "rw-hoto-spawn-5979861", + "rw-hoto-spawn-5979036", + "rw-hoto-spawn-5979022", + "rw-hoto-spawn-5979747" + ], + "finalBossId": "rw-hoto-spawn-5979747" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "ice-dwarf-kingdom", + "title": "Ice Dwarf Kingdom", + "finalBossId": "rw-ice-dwarf-kingdom-spawn-5975908", + "bosses": [ + { + "id": "rw-ice-dwarf-kingdom-spawn-5975879", + "name": "Icebound Guardian" + }, + { + "id": "rw-ice-dwarf-kingdom-spawn-5976133", + "name": "Pangkor" + }, + { + "id": "rw-ice-dwarf-kingdom-spawn-5975890", + "name": "Crasset Hairyfoot" + }, + { + "id": "rw-ice-dwarf-kingdom-spawn-5976134", + "name": "Thynos" + }, + { + "id": "rw-ice-dwarf-kingdom-spawn-5975908", + "name": "Mole King" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-ice-dwarf-kingdom-spawn-5975879", + "rw-ice-dwarf-kingdom-spawn-5976133", + "rw-ice-dwarf-kingdom-spawn-5975890", + "rw-ice-dwarf-kingdom-spawn-5976134", + "rw-ice-dwarf-kingdom-spawn-5975908" + ], + "finalBossId": "rw-ice-dwarf-kingdom-spawn-5975908" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "kal-turok-hive", + "title": "Kal Turok Hive", + "finalBossId": "", + "bosses": [], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [], + "finalBossId": "" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "kalins-shrine", + "title": "Kalin's Shrine", + "finalBossId": "rw-kalins-shrine-spawn-5952255", + "bosses": [ + { + "id": "rw-kalins-shrine-spawn-5952167", + "name": "Iron Rune Warrior" + }, + { + "id": "rw-kalins-shrine-spawn-5952260", + "name": "Yusalien" + }, + { + "id": "rw-kalins-shrine-spawn-5952226", + "name": "Locatha" + }, + { + "id": "rw-kalins-shrine-spawn-5952258", + "name": "Goddess of Art's Disciple" + }, + { + "id": "rw-kalins-shrine-spawn-5952255", + "name": "Ensia" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-kalins-shrine-spawn-5952167", + "rw-kalins-shrine-spawn-5952260", + "rw-kalins-shrine-spawn-5952226", + "rw-kalins-shrine-spawn-5952258", + "rw-kalins-shrine-spawn-5952255" + ], + "finalBossId": "rw-kalins-shrine-spawn-5952255" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "kawaks-tomb", + "title": "Kawak's Tomb", + "finalBossId": "rw-kawaks-tomb-spawn-41888272", + "bosses": [ + { + "id": "rw-kawaks-tomb-spawn-41888272", + "name": "Linobia" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-kawaks-tomb-spawn-41888272" + ], + "finalBossId": "rw-kawaks-tomb-spawn-41888272" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "kulech-bones-nest", + "title": "Kulech Bones Nest", + "finalBossId": "rw-kulech-bones-nest-spawn-41994085", + "bosses": [ + { + "id": "rw-kulech-bones-nest-spawn-41994085", + "name": "Template 107306" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-kulech-bones-nest-spawn-41994085" + ], + "finalBossId": "rw-kulech-bones-nest-spawn-41994085" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "lair-demon-dragon", + "title": "Lair Of The Demon Dragon", + "finalBossId": "", + "bosses": [], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [], + "finalBossId": "" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "magisters-terrace", + "title": "Magister's Terrace", + "finalBossId": "creature-96762", + "bosses": [ + { + "id": "creature-96875", + "name": "Selin Fireheart" + }, + { + "id": "creature-96876", + "name": "Vexallus" + }, + { + "id": "creature-96762", + "name": "Kael'thas Sunstrider" + }, + { + "id": "creature-96966", + "name": "Priestess Delrissa" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-96875", + "creature-96876", + "creature-96762", + "creature-96966" + ], + "finalBossId": "creature-96762" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-96875", + "creature-96876", + "creature-96762", + "creature-96966" + ], + "finalBossId": "creature-96762" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "mana-tombs", + "title": "Auchindoun: Mana-Tombs", + "finalBossId": "creature-91162", + "bosses": [ + { + "id": "creature-91163", + "name": "Pandemonius" + }, + { + "id": "creature-91161", + "name": "Tavarok" + }, + { + "id": "creature-91162", + "name": "Nexus-Prince Shaffar" + }, + { + "id": "mana-tombs:yor", + "name": "Yor" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-91163", + "creature-91161", + "creature-91162", + "mana-tombs:yor" + ], + "finalBossId": "creature-91162" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-91163", + "creature-91161", + "creature-91162", + "mana-tombs:yor" + ], + "finalBossId": "creature-91162" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "maraudon", + "title": "Maraudon", + "finalBossId": "creature-55342", + "bosses": [ + { + "id": "creature-54445", + "name": "Noxxion" + }, + { + "id": "creature-54474", + "name": "Razorlash" + }, + { + "id": "creature-54579", + "name": "Lord Vyletongue" + }, + { + "id": "creature-55104", + "name": "Celebras the Cursed" + }, + { + "id": "creature-89018", + "name": "Landslide" + }, + { + "id": "creature-56538", + "name": "Tinkerer Gizlock" + }, + { + "id": "creature-55342", + "name": "Princess Theradras" + }, + { + "id": "creature-203506", + "name": "Meshlok the Harvester" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-54445", + "creature-54474", + "creature-54579", + "creature-55104", + "creature-89018", + "creature-56538", + "creature-55342", + "creature-203506" + ], + "finalBossId": "creature-55342" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "mechanar", + "title": "Tempest Keep: The Mechanar", + "finalBossId": "creature-138823", + "bosses": [ + { + "id": "creature-138822", + "name": "Mechano-Lord Capacitus" + }, + { + "id": "creature-138824", + "name": "Nethermancer Sepethrea" + }, + { + "id": "creature-138823", + "name": "Pathaleon the Calculator" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-138822", + "creature-138824", + "creature-138823" + ], + "finalBossId": "creature-138823" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-138822", + "creature-138824", + "creature-138823" + ], + "finalBossId": "creature-138823" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "mystic-altar", + "title": "Mystic Altar", + "finalBossId": "rw-mystic-altar-spawn-13506", + "bosses": [ + { + "id": "rw-mystic-altar-spawn-11831", + "name": "Razeela" + }, + { + "id": "rw-mystic-altar-spawn-11448", + "name": "Dorlos" + }, + { + "id": "rw-mystic-altar-spawn-13506", + "name": "Cursed lron Golem" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-mystic-altar-spawn-11831", + "rw-mystic-altar-spawn-11448", + "rw-mystic-altar-spawn-13506" + ], + "finalBossId": "rw-mystic-altar-spawn-13506" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "paspers-shrine", + "title": "Pasper's Shrine", + "finalBossId": "rw-paspers-shrine-spawn-5942136", + "bosses": [ + { + "id": "rw-paspers-shrine-spawn-5942127", + "name": "Blackhorn Nisorn" + }, + { + "id": "rw-paspers-shrine-spawn-4407635", + "name": "Blackhorn Dorglas" + }, + { + "id": "rw-paspers-shrine-spawn-5947918", + "name": "Blackhorn Bareth" + }, + { + "id": "rw-paspers-shrine-spawn-4407640", + "name": "Blackhorn Afalen" + }, + { + "id": "rw-paspers-shrine-spawn-5942136", + "name": "Blackhorn Hafiz" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-paspers-shrine-spawn-5942127", + "rw-paspers-shrine-spawn-4407635", + "rw-paspers-shrine-spawn-5947918", + "rw-paspers-shrine-spawn-4407640", + "rw-paspers-shrine-spawn-5942136" + ], + "finalBossId": "rw-paspers-shrine-spawn-5942136" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "pit-of-saron", + "title": "Pit of Saron", + "finalBossId": "encounter-837", + "bosses": [ + { + "id": "creature-201992", + "name": "Forgemaster Garfrost" + }, + { + "id": "creature-202042", + "name": "Krick" + }, + { + "id": "encounter-837", + "name": "Overlrod Tyrannus" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-201992", + "creature-202042", + "encounter-837" + ], + "finalBossId": "encounter-837" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-201992", + "creature-202042", + "encounter-837" + ], + "finalBossId": "encounter-837" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "ragefire-chasm", + "title": "Ragefire Chasm", + "finalBossId": "creature-48750", + "bosses": [ + { + "id": "creature-48644", + "name": "Oggleflint" + }, + { + "id": "creature-48706", + "name": "Taragaman the Hungerer" + }, + { + "id": "creature-48740", + "name": "Jergosh the Invoker" + }, + { + "id": "creature-48750", + "name": "Bazzalan" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-48644", + "creature-48706", + "creature-48740", + "creature-48750" + ], + "finalBossId": "creature-48750" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "raksha-temple", + "title": "Raksha Temple", + "finalBossId": "rw-raksha-temple-spawn-41884920", + "bosses": [ + { + "id": "rw-raksha-temple-spawn-41884920", + "name": "Shadoj" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-raksha-temple-spawn-41884920" + ], + "finalBossId": "rw-raksha-temple-spawn-41884920" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "razorfen-downs", + "title": "Razorfen Downs", + "finalBossId": "encounter-2618", + "bosses": [ + { + "id": "encounter-434", + "name": "Tuten'kash" + }, + { + "id": "creature-87187", + "name": "Mordresh Fire Eye" + }, + { + "id": "creature-87242", + "name": "Glutton" + }, + { + "id": "creature-87209", + "name": "Amnennar the Coldbringer" + }, + { + "id": "creature-247108", + "name": "Ragglesnout" + }, + { + "id": "encounter-2618", + "name": "Plaguemaw the Rotting" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "encounter-434", + "creature-87187", + "creature-87242", + "creature-87209", + "creature-247108", + "encounter-2618" + ], + "finalBossId": "encounter-2618" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "razorfen-kraul", + "title": "Razorfen Kraul", + "finalBossId": "creature-87486", + "bosses": [ + { + "id": "creature-87346", + "name": "Roogug" + }, + { + "id": "creature-87357", + "name": "Aggem Thorncurse" + }, + { + "id": "creature-87400", + "name": "Death Speaker Jargba" + }, + { + "id": "creature-87386", + "name": "Overlord Ramtusk" + }, + { + "id": "creature-87487", + "name": "Charlga Razorflank" + }, + { + "id": "creature-87486", + "name": "Agathelos the Raging" + }, + { + "id": "creature-1975863", + "name": "Earthcaller Halmgar" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-87346", + "creature-87357", + "creature-87400", + "creature-87386", + "creature-87487", + "creature-87486", + "creature-1975863" + ], + "finalBossId": "creature-87486" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "sand-vortex", + "title": "Sand Vortex", + "finalBossId": "rw-sand-vortex-spawn-41854657", + "bosses": [ + { + "id": "rw-sand-vortex-spawn-41854657", + "name": "Sandlord Sesphiroth" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-sand-vortex-spawn-41854657" + ], + "finalBossId": "rw-sand-vortex-spawn-41854657" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "sardo-castle", + "title": "Sardo Castle", + "finalBossId": "rw-sardo-castle-spawn-41937073", + "bosses": [ + { + "id": "rw-sardo-castle-spawn-41937096", + "name": "Undead Guard" + }, + { + "id": "rw-sardo-castle-spawn-41934140", + "name": "Undead Guard" + }, + { + "id": "rw-sardo-castle-spawn-41937073", + "name": "Undead Guard" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-sardo-castle-spawn-41937096", + "rw-sardo-castle-spawn-41934140", + "rw-sardo-castle-spawn-41937073" + ], + "finalBossId": "rw-sardo-castle-spawn-41937073" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "scarlet-monastery", + "title": "Scarlet Monastery", + "finalBossId": "creature-39946", + "bosses": [ + { + "id": "creature-39934", + "name": "Bloodmage Thalnos" + }, + { + "id": "creature-40088", + "name": "Houndmaster Loksey" + }, + { + "id": "creature-40124", + "name": "Arcanist Doan" + }, + { + "id": "creature-89097", + "name": "Herod" + }, + { + "id": "creature-39946", + "name": "High Inquisitor Whitemane" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-39934", + "creature-40088", + "creature-40124", + "creature-89097", + "creature-39946" + ], + "finalBossId": "creature-39946" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "scholomance", + "title": "Scholomance", + "finalBossId": "creature-247104", + "bosses": [ + { + "id": "creature-48807", + "name": "Jandice Barov" + }, + { + "id": "creature-48910", + "name": "Rattlegore" + }, + { + "id": "creature-48867", + "name": "Instructor Malicia" + }, + { + "id": "creature-48925", + "name": "Doctor Theolen Krastinov" + }, + { + "id": "creature-48946", + "name": "Lorekeeper Polkelt" + }, + { + "id": "creature-48863", + "name": "Lord Alexei Barov" + }, + { + "id": "creature-48857", + "name": "Lady Illucia Barov" + }, + { + "id": "creature-48850", + "name": "Ras Frostwhisper" + }, + { + "id": "scholomance:kirtonos-the-herald", + "name": "Kirtonos the Herald" + }, + { + "id": "creature-247104", + "name": "Darkmaster Gandling" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-48807", + "creature-48910", + "creature-48867", + "creature-48925", + "creature-48946", + "creature-48863", + "creature-48857", + "creature-48850", + "scholomance:kirtonos-the-herald", + "creature-247104" + ], + "finalBossId": "creature-247104" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "sethekk-halls", + "title": "Auchindoun: Sethekk Halls", + "finalBossId": "creature-138719", + "bosses": [ + { + "id": "creature-138718", + "name": "Darkweaver Syth" + }, + { + "id": "creature-138719", + "name": "Talon King Ikiss" + }, + { + "id": "sethekk-halls:anzu", + "name": "Anzu" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-138718", + "creature-138719", + "sethekk-halls:anzu" + ], + "finalBossId": "creature-138719" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-138718", + "creature-138719", + "sethekk-halls:anzu" + ], + "finalBossId": "creature-138719" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "shadow-labyrinth", + "title": "Auchindoun: Shadow Labyrinth", + "finalBossId": "creature-146104", + "bosses": [ + { + "id": "creature-146105", + "name": "Ambassador Hellmaw" + }, + { + "id": "creature-146103", + "name": "Blackheart the Inciter" + }, + { + "id": "creature-146106", + "name": "Grandmaster Vorpil" + }, + { + "id": "creature-146104", + "name": "Murmur" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-146105", + "creature-146103", + "creature-146106", + "creature-146104" + ], + "finalBossId": "creature-146104" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-146105", + "creature-146103", + "creature-146106", + "creature-146104" + ], + "finalBossId": "creature-146104" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "shadowfang-keep", + "title": "Shadowfang Keep", + "finalBossId": "creature-16255", + "bosses": [ + { + "id": "creature-16242", + "name": "Rethilgore" + }, + { + "id": "creature-16254", + "name": "Baron Silverlaine" + }, + { + "id": "creature-16256", + "name": "Commander Springvale" + }, + { + "id": "creature-16257", + "name": "Odo the Blindwatcher" + }, + { + "id": "creature-18714", + "name": "Fenrus the Devourer" + }, + { + "id": "creature-16237", + "name": "Wolf Master Nandos" + }, + { + "id": "creature-16255", + "name": "Archmage Arugal" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-16242", + "creature-16254", + "creature-16256", + "creature-16257", + "creature-18714", + "creature-16237", + "creature-16255" + ], + "finalBossId": "creature-16255" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "shattered-halls", + "title": "Hellfire Citadel: The Shattered Halls", + "finalBossId": "creature-151195", + "bosses": [ + { + "id": "creature-151089", + "name": "Grand Warlock Nethekurse" + }, + { + "id": "creature-151179", + "name": "Warbringer O'mrogg" + }, + { + "id": "creature-151195", + "name": "Warchief Kargath Bladefist" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-151089", + "creature-151179", + "creature-151195" + ], + "finalBossId": "creature-151195" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-151089", + "creature-151179", + "creature-151195" + ], + "finalBossId": "creature-151195" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "slave-pens", + "title": "Coilfang: The Slave Pens", + "finalBossId": "creature-80260", + "bosses": [ + { + "id": "creature-79362", + "name": "Mennu the Betrayer" + }, + { + "id": "creature-79339", + "name": "Rokmar the Crackler" + }, + { + "id": "creature-80260", + "name": "Quagmirran" + }, + { + "id": "slave-pens:ahune", + "name": "Ahune" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-79362", + "creature-79339", + "creature-80260", + "slave-pens:ahune" + ], + "finalBossId": "creature-80260" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-79362", + "creature-79339", + "creature-80260", + "slave-pens:ahune" + ], + "finalBossId": "creature-80260" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "steamvault", + "title": "Coilfang: The Steamvault", + "finalBossId": "creature-142045", + "bosses": [ + { + "id": "creature-142044", + "name": "Hydromancer Thespia" + }, + { + "id": "creature-142043", + "name": "Mekgineer Steamrigger" + }, + { + "id": "creature-142045", + "name": "Warlord Kalithresh" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-142044", + "creature-142043", + "creature-142045" + ], + "finalBossId": "creature-142045" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-142044", + "creature-142043", + "creature-142045" + ], + "finalBossId": "creature-142045" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "stormwind-stockade", + "title": "Stormwind Stockade", + "finalBossId": "creature-79116", + "bosses": [ + { + "id": "creature-84027", + "name": "Targorr the Dread" + }, + { + "id": "creature-79088", + "name": "Kam Deepfury" + }, + { + "id": "creature-79098", + "name": "Hamhock" + }, + { + "id": "creature-79124", + "name": "Bazil Thredd" + }, + { + "id": "creature-79116", + "name": "Dextren Ward" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-84027", + "creature-79088", + "creature-79098", + "creature-79124", + "creature-79116" + ], + "finalBossId": "creature-79116" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "stratholme", + "title": "Stratholme", + "finalBossId": "creature-54237", + "bosses": [ + { + "id": "creature-247226", + "name": "The Unforgiven" + }, + { + "id": "creature-54433", + "name": "Cannon Master Willey" + }, + { + "id": "creature-52122", + "name": "Malor the Zealous" + }, + { + "id": "creature-52148", + "name": "Archivist Galford" + }, + { + "id": "encounter-478", + "name": "Balnazzar" + }, + { + "id": "creature-54238", + "name": "Baroness Anastari" + }, + { + "id": "creature-54239", + "name": "Nerub'enkan" + }, + { + "id": "creature-54240", + "name": "Maleki the Pallid" + }, + { + "id": "creature-54237", + "name": "Magistrate Barthilas" + }, + { + "id": "creature-54241", + "name": "Baron Rivendare" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-247226", + "creature-54433", + "creature-52122", + "creature-52148", + "encounter-478", + "creature-54238", + "creature-54239", + "creature-54240", + "creature-54237", + "creature-54241" + ], + "finalBossId": "creature-54237" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "sunken-temple", + "title": "Sunken Temple", + "finalBossId": "creature-39842", + "bosses": [ + { + "id": "creature-34521", + "name": "Atal'alarion" + }, + { + "id": "creature-39737", + "name": "Jammal'an and Ogom" + }, + { + "id": "encounter-492", + "name": "Avatar of Hakkar" + }, + { + "id": "creature-39842", + "name": "Shade of Eranikus" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-34521", + "creature-39737", + "encounter-492", + "creature-39842" + ], + "finalBossId": "creature-39842" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "the-nexus", + "title": "The Nexus", + "finalBossId": "creature-126453", + "bosses": [ + { + "id": "creature-4764", + "name": "Frozen Commander" + }, + { + "id": "creature-126480", + "name": "Grand Magus Telestra" + }, + { + "id": "creature-126599", + "name": "Anomalus" + }, + { + "id": "creature-126663", + "name": "Ormorok the Tree-Shaper" + }, + { + "id": "creature-126453", + "name": "Keristrasza" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-126480", + "creature-126599", + "creature-126663", + "creature-126453" + ], + "finalBossId": "creature-126453" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-4764", + "creature-126480", + "creature-126599", + "creature-126663", + "creature-126453" + ], + "finalBossId": "creature-126453" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "the-oculus", + "title": "The Oculus", + "finalBossId": "creature-1971378", + "bosses": [ + { + "id": "creature-100172", + "name": "Drakos the Interrogator" + }, + { + "id": "creature-100235", + "name": "Varos Cloudstrider" + }, + { + "id": "creature-100220", + "name": "Mage-Lord Urom" + }, + { + "id": "creature-1971378", + "name": "Ley-Guardian Eregos" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-100172", + "creature-100235", + "creature-100220", + "creature-1971378" + ], + "finalBossId": "creature-1971378" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-100172", + "creature-100235", + "creature-100220", + "creature-1971378" + ], + "finalBossId": "creature-1971378" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "the-origin", + "title": "The Origin", + "finalBossId": "rw-the-origin-spawn-41838293", + "bosses": [ + { + "id": "rw-the-origin-spawn-5990046", + "name": "Life Leecher" + }, + { + "id": "rw-the-origin-spawn-41838293", + "name": "Holy Tree Falynum" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-the-origin-spawn-5990046", + "rw-the-origin-spawn-41838293" + ], + "finalBossId": "rw-the-origin-spawn-41838293" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "tinkertech-showdown", + "title": "Tinkertech Showdown", + "finalBossId": "encounter-9013", + "bosses": [ + { + "id": "encounter-9013", + "name": "Mechatron" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "encounter-9013" + ], + "finalBossId": "encounter-9013" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "tomb-seven-heroes", + "title": "Tomb Of The Seven Heroes", + "finalBossId": "", + "bosses": [], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [], + "finalBossId": "" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "treasure-trove", + "title": "Treasure Trove", + "finalBossId": "rw-treasure-trove-spawn-5952494", + "bosses": [ + { + "id": "rw-treasure-trove-spawn-5955237", + "name": "Lookout Luke" + }, + { + "id": "rw-treasure-trove-spawn-5952828", + "name": "Snow Blake" + }, + { + "id": "rw-treasure-trove-spawn-5952494", + "name": "Boulder Sidaar" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-treasure-trove-spawn-5955237", + "rw-treasure-trove-spawn-5952828", + "rw-treasure-trove-spawn-5952494" + ], + "finalBossId": "rw-treasure-trove-spawn-5952494" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "trial-of-the-champion", + "title": "Trial of the Champion", + "finalBossId": "encounter-340", + "bosses": [ + { + "id": "encounter-33400", + "name": "Mokra the Skullcrusher" + }, + { + "id": "encounter-33401", + "name": "Eressea Dawnsinger" + }, + { + "id": "encounter-33402", + "name": "Runok Wildmane" + }, + { + "id": "encounter-338", + "name": "Eadric the Pure" + }, + { + "id": "encounter-340", + "name": "The Black Knight" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "encounter-33400", + "encounter-33401", + "encounter-33402", + "encounter-338", + "encounter-340" + ], + "finalBossId": "encounter-340" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "encounter-33400", + "encounter-33401", + "encounter-33402", + "encounter-338", + "encounter-340" + ], + "finalBossId": "encounter-340" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "uldaman", + "title": "Uldaman", + "finalBossId": "creature-29650", + "bosses": [ + { + "id": "creature-27532", + "name": "Revelosh" + }, + { + "id": "creature-54080", + "name": "Ironaya" + }, + { + "id": "creature-27553", + "name": "Ancient Stone Keeper" + }, + { + "id": "creature-27491", + "name": "Galgann Firehammer" + }, + { + "id": "creature-27528", + "name": "Grimlok" + }, + { + "id": "creature-33537", + "name": "Archaedas" + }, + { + "id": "creature-29650", + "name": "Obsidian Sentinel" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-27532", + "creature-54080", + "creature-27553", + "creature-27491", + "creature-27528", + "creature-33537", + "creature-29650" + ], + "finalBossId": "creature-29650" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "underbog", + "title": "Coilfang: The Underbog", + "finalBossId": "creature-138323", + "bosses": [ + { + "id": "creature-138367", + "name": "Hungarfen" + }, + { + "id": "creature-138392", + "name": "Ghaz'an" + }, + { + "id": "creature-138500", + "name": "Swamplord Musel'ek" + }, + { + "id": "creature-138323", + "name": "The Black Stalker" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-138367", + "creature-138392", + "creature-138500", + "creature-138323" + ], + "finalBossId": "creature-138323" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-138367", + "creature-138392", + "creature-138500", + "creature-138323" + ], + "finalBossId": "creature-138323" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "utgarde-keep", + "title": "Utgarde Keep", + "finalBossId": "creature-126026", + "bosses": [ + { + "id": "creature-126025", + "name": "Prince Keleseth" + }, + { + "id": "creature-126024", + "name": "Skarvold & Dalronn" + }, + { + "id": "creature-126026", + "name": "Ingvar the Plunderer" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-126025", + "creature-126024", + "creature-126026" + ], + "finalBossId": "creature-126026" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-126025", + "creature-126024", + "creature-126026" + ], + "finalBossId": "creature-126026" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "utgarde-pinnacle", + "title": "Utgarde Pinnacle", + "finalBossId": "creature-126255", + "bosses": [ + { + "id": "encounter-577", + "name": "Svala Sorrowgrave" + }, + { + "id": "creature-126102", + "name": "Gortok Palehoof" + }, + { + "id": "creature-126103", + "name": "Skadi the Ruthless" + }, + { + "id": "creature-126255", + "name": "King Ymiron" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "encounter-577", + "creature-126102", + "creature-126103", + "creature-126255" + ], + "finalBossId": "creature-126255" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "encounter-577", + "creature-126102", + "creature-126103", + "creature-126255" + ], + "finalBossId": "creature-126255" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "violet-hold", + "title": "Violet Hold", + "finalBossId": "encounter-545", + "bosses": [ + { + "id": "creature-102383", + "name": "Erekem" + }, + { + "id": "creature-102384", + "name": "Moragg" + }, + { + "id": "encounter-545", + "name": "Cyanigosa" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-102383", + "creature-102384", + "encounter-545" + ], + "finalBossId": "encounter-545" + }, + { + "id": "heroic", + "name": "heroic", + "bossIds": [ + "creature-102383", + "creature-102384", + "encounter-545" + ], + "finalBossId": "encounter-545" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "wailing-caverns", + "title": "Wailing Caverns", + "finalBossId": "mutanus-spawn", + "bosses": [ + { + "id": "lady-anacondra-spawn", + "name": "Lady Anacondra" + }, + { + "id": "kresh-patrol:kresh-roaming", + "name": "Kresh" + }, + { + "id": "lord-cobrahn-spawn", + "name": "Lord Cobrahn" + }, + { + "id": "lord-pythas-spawn", + "name": "Lord Pythas" + }, + { + "id": "skum-spawn", + "name": "Skum" + }, + { + "id": "lord-serpentis-spawn", + "name": "Lord Serpentis" + }, + { + "id": "verdan-spawn", + "name": "Verdan the Everliving" + }, + { + "id": "mutanus-spawn", + "name": "Mutanus the Devourer" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "lady-anacondra-spawn", + "kresh-patrol:kresh-roaming", + "lord-cobrahn-spawn", + "lord-pythas-spawn", + "skum-spawn", + "lord-serpentis-spawn", + "verdan-spawn", + "mutanus-spawn" + ], + "finalBossId": "mutanus-spawn" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "warnorken-arena", + "title": "Warnorken Arena", + "finalBossId": "", + "bosses": [], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [], + "finalBossId": "" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "ystra-labyrinth-boss1", + "title": "Ystra Labyrinth Boss Rooms", + "finalBossId": "rw-ystra-labyrinth-boss1-spawn-5951049", + "bosses": [ + { + "id": "rw-ystra-labyrinth-boss1-spawn-5951030", + "name": "Specter Guardian Creator" + }, + { + "id": "rw-ystra-labyrinth-boss1-spawn-5951054", + "name": "Raichika the Destroyer" + }, + { + "id": "rw-ystra-labyrinth-boss1-spawn-5951049", + "name": "Rune Guardian Leader" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-ystra-labyrinth-boss1-spawn-5951030", + "rw-ystra-labyrinth-boss1-spawn-5951054", + "rw-ystra-labyrinth-boss1-spawn-5951049" + ], + "finalBossId": "rw-ystra-labyrinth-boss1-spawn-5951049" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "ystra-labyrinth-boss2", + "title": "Ystra Labyrinth Boss Room 2", + "finalBossId": "rw-ystra-labyrinth-boss2-spawn-5952381", + "bosses": [ + { + "id": "rw-ystra-labyrinth-boss2-spawn-5952381", + "name": "Rune Guardian Leader" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-ystra-labyrinth-boss2-spawn-5952381" + ], + "finalBossId": "rw-ystra-labyrinth-boss2-spawn-5952381" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "ystra-labyrinth-boss3", + "title": "Ystra Labyrinth Boss Room 3", + "finalBossId": "rw-ystra-labyrinth-boss3-spawn-5952388", + "bosses": [ + { + "id": "rw-ystra-labyrinth-boss3-spawn-5952388", + "name": "Raichika the Destroyer" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "rw-ystra-labyrinth-boss3-spawn-5952388" + ], + "finalBossId": "rw-ystra-labyrinth-boss3-spawn-5952388" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "zulfarrak", + "title": "Zul'Farrak", + "finalBossId": "creature-37996", + "bosses": [ + { + "id": "creature-81570", + "name": "Hydromancer Velratha" + }, + { + "id": "encounter-594", + "name": "Ghaz'rilla" + }, + { + "id": "creature-81519", + "name": "Antu'sul" + }, + { + "id": "creature-81524", + "name": "Witch Doctor Zum'rah" + }, + { + "id": "creature-37996", + "name": "Chief Ukorz Sandscalp & Ruuzlu" + } + ], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [ + "creature-81570", + "encounter-594", + "creature-81519", + "creature-81524", + "creature-37996" + ], + "finalBossId": "creature-37996" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "zurhidon-stronghold", + "title": "Zurhidon Stronghold", + "finalBossId": "", + "bosses": [], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [], + "finalBossId": "" + } + ], + "defaultDifficultyId": "normal" + }, + { + "id": "zurhidon-stronghold-124", + "title": "Zurhidon Stronghold (124)", + "finalBossId": "", + "bosses": [], + "difficulties": [ + { + "id": "normal", + "name": "normal", + "bossIds": [], + "finalBossId": "" + } + ], + "defaultDifficultyId": "normal" + } + ], + "classes": [ + { + "id": "warrior", + "name": "Warrior" + }, + { + "id": "paladin", + "name": "Paladin" + }, + { + "id": "hunter", + "name": "Hunter" + }, + { + "id": "rogue", + "name": "Rogue" + }, + { + "id": "priest", + "name": "Priest" + }, + { + "id": "death-knight", + "name": "Death Knight" + }, + { + "id": "shaman", + "name": "Shaman" + }, + { + "id": "mage", + "name": "Mage" + }, + { + "id": "warlock", + "name": "Warlock" + }, + { + "id": "druid", + "name": "Druid" + }, + { + "id": "barbarian", + "name": "Barbarian" + }, + { + "id": "witch-doctor", + "name": "Witch Doctor" + }, + { + "id": "felsworn", + "name": "Felsworn" + }, + { + "id": "witch-hunter", + "name": "Witch Hunter" + }, + { + "id": "stormbringer", + "name": "Stormbringer" + }, + { + "id": "knight-of-xoroth", + "name": "Knight of Xoroth" + }, + { + "id": "guardian", + "name": "Guardian" + }, + { + "id": "templar", + "name": "Templar" + }, + { + "id": "bloodmage", + "name": "Bloodmage" + }, + { + "id": "ranger", + "name": "Ranger" + }, + { + "id": "chronomancer", + "name": "Chronomancer" + }, + { + "id": "necromancer", + "name": "Necromancer" + }, + { + "id": "pyromancer", + "name": "Pyromancer" + }, + { + "id": "cultist", + "name": "Cultist" + }, + { + "id": "starcaller", + "name": "Starcaller" + }, + { + "id": "sun-cleric", + "name": "Sun Cleric" + }, + { + "id": "tinker", + "name": "Tinker" + }, + { + "id": "venomancer", + "name": "Venomancer" + }, + { + "id": "reaper", + "name": "Reaper" + }, + { + "id": "primalist", + "name": "Primalist" + }, + { + "id": "runemaster", + "name": "Runemaster" + }, + { + "id": "rom-warrior", + "name": "Warrior" + }, + { + "id": "rom-scout", + "name": "Scout" + }, + { + "id": "rom-rogue", + "name": "Rogue" + }, + { + "id": "rom-mage", + "name": "Mage" + }, + { + "id": "rom-priest", + "name": "Priest" + }, + { + "id": "rom-knight", + "name": "Knight" + }, + { + "id": "rom-warden", + "name": "Warden" + }, + { + "id": "rom-druid", + "name": "Druid" + }, + { + "id": "rom-warlock", + "name": "Warlock" + }, + { + "id": "rom-champion", + "name": "Champion" + } + ] +} diff --git a/src/game/generated/wowDungeonStairs.generated.json b/src/game/generated/wowDungeonStairs.generated.json new file mode 100644 index 00000000..d7e45b10 --- /dev/null +++ b/src/game/generated/wowDungeonStairs.generated.json @@ -0,0 +1,11746 @@ +{ + "schemaVersion": 1, + "dungeons": { + "shadowfang-keep": { + "collision": [ + { + "url": "/assets/game/manastorm/33-shadowfang/collision-000.glb", + "checksum": "09fc6c00e2a19ff48c1db4db89ebe901530bd98172f397b64e19d73a050fd760" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-001.glb", + "checksum": "6618715aa3ad8a62b97850911a76169556073803d238253f110bc2c21f02dc95" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-002.glb", + "checksum": "751524f58b176ae1bb5bd26da757d3c70d2753bd7878c9550a4ba5a5fce305ab" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-003.glb", + "checksum": "b31a64f2425c0dc55f711242223709f9dd5aea950f065a457ea8cf107b9fecae" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-004.glb", + "checksum": "1b14fa94b28c74f800e57ea024848f5a47843070afbcdb99bbceefb6c98cb235" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-005.glb", + "checksum": "71994ba875afb7c2282f2c61ab548309105e28aa59d8bdbe2bf1dcb56faac1e9" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-006.glb", + "checksum": "2ff1f2c72d307d661065bea7ff0c2531549111e20e4961b41b332f80724b6914" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-007.glb", + "checksum": "db5a7002aca3c9aa18f170e60e39f623c4815ec69e6624e5f427ecb9dd3399b8" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-008.glb", + "checksum": "6dabd63340d6268b66a27aaae720055bfe1b2dee6a9512e42c086f2f3f864988" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-009.glb", + "checksum": "85080e2f17cc685ea8ae8cfd38ae3bcecb7553e149756ef0e504898a97437afb" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-010.glb", + "checksum": "902b38de243bbf3eafede498dbf27fb0631d4190df37f21c000d41f2dc921b7b" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-011.glb", + "checksum": "628c38b584c6cd8a2e282919958e8741a0f6d5bfae8e644703c619c5d0eee435" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-012.glb", + "checksum": "5c6979efea9b66c77081eb7e76e2fb4162e4e1640e20f030232e2c4ba09467e9" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-013.glb", + "checksum": "91ce7cc97d75265b20d1963566511d908985291a3ccafd8e47560fc0c63e0f38" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-014.glb", + "checksum": "6f58bbb4875a36dfb23666cdaa83f8018681a698329d5ca2e69436af62f0897a" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-015.glb", + "checksum": "0b382b37616628aeb0c4a6c03a5418aec2e3742bedd2b82e0cb9f3ad7baf0f79" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-016.glb", + "checksum": "8eec0d9e0f059967252b834c965f302ac5eb1b0fb68be9c2a295fb7c22459139" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-017.glb", + "checksum": "58c688e5381ba5c2e8252b085ad67ce4824d9322cfa54460c651ee94442b1472" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-018.glb", + "checksum": "78d886a9d8915270cb414e403ab53c8a4120c9bf7f29c2546a9652e2d635c1f3" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-019.glb", + "checksum": "3dd811c4d6439e282b783bcc5bd79109243103a7c8dd8a9ff4bd5adfd2c07f97" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-020.glb", + "checksum": "4b0ada59502341ce7367a753fac5d55a878a92ed03596f86b4d3451d7fbf2804" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-021.glb", + "checksum": "da9f2fdc179be4f73aa834fad6214344e9ab51da8e5269d3efbf0ba088e8d6b4" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-022.glb", + "checksum": "0d323f8759c0b96302db882c10e61e73e3fc5c3192b28931948506dc28ae9ed8" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-023.glb", + "checksum": "6ed394a3fb802fabb12d6d714783208ce85d6df110deb22ec877c860a31235f6" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-024.glb", + "checksum": "95a085dd46f7bf0b55089bdbf92a174ef97d52f952cc02db675e96a940193c10" + }, + { + "url": "/assets/game/manastorm/33-shadowfang/collision-025.glb", + "checksum": "8114e73992f3b564d2777cad5fed936422f7bb82ea285598790b26ef7669e784" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/33-shadowfang/navigation.glb", + "checksum": "d6def49815be021cf283b5f7777584991d1b79a3496e8cf27c51a3d1ac2db8ab", + "size": 6384420, + "triangleCount": 545171 + }, + "ramps": [ + { + "id": "stair-flight-14", + "start": [ + 2158.0627842314752, + 90.63386535644531, + -241.36844663525653 + ], + "end": [ + 2163.71861118627, + 89.68743896484375, + -239.08334421673385 + ], + "width": 3.475190615702122 + }, + { + "id": "stair-flight-15", + "start": [ + 2066.637163207321, + 17.324121475219727, + -411.8459290596316 + ], + "end": [ + 2060.782954389632, + 19.768892288208008, + -411.7437434646155 + ], + "width": 7.189216887021491 + }, + { + "id": "stair-flight-20", + "start": [ + 2144.2640599814413, + 87.57489013671875, + -238.08451603090217 + ], + "end": [ + 2149.9743454421427, + 90.63386535644531, + -235.8925428127532 + ], + "width": 4.810717387959426 + }, + { + "id": "stair-flight-25", + "start": [ + 2110.193082323837, + 100.03048706054688, + -245.7839902507592 + ], + "end": [ + 2108.306220667101, + 97.52922821044922, + -241.1138437699566 + ], + "width": 3.143026245463734 + }, + { + "id": "stair-flight-36", + "start": [ + 2183.4327308741467, + 94.25054931640625, + -241.85415142383965 + ], + "end": [ + 2189.6684127429585, + 97.34332275390625, + -239.46049742048066 + ], + "width": 5.67733569970277 + }, + { + "id": "stair-flight-43", + "start": [ + 2030.2898933488696, + 17.24559211730957, + -359.199627093401 + ], + "end": [ + 2031.5748841107043, + 18.71883201599121, + -356.3134905881931 + ], + "width": 2.2306448516403816 + }, + { + "id": "stair-flight-44", + "start": [ + 2044.8674923297306, + 18.434661865234375, + -336.21681790005573 + ], + "end": [ + 2046.1524825401402, + 17.00160789489746, + -333.3306826333688 + ], + "width": 2.2311996698245093 + }, + { + "id": "stair-flight-54", + "start": [ + 2156.3414468410438, + 81.15274047851562, + -223.17776585574904 + ], + "end": [ + 2158.9618695993145, + 79.76690673828125, + -222.11904633863253 + ], + "width": 8.206668203134218 + }, + { + "id": "stair-flight-55", + "start": [ + 2153.2769787565803, + 81.15274047851562, + -216.36970784419793 + ], + "end": [ + 2155.7588283510468, + 79.76690673828125, + -215.05008500685483 + ], + "width": 6.881390973282823 + }, + { + "id": "stair-flight-56", + "start": [ + 2159.0430306362227, + 81.15274047851562, + -230.64846313589476 + ], + "end": [ + 2161.7118508000913, + 79.76690673828125, + -229.78131089895032 + ], + "width": 7.80765347335684 + }, + { + "id": "stair-flight-62", + "start": [ + 2112.3677540244767, + 76.89383697509766, + -220.2893302916892 + ], + "end": [ + 2116.8587196700364, + 79.00638580322266, + -218.74296680958795 + ], + "width": 4.423214566005839 + }, + { + "id": "stair-flight-63", + "start": [ + 2043.857354032782, + 19.768892288208008, + -406.0084478963085 + ], + "end": [ + 2043.857354032782, + 18.8697509765625, + -403.6038402913672 + ], + "width": 3.0588995873149725 + }, + { + "id": "stair-flight-64", + "start": [ + 2116.765538453007, + 79.00638580322266, + -218.53022511385996 + ], + "end": [ + 2119.061572530455, + 80.07111358642578, + -217.91500463701345 + ], + "width": 4.648880833779476 + }, + { + "id": "stair-flight-77", + "start": [ + 2296.4936016534302, + 94.67305755615234, + -231.93210460075852 + ], + "end": [ + 2294.67079054789, + 95.5518798828125, + -231.15836719174945 + ], + "width": 6.175943780177267 + }, + { + "id": "stair-flight-78", + "start": [ + 2297.483953128494, + 94.67305755615234, + -218.57574449417518 + ], + "end": [ + 2298.2517238581813, + 95.5518798828125, + -216.76699000423037 + ], + "width": 6.184198152128847 + }, + { + "id": "stair-flight-79", + "start": [ + 2313.177861290075, + 95.5518798828125, + -236.4745300006282 + ], + "end": [ + 2313.9698571909616, + 94.67305755615234, + -234.69567808245495 + ], + "width": 2.79376389703295 + }, + { + "id": "stair-flight-81", + "start": [ + 2028.007292642485, + 17.00160789489746, + -358.9022250196612 + ], + "end": [ + 2028.7657994454125, + 17.869190216064453, + -357.1152949734908 + ], + "width": 2.245575886968727 + }, + { + "id": "stair-flight-82", + "start": [ + 2294.7064684337324, + 94.67305755615234, + -236.05124361368462 + ], + "end": [ + 2292.9337738568265, + 95.5518798828125, + -235.2619891377551 + ], + "width": 2.7895888862881475 + }, + { + "id": "stair-flight-83", + "start": [ + 2292.8841403689225, + 95.87298583984375, + -228.3465893298013 + ], + "end": [ + 2293.6632705198426, + 94.90966033935547, + -226.59663435917707 + ], + "width": 5.601172137136036 + }, + { + "id": "stair-flight-84", + "start": [ + 2309.0990339991554, + 95.87298583984375, + -235.38705220331758 + ], + "end": [ + 2309.8715803954524, + 94.90966033935547, + -233.6518845876875 + ], + "width": 5.578787846167433 + }, + { + "id": "stair-flight-86", + "start": [ + 2302.8028018387818, + 94.90966033935547, + -215.74986204272977 + ], + "end": [ + 2301.093065902372, + 95.87298583984375, + -214.98863855953732 + ], + "width": 5.597930022768214 + }, + { + "id": "stair-flight-92", + "start": [ + 2162.2656640625, + 16.808837890625, + -378.24627685546875 + ], + "end": [ + 2163.9609375, + 17.336936950683594, + -378.24627685546875 + ], + "width": 1.5059619140625 + }, + { + "id": "stair-flight-93", + "start": [ + 2083.684593289743, + 25.266481399536133, + -321.23148763020833 + ], + "end": [ + 2083.684593289743, + 25.753904342651367, + -319.5362455989499 + ], + "width": 3.003840764263732 + }, + { + "id": "stair-flight-94", + "start": [ + 2111.718800252444, + 20.010892868041992, + -423.3676125141701 + ], + "end": [ + 2111.68922076505, + 19.52362060546875, + -421.67300481632367 + ], + "width": 3.004360526058026 + }, + { + "id": "stair-flight-95", + "start": [ + 1985.5494114743528, + 27.38106918334961, + -412.27159171094866 + ], + "end": [ + 1986.1841985685332, + 26.893760681152344, + -410.700438519424 + ], + "width": 3.002628642749205 + }, + { + "id": "stair-flight-96", + "start": [ + 1764.6902264221553, + 48.3041877746582, + 868.7749186249997 + ], + "end": [ + 1764.7788812915758, + 47.816123962402344, + 870.4665543064456 + ], + "width": 3.005871293438322 + }, + { + "id": "stair-flight-100", + "start": [ + 2107.4537509041193, + 79.39509582519531, + -203.81527364102703 + ], + "end": [ + 2106.772703321383, + 78.56697845458984, + -202.35476238665183 + ], + "width": 4.041639041341586 + }, + { + "id": "stair-flight-101", + "start": [ + 2137.7105049443103, + 82.33576202392578, + -189.66714773879266 + ], + "end": [ + 2139.2547622973916, + 83.16388702392578, + -189.2243390683725 + ], + "width": 3.9989955660515943 + }, + { + "id": "stair-flight-104", + "start": [ + 2119.2000725408193, + 79.27679443359375, + -225.96597975402403 + ], + "end": [ + 2120.737995138474, + 80.07111358642578, + -225.6109223458123 + ], + "width": 10.856350464224779 + }, + { + "id": "stair-fallback-21", + "start": [ + 2165.12943359375, + 18.87479591369629, + -369.23492431640625 + ], + "end": [ + 2168.492578125, + 20.65212059020996, + -369.23492431640625 + ], + "width": 1.9721484375 + }, + { + "id": "stair-fallback-68", + "start": [ + 2116.242619928809, + 77.68815612792969, + -227.02812936224757 + ], + "end": [ + 2117.7930966322865, + 78.48247528076172, + -226.58353732179887 + ], + "width": 10.715301134839724 + }, + { + "id": "stair-fallback-70", + "start": [ + 2114.731346001974, + 76.89383697509766, + -227.63751181606278 + ], + "end": [ + 2116.875822248145, + 77.94165802001953, + -226.98187963194903 + ], + "width": 10.655268052196352 + }, + { + "id": "stair-fallback-76", + "start": [ + 2165.991298829049, + 88.85932159423828, + -238.33738944536492 + ], + "end": [ + 2170.2349538160533, + 86.35806274414062, + -236.70840291875527 + ], + "width": 5.085379637653791 + }, + { + "id": "stair-fallback-77", + "start": [ + 2166.005563322139, + 88.85932159423828, + -238.34285873762735 + ], + "end": [ + 2167.444716160399, + 88.03119659423828, + -237.7904197221003 + ], + "width": 5.10581556033971 + }, + { + "id": "stair-fallback-79", + "start": [ + 2172.5279855832146, + 84.97222900390625, + -235.77208731745486 + ], + "end": [ + 2174.435677348748, + 83.85680389404297, + -235.0397930587335 + ], + "width": 5.101349431733542 + }, + { + "id": "stair-fallback-80", + "start": [ + 2177.177877755564, + 82.2005615234375, + -233.92690589607162 + ], + "end": [ + 2181.4354061308327, + 79.75000762939453, + -232.2925938746631 + ], + "width": 5.085150381490752 + }, + { + "id": "stair-fallback-101", + "start": [ + 2169.738738198032, + 86.64537048339844, + -236.89148330159242 + ], + "end": [ + 2171.638744892667, + 85.52993774414062, + -236.12383076769754 + ], + "width": 5.117040075344693 + }, + { + "id": "stair-fallback-102", + "start": [ + 2175.8124643125275, + 83.02867889404297, + -234.50224578354027 + ], + "end": [ + 2177.2234102803354, + 82.18366241455078, + -233.9321866093095 + ], + "width": 5.121089448501625 + }, + { + "id": "stair-fallback-112", + "start": [ + 2189.829201826226, + 116.5733871459961, + 511.3209232900524 + ], + "end": [ + 2189.771687947594, + 115.60082244873047, + 512.9679052858341 + ], + "width": 2.5525034221532406 + }, + { + "id": "stair-fallback-119", + "start": [ + 2289.1300303320218, + 81.35554504394531, + -277.4125984025169 + ], + "end": [ + 2288.092074251108, + 79.91900634765625, + -274.7086303664962 + ], + "width": 3.659483480101262 + }, + { + "id": "stair-fallback-120", + "start": [ + 2305.201808379172, + 81.35554504394531, + -271.0873121950351 + ], + "end": [ + 2304.163618689887, + 79.91900634765625, + -268.3827355884008 + ], + "width": 3.685535896433539 + } + ], + "candidateCount": 272, + "rejectedCount": 231 + }, + "stormwind-stockade": { + "collision": [ + { + "id": "collision", + "fileName": "collision.glb", + "url": "/assets/game/manastorm/34-stormwindjail/collision.glb", + "checksum": "557b6392d1bbcddf5be7856b35f8118feb604254e1d11dad4c3d61c349d61f09", + "size": 681800, + "triangleCount": 56467 + } + ], + "ramps": [], + "reviewedOverride": true + }, + "deadmines": { + "collision": [ + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-000.glb", + "checksum": "3c79220accb5540236ab26c4a7005c5a5287373930748fcc5147e637316706a0" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-001.glb", + "checksum": "fbb80dcc89b1990aee002560e7a021c484a9674cdbec5368b33dbd73a8335d23" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-002.glb", + "checksum": "e96cf008b1224880b708918c938fef1c64497c70e5263d766965c8c11f89570a" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-003.glb", + "checksum": "0f3f726183834b1b122da46c5525bfea15c1240d073accfe480def7fa0bc2c1a" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-004.glb", + "checksum": "49d03beb974916ab59f3cee441270fc886dd631c19154c18bdd4147a51a1c190" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-005.glb", + "checksum": "9267654084dfb0eb5394621e211da6536df69c9060e2cd3aa1fbe17c3874f299" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-006.glb", + "checksum": "1c42bdbe8bdecd7dbfcb160235726f42cb51d2d38e769cd4f9a05588d7cc3c62" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-007.glb", + "checksum": "f5f6620b876ec0d1dd1927389f2172bfc90d7704f37c933926d7d058af8bb13b" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-008.glb", + "checksum": "602e2197ac6f1cc66b7ec87a3ffd5db248ceabfa8c1bbbdf419a0717431346ad" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-009.glb", + "checksum": "91cb699e7066630e0df6a6f7ee231354e79ac91bd0d9a9e99f39a3b9bba27bbb" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-010.glb", + "checksum": "c892ea0ea961e171230d6188d2c54fb86770417570ab43ade90e65bbcbb33d48" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-011.glb", + "checksum": "4ae7c7a61191ceef1615687198a06a08c323a72e98a9480111a3ddb590656f20" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-012.glb", + "checksum": "0bd38a18f7c703ab108062980fd2099bf230264744b8f2e7ce5d300d5436926b" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-013.glb", + "checksum": "9b13bf1257c74c29c42c2b4424234062d0db222f90441744f6bf9c373a47f8ea" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-014.glb", + "checksum": "3abb62143237c9a6bfb185cbbdef25f72ad0a6852a77243ec62dfdc8c697983b" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-015.glb", + "checksum": "aedc5eed8b2057c86b91e524ac455e5e14d9fd846df9d56174d2c94858c1d1a7" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-016.glb", + "checksum": "39f8310712fc133adb1e1b97723517ff696006812a1ac06f9ecbdbb6ee2bcd8a" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-017.glb", + "checksum": "b9fe42c2817744cc54c7f2a943153056acaf9dd012bf92f60a5d41886b717d2a" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-018.glb", + "checksum": "c14338408fb430b03f30c05fa6b08d6f148cf9c124ca3b06309b5e0448fd30a4" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-019.glb", + "checksum": "00fac06da6dcda8826439ecbce947bec35429c233c24a6ed8fbb2a94567573f6" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-020.glb", + "checksum": "b7927b3012de3e8c9567bac8b564120f28853081734605ef0673d035b70cb127" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-021.glb", + "checksum": "7d3839e04adb86dc05de8e0b33642e9c7b5a7f2b4114d25944fbb9303b0c4494" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-022.glb", + "checksum": "7b7814c76422796674f66ea1f15d6be1c9ba8b83065b527526179416c68d5879" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-023.glb", + "checksum": "b2e6c2c619aa5522e6906eb439dc42c4a613bff8438c97f188d99bdd9f43d8a8" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-024.glb", + "checksum": "8030f3d420cca8fb4bbf1f485f50adede366596d8263c393d29d3b974e4cfc98" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-025.glb", + "checksum": "9e4cd861bb5ca6c1d1a36cdda43910110714f677da240633da4dc48473cda51b" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-026.glb", + "checksum": "1f5a0349db4852755f400a13a4101579d19599291d75070e363974552445beab" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-027.glb", + "checksum": "a4b608b49da4366da0417aa2b146f080fb95639f80c8f631e6e56c10a83ea1bc" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-028.glb", + "checksum": "896786c6205b57938a2d0201bc57a60696c3fdef29270b356909983f16f93048" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-029.glb", + "checksum": "3abbbba30ea58dc11d9f200281c1258d5442e92446acd5f0d9b95cf99368c671" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-030.glb", + "checksum": "46fbba8dc90793611922f95921c2a596afd013bc65407442d1608f48a95176e2" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-031.glb", + "checksum": "6294c97fd171aae2e6fa709453731de4151551dbd4c581589b9bc2878c470458" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-032.glb", + "checksum": "54772a435efbbf0f4bb656ff7f6e7ee7b50e4c7ca356beaaf8ddb5d4c26e63fc" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-033.glb", + "checksum": "9a402302ded3a28360017c5dab7757200112d7db0077089ed70387297cf17e4f" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-034.glb", + "checksum": "5fb71d4982ed4bac9140ac7ec1a1cd80a5bf4a507b0ca695381e0f208a90c234" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-035.glb", + "checksum": "165d3038a1f0806ffdc197af231120cc7d163aada8f22aceb6ae2cf9cba4d57c" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-036.glb", + "checksum": "2cc7fba8034b5e5402d2e039379f1b28630bb07d157943a338c5553b840ddaa3" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-037.glb", + "checksum": "0b731f64b2a35ed6e830cf37edf0129098f5409db116abe3e76f6168474f7ddb" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-038.glb", + "checksum": "a0181fc996a4cee567dff1febcd64886f43912d3432265d5abce4ffea28eb697" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-039.glb", + "checksum": "6412ef65fd1fe7b22623cea8083512b544f14139885efb19cccde3265781faee" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-040.glb", + "checksum": "8b118c00d82df41fa66c82d6a2014cbcd54d87d0d569a7b86b60a60ff1fddb25" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-041.glb", + "checksum": "af37400441d66aa0e9108a6bc546133c18e57036a82daf972152f689ac33e7bf" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-042.glb", + "checksum": "e5f39582fcfd8f280f94e32e9860f2fdbe5e823be50871163029b3c60b78f72a" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-043.glb", + "checksum": "f71c1884cc2eb6706f950efe7ef9644f74ea667af6dc1d98bdbb8872844a7931" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-044.glb", + "checksum": "56bc4de15d2ceaf8d8726c2ede55e982d8e0e8d63c4e06e1f0881086dacee4af" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-045.glb", + "checksum": "b573525f30c9c9c644f210e6c7f699e58519028f108b53bb1c568f75d7795222" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-046.glb", + "checksum": "6428265a71f2f7333d0b6d0ef70dc2dae6004052c049e0e47752ce359552b9ca" + }, + { + "url": "/assets/game/manastorm/36-deadminesinstance/collision-047.glb", + "checksum": "37764a47184f88fb577656da6b2924464066202a0201af24215c1a88ecef5afa" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/36-deadminesinstance/navigation.glb", + "checksum": "3ce1c783d2ccee06d502103fb4e3eb58ff05e6f6a012c563b9974035d5fb2fb0", + "size": 1172504, + "triangleCount": 103978 + }, + "ramps": [], + "candidateCount": 6, + "rejectedCount": 6 + }, + "wailing-caverns": { + "collision": [ + { + "fileName": "wailing-caverns-collision.glb", + "checksum": "c8d296c60dc55736bd47615c5456ec96d0a5a6f308dc684b0c80dca6d3a1e405" + } + ], + "navigation": { + "fileName": "wailing-caverns-navigation.glb", + "checksum": "3ddab097edda3db94ef3777d11065cbccb6397a759988e7b59dbf596dc244502" + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "razorfen-kraul": { + "collision": [ + { + "url": "/assets/game/manastorm/47-razorfenkraulinstance/collision.glb", + "checksum": "38f23ca147ad5d6d40ad3d33f55dfec4a82d06bf5c57b0ea9dc49eeda2a81eed" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/47-razorfenkraulinstance/navigation.glb", + "checksum": "feed612f71fd635f96d0cec96486d5a4e8cc96f30510d8f7f47ad8d5b6b57d5b", + "size": 973440, + "triangleCount": 70120 + }, + "ramps": [], + "candidateCount": 2, + "rejectedCount": 2 + }, + "blackfathom-deeps": { + "collision": [ + { + "url": "/assets/game/manastorm/48-blackfathom/collision.glb", + "checksum": "3cff675247f3382146e0636a55f059d59a243cd63861950e6b647bd5b714bf6f" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/48-blackfathom/navigation.glb", + "checksum": "3f3750af3cd035a25ee63f27512088772b2a5faaa558422151ca972eba73e834", + "size": 386652, + "triangleCount": 30083 + }, + "ramps": [ + { + "id": "stair-fallback-2", + "start": [ + 700.731689453125, + -31.89472007751465, + -29.092829704284668 + ], + "end": [ + 705.97359375, + -34.43913269042969, + -29.092829704284668 + ], + "width": 3.8083472442626953 + }, + { + "id": "stair-fallback-3", + "start": [ + 705.93359375, + -34.49567794799805, + -29.092829704284668 + ], + "end": [ + 711.175498046875, + -36.983551025390625, + -29.092829704284668 + ], + "width": 3.8083472442626953 + }, + { + "id": "stair-fallback-4", + "start": [ + 729.8817529296875, + -37.66205978393555, + -58.3253116607666 + ], + "end": [ + 735.1236572265625, + -35.117645263671875, + -58.3253116607666 + ], + "width": 3.808349151611328 + }, + { + "id": "stair-fallback-5", + "start": [ + 736.3276147460938, + -34.43913269042969, + -58.3253116607666 + ], + "end": [ + 741.5695190429688, + -31.89472007751465, + -58.3253116607666 + ], + "width": 3.808349151611328 + }, + { + "id": "stair-fallback-14", + "start": [ + 720.9597778320312, + -37.66205978393555, + -12.085261383056642 + ], + "end": [ + 720.9597778320312, + -35.7961540222168, + -8.14382743835449 + ], + "width": 3.3560595703125 + } + ], + "candidateCount": 24, + "rejectedCount": 19 + }, + "uldaman": { + "collision": [ + { + "url": "/assets/game/manastorm/70-uldaman/collision.glb", + "checksum": "b9a1a79d35897410d89fddd18610e65eed36ad869e80905bd1c353a5bb5008f6" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/70-uldaman/navigation.glb", + "checksum": "7a44b07858eb389f730aa2d6f3d606b7db2365a4b2456885605f33dc96fd73e1", + "size": 205156, + "triangleCount": 15618 + }, + "ramps": [ + { + "id": "stair-flight-1", + "start": [ + 246.99622599283853, + -46.02235412597656, + 60.25302505493164 + ], + "end": [ + 255.94579223632812, + -48.803768157958984, + 60.25302505493164 + ], + "width": 6.608890075683593, + "navigationBridges": [ + { + "linkId": "stair-flight-1-seam-1", + "startPortal": [ + [ + 250.93666076660156, + -46.637451171875, + 57.458194732666016 + ], + [ + 250.93666076660156, + -46.637451171875, + 63.07377243041992 + ] + ], + "endPortal": [ + [ + 251.9162139892578, + -47.31300735473633, + 57.458194732666016 + ], + [ + 251.9162139892578, + -47.31300735473633, + 63.07377243041992 + ] + ] + }, + { + "linkId": "stair-flight-1-seam-2", + "startPortal": [ + [ + 253.13221740722656, + -47.31300735473633, + 57.458194732666016 + ], + [ + 253.13221740722656, + -47.31300735473633, + 63.07377243041992 + ] + ], + "endPortal": [ + [ + 254.11178588867188, + -48.022342681884766, + 57.458194732666016 + ], + [ + 254.11178588867188, + -48.022342681884766, + 63.07377243041992 + ] + ] + } + ] + }, + { + "id": "stair-flight-4", + "start": [ + 199.99157043457032, + -48.803768157958984, + 60.25302505493164 + ], + "end": [ + 208.89592997233072, + -46.02235412597656, + 60.25302505493164 + ], + "width": 6.608890075683593, + "navigationBridges": [ + { + "linkId": "stair-flight-4-seam-1", + "startPortal": [ + [ + 201.71383666992188, + -48.022342681884766, + 57.458194732666016 + ], + [ + 201.71383666992188, + -48.022342681884766, + 63.07377243041992 + ] + ], + "endPortal": [ + [ + 202.7271728515625, + -47.31300735473633, + 57.458194732666016 + ], + [ + 202.7271728515625, + -47.31300735473633, + 63.07377243041992 + ] + ] + }, + { + "linkId": "stair-flight-4-seam-2", + "startPortal": [ + [ + 203.90940856933594, + -47.31300735473633, + 57.458194732666016 + ], + [ + 203.90940856933594, + -47.31300735473633, + 63.07377243041992 + ] + ], + "endPortal": [ + [ + 204.9227294921875, + -46.637451171875, + 57.458194732666016 + ], + [ + 204.9227294921875, + -46.637451171875, + 63.07377243041992 + ] + ] + } + ] + }, + { + "id": "stair-flight-5", + "start": [ + 199.99157043457032, + -47.413063049316406, + 171.57320404052734 + ], + "end": [ + 208.89592997233072, + -44.631649017333984, + 171.57320404052734 + ], + "width": 1.6820635986328125 + }, + { + "id": "stair-flight-12", + "start": [ + 88.73259353637695, + -47.616580963134766, + 291.5556335449219 + ], + "end": [ + 88.73259353637695, + -49.719600677490234, + 298.26085571289065 + ], + "width": 6.600421447753906, + "navigationBridges": [ + { + "linkId": "stair-flight-12-seam-1", + "startPortal": [ + [ + 90.8801498413086, + -47.51567459106445, + 293.253173828125 + ], + [ + 85.7121353149414, + -47.617008209228516, + 293.253173828125 + ] + ], + "endPortal": [ + [ + 91.52192687988281, + -48.22500991821289, + 294.2665100097656 + ], + [ + 85.91480255126953, + -48.22500991821289, + 294.2665100097656 + ] + ] + }, + { + "linkId": "stair-flight-12-seam-2", + "startPortal": [ + [ + 91.52192687988281, + -48.22500991821289, + 295.44873046875 + ], + [ + 85.91480255126953, + -48.22500991821289, + 295.44873046875 + ] + ], + "endPortal": [ + [ + 91.52192687988281, + -48.93434524536133, + 296.46209716796875 + ], + [ + 85.91480255126953, + -48.93434524536133, + 296.46209716796875 + ] + ] + } + ] + }, + { + "id": "stair-flight-13", + "start": [ + 88.73259353637695, + -49.719600677490234, + 246.75013061523438 + ], + "end": [ + 88.73259353637695, + -47.616580963134766, + 253.45532989501953 + ], + "width": 6.600421447753906, + "navigationBridges": [ + { + "linkId": "stair-flight-13-seam-1", + "startPortal": [ + [ + 91.52192687988281, + -48.93434524536133, + 248.45526123046875 + ], + [ + 85.91480255126953, + -48.93434524536133, + 248.45526123046875 + ] + ], + "endPortal": [ + [ + 91.52192687988281, + -48.22500991821289, + 249.46859741210938 + ], + [ + 85.91480255126953, + -48.22500991821289, + 249.46859741210938 + ] + ] + }, + { + "linkId": "stair-flight-13-seam-2", + "startPortal": [ + [ + 91.52192687988281, + -48.22500991821289, + 250.65081787109375 + ], + [ + 85.91480255126953, + -48.22500991821289, + 250.65081787109375 + ] + ], + "endPortal": [ + [ + 90.8801498413086, + -47.51567459106445, + 251.66415405273438 + ], + [ + 85.7121353149414, + -47.617008209228516, + 251.66415405273438 + ] + ] + } + ] + } + ], + "candidateCount": 23, + "rejectedCount": 18 + }, + "gnomeregan": { + "collision": [ + { + "url": "/assets/game/manastorm/90-gnomeragoninstance/collision.glb", + "checksum": "c0a73fd9508fea6d80e446e9c77c6a0fb6d543edcdb595e52fad2c42a6f5bb4f" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/90-gnomeragoninstance/navigation.glb", + "checksum": "2630886a2ff3960221014305645e9e55197e5459ce5b7fa3e35097bb701e4eae", + "size": 258384, + "triangleCount": 20294 + }, + "ramps": [ + { + "id": "stair-flight-7", + "start": [ + 238.7205581665039, + -152.8293914794922, + 66.10004806518555 + ], + "end": [ + 244.379599609375, + -156.447509765625, + 66.10004806518555 + ], + "width": 11.64183380126953 + }, + { + "id": "stair-flight-14", + "start": [ + 301.13311767578125, + -152.8293914794922, + 0.33953768866402784 + ], + "end": [ + 301.13311767578125, + -156.2282257080078, + 5.563934364318848 + ], + "width": 11.641826171875 + }, + { + "id": "stair-flight-25", + "start": [ + 490.5636444091797, + -209.40362548828125, + 269.9482116699219 + ], + "end": [ + 490.5636444091797, + -211.54161071777344, + 273.3870458984375 + ], + "width": 11.641856689453125 + }, + { + "id": "stair-flight-26", + "start": [ + 490.5636444091797, + -207.92349243164062, + 267.6884121365017 + ], + "end": [ + 490.5636444091797, + -209.12953186035156, + 269.60448364257815 + ], + "width": 11.641856689453125 + } + ], + "candidateCount": 57, + "rejectedCount": 53 + }, + "sunken-temple": { + "collision": [ + { + "url": "/assets/game/manastorm/109-sunkentemple/collision.glb", + "checksum": "05969af5c5483a4fa92f0ffcd613f6db2d14e7ac46039343a6a1848b08fe0614" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/109-sunkentemple/navigation.glb", + "checksum": "171ffab86deb411c74f282d6e0821c880dc0ac03833f33802b8d2637fd06509f", + "size": 149416, + "triangleCount": 11583 + }, + "ramps": [ + { + "id": "stair-flight-1", + "start": [ + 421.7140696207682, + -174.16940307617188, + 94.81912231445312 + ], + "end": [ + 447.6145544433594, + -189.72247314453125, + 94.81912231445312 + ], + "width": 17.609096069335937 + }, + { + "id": "stair-flight-41", + "start": [ + 473.69199462890623, + -66.78968048095703, + 22.716552734375 + ], + "end": [ + 479.1331329345703, + -65.1495361328125, + 22.716552734375 + ], + "width": 3.4982257080078125 + } + ], + "candidateCount": 206, + "rejectedCount": 204 + }, + "razorfen-downs": { + "collision": [ + { + "url": "/assets/game/manastorm/129-razorfendowns/collision.glb", + "checksum": "71108797744e64985618caebb9c80f7a5cc9f39804e1f1fcdc8426286a542f09" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/129-razorfendowns/navigation.glb", + "checksum": "5468601f6a49eb1c695148a2793e6914f3a1fb378048974829574ff4da19e3e3", + "size": 902384, + "triangleCount": 65826 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "scarlet-monastery": { + "collision": [ + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-000.glb", + "checksum": "d0c3299ea5ded62a251b68e83451d02aecbbc85e87b6180344d21d6ef1d15fe2" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-001.glb", + "checksum": "343b0a65ed770b9617f265398bce94b52e51f47c0be65e12961b52e42bedaaf0" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-002.glb", + "checksum": "70e2e9ba7f5933c35bf5fc2252b4040ad531ddcc0d88ac018dce893cff562923" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-003.glb", + "checksum": "297e8c08db94a1d4e3bbffb973e90b3bd9a4aa3b21ae50a74bae463b6a2f9239" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-004.glb", + "checksum": "07d51b7eba9b44d44531b82c12974da9a21f75c5a3f5a9c969831ab470f54974" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-005.glb", + "checksum": "9fd3f2e4638570b4000a59a2038eed768e523f75ee86bcf2e2d436bb07ac98dd" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-006.glb", + "checksum": "a0db3bd31e9206e8f38c3ac8b26bec6d8414ad2abdc0fbddbe0fbf5309cd41c5" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-007.glb", + "checksum": "af616578ecb2b82ca4ba4a65656f10ad2d0035fcbb1b9c9912e87dd91c8971f3" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-008.glb", + "checksum": "015b037edb5b874fe37dace7ccc300170c2c91f7d255dee9e28598691c0f9b2a" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-009.glb", + "checksum": "6f3d6f165bece112f8ec37bc9941f0f70dceb0c9a710d707f8e438bf75a1f8e0" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-010.glb", + "checksum": "956d7f3690cc8f637a048a22cf519c13f2bb20a9342e9c21b89b75abdb7c252b" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-011.glb", + "checksum": "cbe22e3fd08de619c69e4b07a25fc17255e18f0c11b1504db60fff463c0c7f6f" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-012.glb", + "checksum": "2db39239150831e5332107a16d1d48c7d1de542595af91efdca6aa61ec83b691" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-013.glb", + "checksum": "9d0977cec5676e7c056fea4ae6f744c6e4af84fa2ef60a9d97570f4c81e43cd1" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-014.glb", + "checksum": "d82b7e84a1cd82060b1d689e07731170724fed1236f7c8f78275cad4f100750b" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-015.glb", + "checksum": "cd60ba21373412aa70a3add5b2289f349b5e915049f0c19b29f04c5e1f563912" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-016.glb", + "checksum": "37c2be520b7f3cb894141e0ad2ddb2669144316d8b727c8145be37ffefdbe12b" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-017.glb", + "checksum": "3f4a78baf88fa234f4c1c19836ee2df6e3b9830d234201a01f6ab51837670799" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-018.glb", + "checksum": "3f54e39288b3c987f8b2ef395257894bb61ce0c17d52f34a9a3db797d6e17196" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-019.glb", + "checksum": "58958d8c718846285d68dc69373e94d2e07cdce19408d386f656f794cc00ea6e" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-020.glb", + "checksum": "fd6ff2618c2181c0c15847394831bcd0812fae6a50f20a993f49f1f5dac57d2d" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-021.glb", + "checksum": "f7ff4cd70711b3a9168339f48793f6f5241cebfa4398bcc88120c757003f231a" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-022.glb", + "checksum": "4b4cd8a206f546dd5d75188f0bec5ab99ef3e71cc4839b24e50eb3c5ab21cdbe" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-023.glb", + "checksum": "702ccb99de34b593ebd7f4beafc29b6ea694cfd3da883d0e3f0230a2ed23d607" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-024.glb", + "checksum": "d1c5ebd7592e4aae7fb18716b64d518b41a2bc97f0c2f1135f60373e4233e345" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-025.glb", + "checksum": "1a66ca04f6b73699de6456454f981444f4c271f18d641c1244cbf05c49e3c17b" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-026.glb", + "checksum": "693b64a5d3bb2bc2c312d75894a8f474ba11e514b8fce05e4a133d92a77213c5" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-027.glb", + "checksum": "ab9414b99167b44708db8842fc587fc9bf15559e3e69303d6e5af45c84fb80ea" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-028.glb", + "checksum": "ade13d4e9921a2265c996a60d8117242cf13028a1c836b9c92f2a4f366df22a2" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-029.glb", + "checksum": "5a33702d71847f7301a17a4ee4d9838ad7ab98580f9f6e46d8fee36399f7fc5f" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-030.glb", + "checksum": "5befa71c228d06efe93b91fee2765de405a16213cfc421478a9763236d22cac7" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-031.glb", + "checksum": "17a331ffad0dbc1b6f871e1995628cea2b4a61641e0ead361884733cb776dcdc" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-032.glb", + "checksum": "cf3e36b973bfeb8b27c386480b42b6f84c1c3f2c38d92962bd6d6068d9f683c2" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-033.glb", + "checksum": "3e4b45f13ea5013d0486681e220409c1d45cb657b9af8626c04f0fad3b4d56f9" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-034.glb", + "checksum": "2233e71eaee5e04e637abc79300efb4d8ec58b24bb00f0d1da122be3d27727e0" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-035.glb", + "checksum": "f7f4a3397079fd312e83dca9004859bf5c9ec7ad0468a5b72e2598f9ad03eb57" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-036.glb", + "checksum": "79ca9fac6df5610e58bd13389ab8df66fd6e2b6ab1f59b319817575cc8b54266" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-037.glb", + "checksum": "1f36edbe85ac9a8f28daccdaf413a5140c541f84965bc3ab9264d430411edf46" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-038.glb", + "checksum": "dc8fccaced3cb5ec4acff45cd3a185a6c86d9d050596e715311d01592880129e" + }, + { + "url": "/assets/game/manastorm/189-monasteryinstances/collision-039.glb", + "checksum": "d579d98aee7236b15dedc024163b7a680a855230545e6796fbba026adb70a3e8" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/189-monasteryinstances/navigation.glb", + "checksum": "3ad09ecfdf9c51117f3257586e16b4fd97e71cefc40e02d147bd2e3976d16a0f", + "size": 267376, + "triangleCount": 25804 + }, + "ramps": [ + { + "id": "stair-flight-3", + "start": [ + 1379.8168334960938, + 18.750417709350586, + 968.4449650065103 + ], + "end": [ + 1379.8168334960938, + 23.846668243408203, + 979.1136774941095 + ], + "width": 7.3687548828125 + }, + { + "id": "stair-flight-4", + "start": [ + 1418.3906860351562, + 18.750417709350586, + 968.4543440755208 + ], + "end": [ + 1418.3906860351562, + 23.846668243408203, + 979.1033838665674 + ], + "width": 7.3687548828125 + }, + { + "id": "stair-flight-5", + "start": [ + 1399.103759765625, + 30.294403076171875, + 1137.69583984375 + ], + "end": [ + 1399.103759765625, + 31.98377227783203, + 1147.534033203125 + ], + "width": 18.968974609375 + }, + { + "id": "stair-flight-7", + "start": [ + 1417.5302734375, + -1.8824938535690308, + 1790.3044956752233 + ], + "end": [ + 1417.5302734375, + -7.2939348220825195, + 1798.931357421875 + ], + "width": 1.527080078125 + }, + { + "id": "stair-flight-8", + "start": [ + 1400.3425903320312, + 31.98377227783203, + 1178.6334577287946 + ], + "end": [ + 1400.3425903320312, + 29.815752029418945, + 1186.0423193359375 + ], + "width": 2.9200244140624996 + }, + { + "id": "stair-flight-9", + "start": [ + 1387.1145663758966, + 27.30986213684082, + 1055.2539221248476 + ], + "end": [ + 1384.3948991370642, + 30.773056030273438, + 1061.9853347533485 + ], + "width": 8.490376344346732 + }, + { + "id": "stair-flight-10", + "start": [ + 1395.0692611217494, + 27.30986213684082, + 1057.4559579867 + ], + "end": [ + 1394.0705994776952, + 30.773056030273438, + 1064.5618048117085 + ], + "width": 8.21063565504689 + }, + { + "id": "stair-flight-11", + "start": [ + 1403.167074633512, + 27.30986213684082, + 1057.4607894435599 + ], + "end": [ + 1404.1650568665195, + 30.773056030273438, + 1064.56180200778 + ], + "width": 8.210756537379684 + }, + { + "id": "stair-flight-13", + "start": [ + 1411.3171423332012, + 27.535110473632812, + 1055.6901202867653 + ], + "end": [ + 1413.9662572749025, + 30.773056030273438, + 1061.9310439895112 + ], + "width": 8.55312585746794 + }, + { + "id": "stair-flight-14", + "start": [ + 1369.887919108073, + 27.30986213684082, + 986.6503295898438 + ], + "end": [ + 1376.2012548828125, + 24.29716682434082, + 986.6503295898438 + ], + "width": 9.0580859375 + }, + { + "id": "stair-flight-15", + "start": [ + 1422.0250634765625, + 24.29716682434082, + 986.6503295898438 + ], + "end": [ + 1428.328172471788, + 27.30986213684082, + 986.6503295898438 + ], + "width": 9.0580859375 + }, + { + "id": "stair-flight-16", + "start": [ + 1381.3795166015625, + 30.294403076171875, + 1140.9806298828125 + ], + "end": [ + 1381.3795166015625, + 31.98377227783203, + 1146.8770751953125 + ], + "width": 7.79099609375 + }, + { + "id": "stair-flight-17", + "start": [ + 1422.9097290039062, + 30.294403076171875, + 1140.990029296875 + ], + "end": [ + 1422.9097290039062, + 31.98377227783203, + 1146.8751953125 + ], + "width": 19.9544482421875 + }, + { + "id": "stair-flight-18", + "start": [ + 1366.9917602539062, + 30.294403076171875, + 1140.990029296875 + ], + "end": [ + 1366.9917602539062, + 31.98377227783203, + 1146.8751953125 + ], + "width": 3.3423876953125 + }, + { + "id": "stair-flight-20", + "start": [ + 1074.6377825055804, + 14.831316947937012, + 1705.1758422851562 + ], + "end": [ + 1085.34908203125, + 9.488373756408691, + 1705.1758422851562 + ], + "width": 1.5269580078125 + }, + { + "id": "stair-flight-21", + "start": [ + -363.90753173828125, + 18.666223526000977, + 1716.7780064174108 + ], + "end": [ + -363.90753173828125, + 16.00090217590332, + 1722.148642578125 + ], + "width": 1.5264697265625 + }, + { + "id": "stair-flight-22", + "start": [ + -365.4140167236328, + 18.52136993408203, + 1716.7780064174108 + ], + "end": [ + -365.4140167236328, + 16.00090217590332, + 1722.148642578125 + ], + "width": 1.526500244140625 + }, + { + "id": "stair-flight-33", + "start": [ + 1176.7240380859375, + 7.501898288726807, + 1799.8161010742188 + ], + "end": [ + 1184.757958984375, + 11.337854385375977, + 1799.8161010742188 + ], + "width": 1.5269580078125 + }, + { + "id": "stair-flight-41", + "start": [ + 1180.731240234375, + 9.488373756408691, + 1795.295166015625 + ], + "end": [ + 1184.757958984375, + 11.406352043151855, + 1795.295166015625 + ], + "width": 1.527080078125 + }, + { + "id": "stair-flight-44", + "start": [ + 1350.1021263485861, + 29.14000701904297, + 986.6503295898438 + ], + "end": [ + 1354.0142431640625, + 27.30986213684082, + 986.6503295898438 + ], + "width": 9.0580859375 + }, + { + "id": "stair-flight-45", + "start": [ + 1444.2284326171875, + 27.30986213684082, + 986.6503295898438 + ], + "end": [ + 1448.1405494326639, + 29.14000701904297, + 986.6503295898438 + ], + "width": 9.0580859375 + }, + { + "id": "stair-flight-46", + "start": [ + 1444.200234375, + 27.30986213684082, + 1045.271240234375 + ], + "end": [ + 1448.1017020089287, + 29.14000701904297, + 1045.271240234375 + ], + "width": 9.0580859375 + }, + { + "id": "stair-flight-47", + "start": [ + 1350.1329171316963, + 29.14000701904297, + 1045.271240234375 + ], + "end": [ + 1354.023642578125, + 27.30986213684082, + 1045.271240234375 + ], + "width": 9.0580859375 + }, + { + "id": "stair-flight-49", + "start": [ + 1416.0104470230826, + -1.0605031251907349, + 1789.1435562678903 + ], + "end": [ + 1416.071514202764, + -3.046978712081909, + 1792.6420926487385 + ], + "width": 1.5324796057832555 + }, + { + "id": "stair-flight-58", + "start": [ + -430.80857849121094, + 16.00090217590332, + 1832.5419091796875 + ], + "end": [ + -430.80857849121094, + 17.68121337890625, + 1835.9136657714844 + ], + "width": 1.526500244140625 + }, + { + "id": "stair-flight-60", + "start": [ + 1438.5679931640625, + 30.294403076171875, + 1160.8964013671875 + ], + "end": [ + 1438.5679931640625, + 31.561429977416992, + 1164.2446594238281 + ], + "width": 10.1069140625 + }, + { + "id": "stair-flight-65", + "start": [ + 1342.6508178710938, + 30.294403076171875, + 1160.8964013671875 + ], + "end": [ + 1342.6508178710938, + 31.561429977416992, + 1164.2446594238281 + ], + "width": 2.5259814453125 + }, + { + "id": "stair-flight-66", + "start": [ + 1340.130859375, + 30.294403076171875, + 1160.8964013671875 + ], + "end": [ + 1340.130859375, + 31.561429977416992, + 1164.2446594238281 + ], + "width": 2.553935546875 + }, + { + "id": "stair-flight-82", + "start": [ + 1416.7938842773438, + -0.03301266208291054, + 1786.6985473632812 + ], + "end": [ + 1416.7938842773438, + -1.0605031251907349, + 1789.2044287109375 + ], + "width": 5.9451708984375 + }, + { + "id": "stair-flight-93", + "start": [ + -363.90753173828125, + 18.08680534362793, + 1717.4443969726562 + ], + "end": [ + -363.90753173828125, + 17.507389068603516, + 1719.483359375 + ], + "width": 1.5264697265625 + }, + { + "id": "stair-fallback-31", + "start": [ + 1196.7344140625, + 17.67403221130371, + 1798.6687622070312 + ], + "end": [ + 1198.7779541015625, + 18.496023178100586, + 1798.6687622070312 + ], + "width": 2.2461962890625 + }, + { + "id": "stair-fallback-37", + "start": [ + 1401.0733032226562, + 23.49647331237793, + 1797.4871215820312 + ], + "end": [ + 1402.603232421875, + 22.605976104736328, + 1797.4871215820312 + ], + "width": 5.0889697265625 + }, + { + "id": "stair-fallback-54", + "start": [ + -360.8945617675781, + 18.347543716430664, + 1717.4443969726562 + ], + "end": [ + -360.8945617675781, + 16.667232513427734, + 1720.8160009765625 + ], + "width": 1.5264697265625 + }, + { + "id": "stair-fallback-65", + "start": [ + -365.4140167236328, + 12.176742553710938, + 1729.4382847377233 + ], + "end": [ + -365.4140167236328, + 9.685245513916016, + 1734.8089208984375 + ], + "width": 1.526500244140625 + }, + { + "id": "stair-fallback-66", + "start": [ + -363.90753173828125, + 12.350567817687988, + 1729.4382847377233 + ], + "end": [ + -363.90753173828125, + 9.685245513916016, + 1734.8089208984375 + ], + "width": 1.5264697265625 + }, + { + "id": "stair-fallback-72", + "start": [ + -363.90753173828125, + 11.01790714263916, + 1732.1036987304688 + ], + "end": [ + -363.90753173828125, + 10.177751541137695, + 1734.1426611328125 + ], + "width": 4.53947021484375 + }, + { + "id": "stair-fallback-73", + "start": [ + -363.154296875, + 9.685245513916016, + 1734.7689819335938 + ], + "end": [ + -363.154296875, + 8.004934310913086, + 1738.1405859375 + ], + "width": 6.04593994140625 + } + ], + "candidateCount": 264, + "rejectedCount": 227 + }, + "zulfarrak": { + "collision": [ + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-000.glb", + "checksum": "58737b57663dd73b828e2ca80fcc1c7e548ddae72be9473bfdb85998778fa232" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-001.glb", + "checksum": "b5aacda77fdab384e038c9f725672c21adbc6e7eba323a8af154c18e970eb6d6" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-002.glb", + "checksum": "efac28c0e971def89f429a45e2ca5fb993f7d01f018003e7108a1cffa4ef8604" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-003.glb", + "checksum": "86e161edf7f9b16d2687b24cb83d6658de43d1b5f5c162a8688af16e7ea1d0ff" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-004.glb", + "checksum": "de85b394dc6d50433905c377d97c2a894540a908e866e44b9a85054f1d3a2feb" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-005.glb", + "checksum": "4de105f5c55e0cf0f4e25e08435a6a934367211d1944eba485412d874d53f76c" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-006.glb", + "checksum": "de7441c632aee009148e43a272b197f4688d3755f900596f071ac95ad9064409" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-007.glb", + "checksum": "61178557e4663f255d831cc57e18ec68ff15e60514ddf549afa7fb55a1b7b865" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-008.glb", + "checksum": "d8d7c079b71e91025e9b137a7054e19161dee2b0818053c002c4829f23cb7b95" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-009.glb", + "checksum": "a4b47773fdd6828727c300cbbe0a0fe77d352b99b60f04f45fe9a800661a5e40" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-010.glb", + "checksum": "7bb043fb3179dceeebf9f236f0f8bfad264e16fdf3e1cfd847ff0d6d2992639a" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-011.glb", + "checksum": "6984ad4a4671009db677fb73f3ecb52f205aea620273d14a743f9bc9e77b3f33" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-012.glb", + "checksum": "e060e9478585d9a28c954e08c5734e97730f40607cb2fdf4dc773401eccafb79" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-013.glb", + "checksum": "d82a6fe0724faa2e80053fb7b6bcb5dc6892a1d442f90128b246a83502ea3936" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-014.glb", + "checksum": "d4680580167b9958109ccbc6923399ee43f016d8438359a14d1331c6ac120210" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-015.glb", + "checksum": "e9bec24f4092282a6f1dde794fc1abc8464583f34b285bf8a7a613c5c2523349" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-016.glb", + "checksum": "901c88083c8fc41212d937e7931a8f1255a6c0ac55917f99aaa84655bc7fe18f" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-017.glb", + "checksum": "46b9f4f9b7108709c041bf31d5edfecfbba0e96381aa1df9735ccb0c1e7b5cfb" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-018.glb", + "checksum": "9cb7191851656cff1df6ee97c2b2729e187e403527fe25cbe04ba24e10f2b0d1" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-019.glb", + "checksum": "e4898a2d981a59f6634748554ff26a6e3188f780991c629eae6ed3d5a3f5a714" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-020.glb", + "checksum": "5bccf455ad1f05d6d204b66622e54275b695d31596bc9b281ff403cbea8de82f" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-021.glb", + "checksum": "70cbb6d0114d32d247ce1962cef21dfc80baddb3f30fdc859b3a5216d43bd634" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-022.glb", + "checksum": "893e94911ac23080407bc3165ebd12e3a6f497f8dde5587bf31b037973c8d76d" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-023.glb", + "checksum": "d1b2d63da5c4911dbbd6194d6d3e785c4763c5c1f36e7de3aeabe4b0235b5814" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-024.glb", + "checksum": "fdb9468b38974e30d4e150f39e7804138e1b9040343b5a31ede245020fe6dd06" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-025.glb", + "checksum": "fbe65d67f0e006f5613ee513ae7a71f66be5c3e6d31d1dc69f98077decec4675" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-026.glb", + "checksum": "66e00cdebc6a2985588ae8db47b9e4c35f7dc69dac770b179dce5bfaea9b1f15" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-027.glb", + "checksum": "82a29185cc49f23732e6f4efd07e32e779da9f19cea97e3c99b2a15ad2403748" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-028.glb", + "checksum": "231fab5bbf47167ca737cad0d44df66f3a2b3b5766a9acd1d113864d26dbd836" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-029.glb", + "checksum": "f8399aaf76002e85117a434006d5e8ce6103089c7a2c5e238b36775c0fce028d" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-030.glb", + "checksum": "62a3b7ee67e65c0a8570a8a08ce8f33fccdf923dcf8d9239f6e08e3aca16e682" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-031.glb", + "checksum": "f3e16382f6bdef2a525bf235d6df35cd1b01a6da4763ff02198e0a8ec7432cc6" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-032.glb", + "checksum": "d1efbc9e159894c64756832cda3b70e7212a598d57df05ad9971b6599893a0a7" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-033.glb", + "checksum": "9f9b2d9706cdd276f24a5b12f20bb5d07bc91ebdab5e4e9447ad45c10b05889d" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-034.glb", + "checksum": "e0d3eeb250ffd74acb214f276b7cdf08194984ba4c26b9f04803ff0ac5b6ee5b" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-035.glb", + "checksum": "a1c39c6f1aaa2986f54b578d834b5a9631aa156981019942a6b017956a5117bd" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-036.glb", + "checksum": "19ef36bfb8d029226e17d5b1aadf1df019188cecffdc2d3495d50f3999bea894" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-037.glb", + "checksum": "426e016cf17766d0736bf6f746e2f0e56f9facb783ac97044f662b4d63f0ed19" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-038.glb", + "checksum": "40100ad3787387192adce161ec406812ea7cd37f6d88ced3365e88f7a2ff2493" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-039.glb", + "checksum": "42679285a0952e25f26e339ef4e5a5b328fffe44fb27cca94a5691a11c787100" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-040.glb", + "checksum": "c4a184348ed67e507d5d7dbd77ade32cd4a5b1c1248f7ac1f92ebb88a371c6a7" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-041.glb", + "checksum": "d691404413de222c7aa179a749e5cf058ac7e02b58de3314be8f12d4e7d56306" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-042.glb", + "checksum": "13fa764bbf7b190e8141f487cb079b83666f00d96599da67e12484477b37bc80" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-043.glb", + "checksum": "3058313be1938a2511db66c830f28b382cfd354c19ede3a57123d779b09e0c09" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-044.glb", + "checksum": "7d3fdcef115066f7cf4eafb12d6de36a452a5cafeae7a2ccfc0d08513997ccd7" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-045.glb", + "checksum": "567ef13b41aa4926ea4626f5e28b020701a2c9cb10a28e07ace27ca3b1428253" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-046.glb", + "checksum": "137de2fddfd55345af289d868a01ff21dbeca852283a84d5e17b268d4f17ee03" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-047.glb", + "checksum": "272124959ba852b804e1e324c21968c03a8fd1239396750bffa38d12a66e51d8" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-048.glb", + "checksum": "a6156b00a74341627f3b2104f5636c252fd405a14ad767d2d7630fe2d144ac6e" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-049.glb", + "checksum": "d46ec59311fb48c0cfbdb4de17e4f26862b2f1d297744914be0c5114b3ef6417" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-050.glb", + "checksum": "6c23c45b44a81e1c02ec4b6f18b7d0ec9346c5517b7cf04c789e68a56f8641e1" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-051.glb", + "checksum": "beccd61ea14ece654733b45359c7dc9e4eab7ec645cd74a88245fcde76731177" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-052.glb", + "checksum": "6577b398f1534109a8cf0f27b509b67d2d5311efa26345115346bfd96de07f3c" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-053.glb", + "checksum": "c98568055da4bb00e9fd3169f40b827dd1a21431029ffe44b19c4efe56fbca75" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-054.glb", + "checksum": "6934db4d97da43522d8e177cef641ee84b4c036187f1b1c932f4f5e3af503110" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-055.glb", + "checksum": "e85a89c4c568cc8311d7fdd96f26738281a4b87398e12076552601dd2d7ffa4f" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-056.glb", + "checksum": "182660b091693bc02910bdb638cf062e61b6f05da0d6f058557ac2282bb6af06" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-057.glb", + "checksum": "240fcdff808996594837277151b7c06084d31c24c8ed087309c5058d763dbe63" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-058.glb", + "checksum": "8d2b8b20752c2d167202f3c86723cf685798be228ddc2bc6a4566bb7166e39e7" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-059.glb", + "checksum": "971b23e003bb14360749ee81116210d1fef6a5ee54c4fb37a7aa982760f8f334" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-060.glb", + "checksum": "4abbbb3ec7c824d1f5977ae28453f7a1f003c76b832c3dc8cdbef8ab943d0b3e" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-061.glb", + "checksum": "afbf8096fd50d97550bd22aec35c75cfb7dc7b42f4fc3e417fa8fc9ee16f48a3" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-062.glb", + "checksum": "4f462eac1495855d3e586799bda32b4b3fe853a1f5c266eb15dd78e2ce15c5de" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-063.glb", + "checksum": "586bd9ec924ef0f1de8c4090c9ee0169ef28fd4af6c1b302a32af96c49b6f4ea" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-064.glb", + "checksum": "1be5847e5f904b27a038271c4a35e9f9036a33b89f23cc4474b61f98deb97dc1" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-065.glb", + "checksum": "c34cc360df4aa692a10f3089662e98aaa76616d2e5c6145cf3df7241a0c0ea37" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-066.glb", + "checksum": "e91eb03f63becd1cc08f8eb392cab9262a1bf753fb6b3c45e957fa844fe26d60" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-067.glb", + "checksum": "c9e14e9611ef19764b5960522b279cd87ef92ed1c2686882ce7fbe8ece05f004" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-068.glb", + "checksum": "86ab4df723625d0a6185607f5a565b184b200765a6afc41b5740da9ad631edd6" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-069.glb", + "checksum": "b1f5bb8533e0fff41107c4315d50405f6d1e8db54794a4fd8c60da51c8d0fb55" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-070.glb", + "checksum": "08be59dd805ac2fa0bbb2e5714072c4d2a45ce239e7954448bec9f095a42c0da" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-071.glb", + "checksum": "8cd098fdcc5a8a8a3e7a14a6eb9451ab3a6097ba19fe00433b6823b3a9c0e2a8" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-072.glb", + "checksum": "066fc08d9e55225aa5e7004015bd3adb6cf897daac1b1c5160c6ef9c37b21b9c" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-073.glb", + "checksum": "7587c39dacc98fa9e27f15ec65de9f42cdb604519a258d364d43c1049653f0b2" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-074.glb", + "checksum": "c3b1e6335a4c4f752eba5cec8ef4f2256b92901ccdaf3070eecd02147511cd4c" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-075.glb", + "checksum": "e3287de4f455839791084570e1c9f6331d319a5708f57fed194b8ade37bfa187" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-076.glb", + "checksum": "1dcff1f672251ea631c299444fb19e1df171adc85e9515ce74749e5a86bcda30" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-077.glb", + "checksum": "a1574115076b3994cb429023309f438dfa6d80d73c7d1bdffcafb40b24efd55d" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-078.glb", + "checksum": "2a90ad2e1f813b34d6528193a344929255ff01a9cddadfbff87d93fb06e35422" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-079.glb", + "checksum": "9ccebe5910e0e11b627e0bb3022c93758ea34bbb9be3ea037495aba9332ec4d1" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-080.glb", + "checksum": "e53dde1f854244234795a186520e8a9daa8593f4deb5402cda7e984a0581613b" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-081.glb", + "checksum": "7fe560c86498dab84883dcb85c11050e02d74017d9f6dc69291a5e65da1be5d1" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-082.glb", + "checksum": "ba634fb75402ee64bdc5ed2716e07dc03b067a51c38734c01ff253a7793f5f63" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-083.glb", + "checksum": "8471a0ca83bb7812591b0817c1e32add00e1fcb101cee932f2af889c3cbdba04" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-084.glb", + "checksum": "e8fc9e3342a1747f0da7289fe33638f322ca94dc0a1c3fdc19e5b3df0e47cf7f" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-085.glb", + "checksum": "bd886be83fb10ec169a4fe8a1a8e6c193d7d18409e8fcaf1cae67f17039545cf" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-086.glb", + "checksum": "e9c765735dcf36190ebe81fcfaf3d3b78f893a5b6db586ee99d0590fdc7c2705" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-087.glb", + "checksum": "c4907b165ad8a4f262ff9aa82a94d73eded61b136c5eef2b02283dd20e60400a" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-088.glb", + "checksum": "17880771d4a909a61ec8d678af475c845c38d071c377412c7d0d6b15510a76ac" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-089.glb", + "checksum": "a8f5a2aa583bfaffa667acf5f545edf3be7ef8e69257f6e38252f622dee2bda4" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-090.glb", + "checksum": "d9f48025b710bc3d6a1ac7cab8afef79d7dcbd7d41de22ee0a878af798738ac9" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-091.glb", + "checksum": "90e4dd576f278dfeda536db2cfd2ec005f1f188f1bdbb2738354df5423642535" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-092.glb", + "checksum": "f7a46117e708d9656617a1513ead32fdc250055e3d34e66f8399221e6f542cf1" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-093.glb", + "checksum": "9c048f2e1753c457c6c617a3f636c7c105175a0c85949290127dabb201652d77" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-094.glb", + "checksum": "e822279716a12cc1476aa82c8c9fdb83a85ec738290dd263023331294cfdfee5" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-095.glb", + "checksum": "373f99cbaad23b2ace52cd10355c6fdec602b84922fe2939a885b05526d09d92" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-096.glb", + "checksum": "a909a1fb5feb078e48fd994b924d95f1615491aef99ab96f55809e290ce8fed1" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-097.glb", + "checksum": "cb89a22ad3ed6227f602b28099a229848bedba6a9df7a188d8ebbfa2d72f9100" + }, + { + "url": "/assets/game/manastorm/209-tanarisinstance/collision-098.glb", + "checksum": "1e9def5c001274548214bc325e980b8db229650926f3ceb78404b26211fabc7a" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/209-tanarisinstance/navigation.glb", + "checksum": "ad9ae8e7f13bbbb0fb082dadbea4e3aaef333db9819c45cee230386c4705f2df", + "size": 7536340, + "triangleCount": 657069 + }, + "ramps": [ + { + "id": "stair-flight-1", + "start": [ + 1.7259266923655332, + 8.505298614501953, + 857.2012893983813 + ], + "end": [ + -4.517125316449494, + 9.50668716430664, + 859.3509445962197 + ], + "width": 2.656080579257964 + } + ], + "candidateCount": 1, + "rejectedCount": 0 + }, + "blackrock-spire": { + "collision": [ + { + "url": "/assets/game/manastorm/229-blackrockspire/collision.glb", + "checksum": "eb47fcb1cf8ee74d46e4b8f0e8f8a057fb36ad2a4ceaffc2c8a3a9f1c7e4d309" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/229-blackrockspire/navigation.glb", + "checksum": "3fb6be3c02b2d84d1ffb7a943570df7f101173338a5dc9f109e34c275cfcabf9", + "size": 435408, + "triangleCount": 33545 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "blackrock-depths": { + "collision": [ + { + "url": "/assets/game/manastorm/230-blackrockdepths/collision.glb", + "checksum": "78fb36852edfa10dbc7405ec3f2f02be825381114014919aa186972b976112e3" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/230-blackrockdepths/navigation.glb", + "checksum": "5aecda48e63b281d3841d604b7dfc2ae3ff4c45385b0c030fccfeeb48e1c1bda", + "size": 575024, + "triangleCount": 46606 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "scholomance": { + "collision": [ + { + "url": "/assets/game/manastorm/289-schoolofnecromancy/collision.glb", + "checksum": "b3e631bba1aa625fcb824fe1eafd1c640f372cda7c640d56402a73d91a40034d" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/289-schoolofnecromancy/navigation.glb", + "checksum": "ca327ee985145aa0675c2d60ec7b74ea93836d4d7e331af9d6c7e264eacb6948", + "size": 92620, + "triangleCount": 6886 + }, + "ramps": [ + { + "id": "stair-flight-1", + "start": [ + 62.20846939086914, + -50.59125900268555, + 6.9992426958951075 + ], + "end": [ + 62.20846939086914, + -64.6360092163086, + 28.34836296081543 + ], + "width": 7.439868469238281 + }, + { + "id": "stair-flight-2", + "start": [ + -90.36037353515626, + -52.277591705322266, + 88.90494155883789 + ], + "end": [ + -70.05644049160722, + -38.256935119628906, + 88.90494155883789 + ], + "width": 16.889346618652343 + }, + { + "id": "stair-flight-6", + "start": [ + 53.833165130615235, + -54.51800537109375, + 72.86972427368164 + ], + "end": [ + 66.90076043870714, + -46.03815841674805, + 72.86972427368164 + ], + "width": 7.512149353027343 + }, + { + "id": "stair-flight-8", + "start": [ + 54.58383750915527, + -38.64238357543945, + 47.07785415649414 + ], + "end": [ + 68.36744598388673, + -46.086341857910156, + 47.07785415649414 + ], + "width": 15.01029541015625 + }, + { + "id": "stair-flight-9", + "start": [ + -58.532989501953125, + -42.76185607910156, + 3.9851646041870117 + ], + "end": [ + -58.532989501953125, + -38.907379150390625, + 13.950439929962155 + ], + "width": 8.138492126464843 + }, + { + "id": "stair-flight-11", + "start": [ + -37.26712226867676, + -71.59815216064453, + -102.2839422607422 + ], + "end": [ + -37.26712226867676, + -59.022926330566406, + -86.72967147827148 + ], + "width": 6.2353434753417964 + }, + { + "id": "stair-flight-13", + "start": [ + -83.54276184082032, + -29.656635284423828, + -56.5804443359375 + ], + "end": [ + -71.81085268656412, + -21.995864868164062, + -56.5804443359375 + ], + "width": 11.222072143554687 + }, + { + "id": "stair-flight-14", + "start": [ + -125.37803268432617, + -32.402950286865234, + -58.435412089029946 + ], + "end": [ + -125.37803268432617, + -37.5101318359375, + -50.60214324951172 + ], + "width": 3.8262973022460933 + }, + { + "id": "stair-flight-15", + "start": [ + -165.99240783691405, + -45.96588897705078, + -17.481603145599365 + ], + "end": [ + -160.6525137765067, + -42.42458724975586, + -17.481603145599365 + ], + "width": 4.235832710266113 + }, + { + "id": "stair-flight-16", + "start": [ + -24.872572898864746, + -76.8498764038086, + -182.58954528808593 + ], + "end": [ + -24.872572898864746, + -72.65813446044922, + -174.63983662923178 + ], + "width": 5.440358657836914 + }, + { + "id": "stair-flight-17", + "start": [ + -49.508094787597656, + -76.8498764038086, + -182.56545166015624 + ], + "end": [ + -49.508094787597656, + -72.65813446044922, + -174.6157430013021 + ], + "width": 5.470469970703125 + }, + { + "id": "stair-flight-18", + "start": [ + 39.54636240005493, + -72.70631408691406, + -106.75187301635742 + ], + "end": [ + 44.166564025878905, + -75.30809020996094, + -106.75187301635742 + ], + "width": 6.819537658691406 + }, + { + "id": "stair-flight-19", + "start": [ + 26.271504402160645, + -75.25990295410156, + -117.77210652669271 + ], + "end": [ + 26.271504402160645, + -72.65813446044922, + -113.14153628879124 + ], + "width": 3.4649367523193355 + }, + { + "id": "stair-flight-20", + "start": [ + -58.532989501953125, + -40.015541076660156, + 10.58595844268799 + ], + "end": [ + -58.532989501953125, + -38.232845306396484, + 15.026485125223793 + ], + "width": 8.138492126464843 + }, + { + "id": "stair-flight-22", + "start": [ + -153.73035339355468, + -41.19597244262695, + -15.36164140701294 + ], + "end": [ + -149.50550406319755, + -38.064212799072266, + -15.36164140701294 + ], + "width": 8.475756187438964 + }, + { + "id": "stair-flight-23", + "start": [ + -71.85888580322266, + -21.995864868164062, + -56.5804443359375 + ], + "end": [ + -67.6849594116211, + -19.24955177307129, + -56.5804443359375 + ], + "width": 11.222072143554687 + }, + { + "id": "stair-flight-24", + "start": [ + -65.46688242594402, + -75.16354370117188, + -106.85124588012695 + ], + "end": [ + -61.82936713809059, + -72.489501953125, + -106.85124588012695 + ], + "width": 4.886279602050781 + }, + { + "id": "stair-flight-26", + "start": [ + -86.98770050048829, + -31.94523048400879, + -53.77390480041504 + ], + "end": [ + -83.48469924926758, + -29.656635284423828, + -53.77390480041504 + ], + "width": 5.608993072509765 + }, + { + "id": "stair-flight-29", + "start": [ + -27.526540756225586, + -43.34002685546875, + -31.779301643371582 + ], + "end": [ + -24.7241658782959, + -45.170902252197266, + -31.779301643371582 + ], + "width": 7.728951950073242 + }, + { + "id": "stair-flight-32", + "start": [ + -112.6100902557373, + -72.82676696777344, + -106.87533187866211 + ], + "end": [ + -108.66742797851562, + -75.45262908935547, + -106.87533187866211 + ], + "width": 5.512622375488281 + }, + { + "id": "stair-flight-38", + "start": [ + 65.91839981079102, + -63.28693771362305, + 26.248629570007324 + ], + "end": [ + 65.91839981079102, + -64.39510345458984, + 28.34836296081543 + ], + "width": 14.85972930908203 + }, + { + "id": "stair-fallback-3", + "start": [ + -50.611236572265625, + -19.24955177307129, + -56.435903549194336 + ], + "end": [ + -47.80886169433594, + -21.032245635986328, + -56.435903549194336 + ], + "width": 11.414794464111328 + }, + { + "id": "stair-fallback-5", + "start": [ + -47.15812210083008, + -9.902446746826172, + 19.936925888061523 + ], + "end": [ + -33.55250523885091, + -2.2898569107055664, + 19.936925888061523 + ], + "width": 8.957564849853515 + }, + { + "id": "stair-fallback-7", + "start": [ + -34.7998571395874, + -29.632545471191406, + -56.435903549194336 + ], + "end": [ + -31.31692596435547, + -31.921140670776367, + -56.435903549194336 + ], + "width": 11.414794464111328 + }, + { + "id": "stair-fallback-9", + "start": [ + -22.51557258605957, + -57.11977767944336, + -58.08609962463379 + ], + "end": [ + -16.296363830566406, + -53.02439498901367, + -58.08609962463379 + ], + "width": 7.439864654541015 + }, + { + "id": "stair-fallback-10", + "start": [ + -19.769257583618163, + -55.288902282714844, + -65.49392127990723 + ], + "end": [ + -16.966882705688477, + -53.7230224609375, + -65.49392127990723 + ], + "width": 7.415778656005859 + }, + { + "id": "stair-fallback-11", + "start": [ + -17.022942581176757, + -53.48211669921875, + -61.78398895263672 + ], + "end": [ + -14.22056770324707, + -51.675331115722656, + -61.78398895263672 + ], + "width": 14.835643310546875 + }, + { + "id": "stair-fallback-12", + "start": [ + -14.276628532409667, + -51.675331115722656, + -61.78398895263672 + ], + "end": [ + -8.730234009878975, + -48.30266189575195, + -61.78398895263672 + ], + "width": 14.835643310546875 + }, + { + "id": "stair-fallback-13", + "start": [ + -9.458533325195312, + -48.51947784423828, + -61.78398895263672 + ], + "end": [ + -6.688279469807942, + -46.929508209228516, + -61.78398895263672 + ], + "width": 14.835643310546875 + }, + { + "id": "stair-fallback-14", + "start": [ + -7.410842933654785, + -47.14632034301758, + -65.49392127990723 + ], + "end": [ + -5.311107397079468, + -46.014068603515625, + -65.49392127990723 + ], + "width": 7.415778656005859 + }, + { + "id": "stair-fallback-15", + "start": [ + -6.712219276428223, + -46.688602447509766, + -61.78398895263672 + ], + "end": [ + -4.612483739852905, + -45.55635070800781, + -61.78398895263672 + ], + "width": 14.835643310546875 + }, + { + "id": "stair-fallback-17", + "start": [ + 53.833165130615235, + -54.30119323730469, + 80.36186218261719 + ], + "end": [ + 55.92085838317871, + -53.193031311035156, + 80.36186218261719 + ], + "width": 7.51212646484375 + }, + { + "id": "stair-fallback-18", + "start": [ + 28.00601863861084, + -73.66993713378906, + -115.2265463256836 + ], + "end": [ + 28.00601863861084, + -72.9713134765625, + -113.65680694580078 + ], + "width": 6.933965225219726 + }, + { + "id": "stair-fallback-23", + "start": [ + -121.57173919677734, + -32.45112991333008, + -58.43541145324706 + ], + "end": [ + -121.57173919677734, + -33.77610778808594, + -56.26340576171875 + ], + "width": 11.43888427734375 + }, + { + "id": "stair-fallback-24", + "start": [ + -113.97119140625, + -33.36656951904297, + -57.011666107177724 + ], + "end": [ + -113.97119140625, + -36.16106414794922, + -52.72210403442382 + ], + "width": 3.8022113037109375 + }, + { + "id": "stair-fallback-28", + "start": [ + -113.97119140625, + -33.82428741455078, + -56.28533935546874 + ], + "end": [ + -113.97119140625, + -36.040611267089844, + -52.72210403442382 + ], + "width": 3.8022113037109375 + } + ], + "candidateCount": 73, + "rejectedCount": 37 + }, + "stratholme": { + "collision": [ + { + "url": "/assets/game/manastorm/329-stratholme/collision.glb", + "checksum": "f6482e63a2547489bb27a6eb5ac1c556af5a93dd7995ce5fe78e96bc300e53df" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/329-stratholme/navigation.glb", + "checksum": "987b03c42d5a9d112baf9b8f5b0b51b5e0f9c760c5769bf350d2e8cfebb19f2f", + "size": 683920, + "triangleCount": 53098 + }, + "ramps": [ + { + "id": "stair-flight-7", + "start": [ + -130.24910521651552, + -11.49190616607666, + 164.24542162609677 + ], + "end": [ + -131.87329186959994, + -10.586028099060059, + 165.92731614061972 + ], + "width": 19.99244000364371 + }, + { + "id": "stair-flight-14", + "start": [ + -150.81269319179125, + -6.05662727355957, + -142.33515187009735 + ], + "end": [ + -151.76707259241377, + -5.0974602699279785, + -140.19158064004247 + ], + "width": 14.163516204806587 + }, + { + "id": "stair-flight-15", + "start": [ + -440.73667064462825, + -3.3389878273010254, + -194.82063460151807 + ], + "end": [ + -438.9738405880905, + -2.433107852935791, + -193.34144455124908 + ], + "width": 18.17532158299311 + }, + { + "id": "stair-flight-18", + "start": [ + -519.558847210649, + -23.694643020629883, + -59.82396160883388 + ], + "end": [ + -521.4121269504531, + -22.788761138916016, + -58.47747506157555 + ], + "width": 18.170875798551055 + }, + { + "id": "stair-flight-19", + "start": [ + -141.7100457332137, + -6.05662727355957, + -138.15622297002375 + ], + "end": [ + -142.67551153450972, + -5.0974602699279785, + -136.08577487762906 + ], + "width": 5.811559931056923 + }, + { + "id": "stair-flight-20", + "start": [ + -478.58675092818885, + -8.614405632019043, + -101.3393860052947 + ], + "end": [ + -477.84416437801195, + -9.52028751373291, + -99.18275806865523 + ], + "width": 20.026363466773905 + }, + { + "id": "stair-flight-21", + "start": [ + -419.28465270996094, + -3.552135944366455, + -88.66045379638673 + ], + "end": [ + -419.28465270996094, + -4.4580159187316895, + -86.38240142822266 + ], + "width": 18.190867919921875 + }, + { + "id": "stair-flight-22", + "start": [ + -199.6531187134227, + -18.365938186645508, + 23.183873360792177 + ], + "end": [ + -200.95933411982907, + -17.46005630493164, + 25.04934228982728 + ], + "width": 17.77427708956019 + }, + { + "id": "stair-fallback-4", + "start": [ + -528.4331268263062, + -3.072552442550659, + -329.64649033664114 + ], + "end": [ + -526.4639912099191, + -2.166672706604004, + -328.4160378402077 + ], + "width": 19.975842602347615 + }, + { + "id": "stair-fallback-7", + "start": [ + -304.2551265202943, + 0.17795756459236145, + -154.80039116277214 + ], + "end": [ + -302.8194594088547, + -0.727922260761261, + -153.0274899291245 + ], + "width": 19.964229118715554 + }, + { + "id": "stair-fallback-24", + "start": [ + -31.47852403112363, + -11.172183990478516, + 91.68321397025954 + ], + "end": [ + -30.537263461939517, + -12.078065872192383, + 93.79731982247307 + ], + "width": 19.987154231511173 + }, + { + "id": "stair-fallback-25", + "start": [ + -69.55823607198423, + -6.269775390625, + -118.01685835707116 + ], + "end": [ + -70.57327547089261, + -5.363895416259766, + -115.93571917813497 + ], + "width": 19.965560503839942 + } + ], + "candidateCount": 51, + "rejectedCount": 39 + }, + "maraudon": { + "collision": [ + { + "url": "/assets/game/manastorm/349-mauradon/collision.glb", + "checksum": "471eb50b6a3c923363404555572768aee09e9005c5fe7e2230c33262808a912b" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/349-mauradon/navigation.glb", + "checksum": "af2227f0940b81a01b9c9df004692598c2d41b1ce33d51a70bdde2e66d8115aa", + "size": 435520, + "triangleCount": 34479 + }, + "ramps": [], + "candidateCount": 9, + "rejectedCount": 9 + }, + "ragefire-chasm": { + "collision": [ + { + "url": "/assets/game/manastorm/389-orgrimmarinstance/collision.glb", + "checksum": "7e690b5792ddc9f0e096ea537fd46f5ba999d72497bbd9efb7fd463b2c9a5862" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/389-orgrimmarinstance/navigation.glb", + "checksum": "479f5bf477d1a5bbbcc1a2de078fcd04dfc086c15069257a71c1ba1e07fb4ecc", + "size": 245956, + "triangleCount": 18137 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "dire-maul": { + "collision": [ + { + "url": "/assets/game/manastorm/429-diremaul/collision.glb", + "checksum": "90a73cfc0221c68f6416227414f38dbc80045548cfb316ca227921dbb5ce5866" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/429-diremaul/navigation.glb", + "checksum": "53cbf4381ae47fe2d69a2c08318df7d7fa6b08ace81d6e981a56280f83764405", + "size": 583284, + "triangleCount": 48905 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "shattered-halls": { + "collision": [ + { + "url": "/assets/game/manastorm/540-hellfiremilitary/collision.glb", + "checksum": "4ddfc2741199bf4c4915315f8690412319476a3e5e4033ba403f4a5c70a44f5b" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/540-hellfiremilitary/navigation.glb", + "checksum": "2f7b6b0e32d292eabdf0593ea352a37abb1a4c2143a113ee484bb33bc04417c9", + "size": 197280, + "triangleCount": 15319 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "blood-furnace": { + "collision": [ + { + "url": "/assets/game/manastorm/542-hellfiredemon/collision.glb", + "checksum": "552ec34a2ddc7f2621bc0496a2fa27c105283c36b5f1626cac609c70adbb8a2d" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/542-hellfiredemon/navigation.glb", + "checksum": "800a067caba512c2e36177b682b548d45d7efecb4944dbafd202e1e651de78ee", + "size": 382552, + "triangleCount": 28809 + }, + "ramps": [ + { + "id": "stair-flight-1", + "start": [ + -65.09699058532715, + -34.130516052246094, + -84.42904281616211 + ], + "end": [ + -62.26026626586914, + -35.446624755859375, + -84.42904281616211 + ], + "width": 29.60501434326172 + } + ], + "candidateCount": 1, + "rejectedCount": 0 + }, + "hellfire-ramparts": { + "collision": [ + { + "url": "/assets/game/manastorm/543-hellfirerampart/collision.glb", + "checksum": "2e0a776a82e3d43e9dfbc29329bbeb1f864aa2deaa5c88977a180ac2e6abbf67" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/543-hellfirerampart/navigation.glb", + "checksum": "f3c70d131401f432288971bb1abbd7458d3dd22ab419f8308e1e58126f914c27", + "size": 144020, + "triangleCount": 10840 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "steamvault": { + "collision": [ + { + "url": "/assets/game/manastorm/545-coilfangpumping/collision.glb", + "checksum": "fd6aaedc17b593f9de1b31bbe6c18c75760c4fd7d525a206f1705f3df4a13a0c" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/545-coilfangpumping/navigation.glb", + "checksum": "9e9f8a632d6421e36a9d0f0c7505c9b1d272e7081c4d44de47e431a31eb2aa66", + "size": 272688, + "triangleCount": 20491 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "underbog": { + "collision": [ + { + "url": "/assets/game/manastorm/546-coilfangmarsh/collision.glb", + "checksum": "878c191c326b8c650c521e295fa052961f10c679fc9bc1aabef9089fc79bf145" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/546-coilfangmarsh/navigation.glb", + "checksum": "7f238832da85f4ceebdad8590f9d74cf334dd45657b0b763c485935db1748a20", + "size": 414420, + "triangleCount": 30573 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "slave-pens": { + "collision": [ + { + "url": "/assets/game/manastorm/547-coilfangdraenei/collision.glb", + "checksum": "7afe7f72ebcd5a8d9402f63cd1d36adedadb374bf10dfa798f5c6320abddd6a6" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/547-coilfangdraenei/navigation.glb", + "checksum": "86f3888f0599202273821f7ee75a29d65efff22c1bbb9d3ff00b280b485a667f", + "size": 300600, + "triangleCount": 22696 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "arcatraz": { + "collision": [ + { + "url": "/assets/game/manastorm/552-tempestkeeparcane/collision.glb", + "checksum": "dbcbf99863d27f9e70fd476154093b4b4862ed7a4bbe57d1d51816e02b272d3a" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/552-tempestkeeparcane/navigation.glb", + "checksum": "72710decaeda6a422d7a717b5e203be5ed645e48061dcd78a8e82832bee78d1c", + "size": 66316, + "triangleCount": 5937 + }, + "ramps": [], + "candidateCount": 27, + "rejectedCount": 27 + }, + "botanica": { + "collision": [ + { + "url": "/assets/game/manastorm/553-tempestkeepatrium/collision.glb", + "checksum": "ff352b43d8bca1d99cdb5ca3839b00367db27fb3fc9fb9f96512c5f3940cd576" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/553-tempestkeepatrium/navigation.glb", + "checksum": "867dd48c578babc40ce5108c7be1a5e13f08685e30d85b0a4600e244b58db36c", + "size": 309724, + "triangleCount": 23024 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "mechanar": { + "collision": [ + { + "url": "/assets/game/manastorm/554-tempestkeepfactory/collision.glb", + "checksum": "5442ee065fa0a137c1f1728cf30f907aae02537050aa056589d23c551858278a" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/554-tempestkeepfactory/navigation.glb", + "checksum": "042f0d2003f53bf20dc2be1b74c5d583f46c6282636159fa6341c20ea03e10f5", + "size": 140680, + "triangleCount": 10194 + }, + "ramps": [], + "candidateCount": 6, + "rejectedCount": 6 + }, + "shadow-labyrinth": { + "collision": [ + { + "url": "/assets/game/manastorm/555-auchindounshadow/collision.glb", + "checksum": "879baca1993fd08f5f8305f711af7ac4011dbf1bb124c72743f7a6a2f6fea7d7" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/555-auchindounshadow/navigation.glb", + "checksum": "0c6b3b53163f67c1ef13502abfe5d289548190a37df91a445d1c6a38b5651111", + "size": 123396, + "triangleCount": 9231 + }, + "ramps": [ + { + "id": "stair-flight-1", + "start": [ + 307.77710815429685, + 8.061164855957031, + -38.992424964904785 + ], + "end": [ + 316.9444749620226, + 12.705889701843262, + -38.992424964904785 + ], + "width": 23.090074081420898 + } + ], + "candidateCount": 34, + "rejectedCount": 33 + }, + "sethekk-halls": { + "collision": [ + { + "url": "/assets/game/manastorm/556-auchindoundemon/collision.glb", + "checksum": "c32469a3f686597f16e8e4b6105162fe3ab57f923a4858d2977e297df744db3f" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/556-auchindoundemon/navigation.glb", + "checksum": "2cb2e0ac415ecdd78a602b1686af5d3d25d3803145aff71e60a3a6ef06454f29", + "size": 125684, + "triangleCount": 9052 + }, + "ramps": [], + "candidateCount": 52, + "rejectedCount": 52 + }, + "mana-tombs": { + "collision": [ + { + "url": "/assets/game/manastorm/557-auchindounethereal/collision.glb", + "checksum": "833cbf07454221f35db493f84ad2508c95395ef1154bcd88073f093c74532b65" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/557-auchindounethereal/navigation.glb", + "checksum": "5c905d93955353a9b8aea1a0e67b7c362a2b3d8095b5a10826e130b17214a1ec", + "size": 176400, + "triangleCount": 12947 + }, + "ramps": [ + { + "id": "stair-flight-8", + "start": [ + 371.91064453125, + 0.6207756400108337, + -17.473958053588866 + ], + "end": [ + 371.91064453125, + 2.4220046997070312, + -13.33460807800293 + ], + "width": 2.41129638671875 + }, + { + "id": "stair-flight-9", + "start": [ + 341.5148620605469, + 16.40483856201172, + 15.073604583740234 + ], + "end": [ + 344.62938598632815, + 15.504223823547363, + 15.073604583740234 + ], + "width": 2.4112925720214844 + }, + { + "id": "stair-flight-10", + "start": [ + 341.5148620605469, + 16.40483856201172, + 17.464889526367188 + ], + "end": [ + 344.62938598632815, + 15.224723815917969, + 17.464889526367188 + ], + "width": 2.411277313232422 + } + ], + "candidateCount": 42, + "rejectedCount": 39 + }, + "auchenai-crypts": { + "collision": [ + { + "url": "/assets/game/manastorm/558-auchindoundraenei/collision.glb", + "checksum": "e88f28de460fd3373ffec258840ebe62deedaf3df84b7d66cb98fa83b4fe0baa" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/558-auchindoundraenei/navigation.glb", + "checksum": "783770eef6c686c74ddb736af3ae9450796f824f2ed66b3d7e95f83c1dc70978", + "size": 161076, + "triangleCount": 11974 + }, + "ramps": [ + { + "id": "stair-flight-2", + "start": [ + 28.034057896931966, + 19.42527198791504, + -387.623291015625 + ], + "end": [ + 37.19494042573152, + 26.568115234375, + -387.623291015625 + ], + "width": 11.9446826171875 + }, + { + "id": "stair-flight-7", + "start": [ + 4.147374750773112, + 15.330838203430176, + -169.54988861083984 + ], + "end": [ + 16.972931226094566, + 25.223230361938477, + -169.54988861083984 + ], + "width": 13.199885864257812 + }, + { + "id": "stair-flight-8", + "start": [ + 0.48131273349126186, + 12.551406860351562, + -169.54988861083984 + ], + "end": [ + 5.078984056909879, + 16.048110961914062, + -169.54988861083984 + ], + "width": 13.199885864257812 + }, + { + "id": "stair-flight-11", + "start": [ + 14.186703643798829, + 23.01163673400879, + -156.50448608398438 + ], + "end": [ + 16.991195678710938, + 25.223230361938477, + -156.50448608398438 + ], + "width": 12.930919189453125 + }, + { + "id": "stair-flight-12", + "start": [ + 8.707534116109214, + 18.827545166015625, + -156.50448608398438 + ], + "end": [ + 11.497083981831867, + 20.919591903686523, + -156.50448608398438 + ], + "width": 12.930919189453125 + } + ], + "candidateCount": 40, + "rejectedCount": 35 + }, + "magisters-terrace": { + "collision": [ + { + "url": "/assets/game/manastorm/585-sunwell5manfix/collision.glb", + "checksum": "99a02b2b82db8f1526b5a62d6e886e099b036d670bb7ce3ed423ec7397daa3e3" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/585-sunwell5manfix/navigation.glb", + "checksum": "a3f44f11f336fdd894fc1909bd23718ecfce8fd5eb2e54246999f5a0bcdd02d1", + "size": 195372, + "triangleCount": 14702 + }, + "ramps": [ + { + "id": "stair-flight-4", + "start": [ + -149.80823516845703, + -22.21286392211914, + 88.0768441772461 + ], + "end": [ + -149.80823516845703, + -19.629688262939453, + 95.05451812744143 + ], + "width": 9.645411987304687 + }, + { + "id": "stair-flight-25", + "start": [ + -227.34962463378906, + -8.681944847106934, + -265.64134887695315 + ], + "end": [ + -227.34962463378906, + -7.790135383605957, + -261.4959411621094 + ], + "width": 7.4620166015625 + }, + { + "id": "stair-flight-34", + "start": [ + -155.15909576416016, + -21.56707191467285, + -11.575290718078612 + ], + "end": [ + -155.15909576416016, + -19.998714447021484, + -7.70665693283081 + ], + "width": 2.7569232177734375 + }, + { + "id": "stair-flight-35", + "start": [ + -104.05681991577148, + -21.56707191467285, + -11.575290718078612 + ], + "end": [ + -104.05681991577148, + -19.998714447021484, + -7.706656932830811 + ], + "width": 2.7261843872070313 + }, + { + "id": "stair-flight-37", + "start": [ + -104.05681991577148, + -19.998714447021484, + 7.946159839630126 + ], + "end": [ + -104.05681991577148, + -21.56707191467285, + 11.81479076385498 + ], + "width": 2.7261843872070313 + }, + { + "id": "stair-flight-44", + "start": [ + -218.830580842825, + -8.374422073364258, + -260.7985398576039 + ], + "end": [ + -220.96018876745416, + -7.513363838195801, + -257.86738601245213 + ], + "width": 8.62199220615408 + }, + { + "id": "stair-flight-47", + "start": [ + -254.35765075683594, + 0.6051875352859497, + -37.222533264160155 + ], + "end": [ + -254.35765075683594, + 1.8352711200714111, + -33.72292137145996 + ], + "width": 9.676158447265625 + }, + { + "id": "stair-fallback-25", + "start": [ + -234.8113822275399, + -9.173978805541992, + -105.92120319734633 + ], + "end": [ + -234.75986142715348, + -7.882389068603516, + -102.96957852002285 + ], + "width": 6.700948744563664 + }, + { + "id": "stair-fallback-35", + "start": [ + -152.22227478027344, + -19.475929260253906, + 100.68520446777345 + ], + "end": [ + -152.22227478027344, + -18.215089797973633, + 104.36933517456056 + ], + "width": 4.817332763671875 + }, + { + "id": "stair-fallback-36", + "start": [ + -149.80823516845703, + -18.215089797973633, + 104.12944122314454 + ], + "end": [ + -149.80823516845703, + -16.98500633239746, + 107.67517852783205 + ], + "width": 9.645411987304687 + } + ], + "candidateCount": 107, + "rejectedCount": 97 + }, + "black-morass": { + "collision": [ + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-000.glb", + "checksum": "801e14701ff19bde501b0022b1270c85394ddb52eb6b6b87ab97cf90f5e5c2e0" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-001.glb", + "checksum": "2a22407412d36c90bfcf7ade663990cd19aa1f7caf2126a835862c2cea32174e" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-002.glb", + "checksum": "46ef0199698499c6ac8e5600efc85089294382b7c89113ababebe5e5538a0f34" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-003.glb", + "checksum": "4bf462ae69ae523a326227ed6a160d28813b2613c0096620120724435ba4d22c" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-004.glb", + "checksum": "c679a9d87eb09ca0b27195876ef4b8518b5f046886436255dc6dfcef8063908f" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-005.glb", + "checksum": "ed5da5f5289bd287303c35613a239c38b197463c45dfaa4d6cedc5c356d74c08" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-006.glb", + "checksum": "6e2bb234a7c5a318e23d2bc7afa08f5014f098e522077b4e32c2067dde6169a2" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-007.glb", + "checksum": "8f330d4323bcbfa6cb9f64e63862e38b66fb86603d1b5c47bd5a27597c771864" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-008.glb", + "checksum": "e0e2e71934bbc85b81ee447b648c3396e4bb2dd7a07d8418885687605b7493ef" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-009.glb", + "checksum": "cf2d7b30f374aea8aaa2fe05fcc0a046cde3792ebb183ef5b1e08a7b5cfb536f" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-010.glb", + "checksum": "2b6cce9e09ffc3f3985618bc9050edc978df62c2a6b1d40a2265e9aceaaeea59" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-011.glb", + "checksum": "c337e69d296d6b2a7553b11b7fc60d6f610bc8355bb59b61d92c55ae024810d8" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-012.glb", + "checksum": "75e11dc46d1474ecd2d0cd98f6b71d9d836608628a097dfb60ce8e14907dbf38" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-013.glb", + "checksum": "c7416c09d9fdbaaf2e957e1412a2ba3f1bbaa1163cf6a33cd2e46a711d88a80d" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-014.glb", + "checksum": "40d9029b10dd598720476413ce3e8e916830560ed83e08d954c2f36b3dabcbc3" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-015.glb", + "checksum": "268ec708dcc35462e05020ccb5299aced279559df232700e844bf615ead7ce17" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-016.glb", + "checksum": "943d1142ae1c628490121f2e186b656b16d265b208266229fbb57f09138ae9aa" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-017.glb", + "checksum": "14e9233e16cd7f17f4b8a342a5757d970d82829b2748ea652a874383a0856016" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-018.glb", + "checksum": "144b3fbe63c0916fcaf7299f243bbd4b0ec971fd7cfdfb85a4d704fef1f37de9" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-019.glb", + "checksum": "fa2394a906ec3616bd649b4abddff8e12f659d633c159df2c37a7bfb69b9e994" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-020.glb", + "checksum": "e28a442730d8409fc5f7963c522f899be3033b21c648e0a55ab56872cbff7014" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-021.glb", + "checksum": "50bc20dc3314250d55804acce46a723ae4a77d67d9b4bfa11f004e16f814651e" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-022.glb", + "checksum": "7bd1a647af6e7e5debe4f22ce64248a6fd562f7558e9a6da56f43a257a2a9f15" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-023.glb", + "checksum": "edafce3ace2f98d578e10f81b8bc02f101dc9937dc1d1587a0499ae329afae41" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-024.glb", + "checksum": "d7aa63f0bf75da4f8f954b70ab2a7bdecd83db1cc5ffd437a83e39f4831fca4a" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-025.glb", + "checksum": "1b50f20eaeee6ba603b9ac357f2090b754960a9c78bda5b199a94173047fd222" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-026.glb", + "checksum": "90f676c2d460ac82afb204be8ce282a5d76a383a4da50bce7d3cf7978e52a016" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-027.glb", + "checksum": "aff560fcd0be7f5204b2fd7a877038410603fee01c224ecc4aed541ac63eed6b" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-028.glb", + "checksum": "0b813454f8e7445332835f8bb68f59446e78358f9328e04b72dd430233d7c66d" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-029.glb", + "checksum": "50fd31624485b5d23520b0f9d56b7cdb897783ab50372520affeda57bf6a34ba" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-030.glb", + "checksum": "61a61c8f8a02537fafe05071646db824ee918225f74f071351b4098cce460780" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-031.glb", + "checksum": "8937203d422c27f21fc0728fe0e75ffb4112ae81e85647fa925ff72982f7ee1d" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-032.glb", + "checksum": "90265a543674dd0189ebcf8d5521fe627226960c21b346fdee5533f367803d14" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-033.glb", + "checksum": "e4e547d02b6b2596956a0424c99b4a288a99354a6aee416da83918113a91363b" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-034.glb", + "checksum": "0b78819783d2d11c1101d12d17c02bb7dfd05a46b0e8132a86893615b4326162" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-035.glb", + "checksum": "e18c47e53b3ef2901489e2f51f89fd9bfbb56a5f9dd1bb2786d994c5fcfe42ab" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-036.glb", + "checksum": "36b76c9f48c63eefb5b41a80ea6b3edcc6f2dd2f23b69c296d4c625e9eb9ce8e" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-037.glb", + "checksum": "a8803c5330fdc3231c92277c55c369073477e2346eeb5631a788fa043bdf5865" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-038.glb", + "checksum": "ae40683692853db8ac3a841fdeed942b1a7432ab46a04424e5ea6188d57d6fb4" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-039.glb", + "checksum": "a7bb1a3ce22dee55fcdf56ff1dc97e406d3507634dceea3c630bf3ffe9c58af7" + }, + { + "url": "/assets/game/manastorm/269-cavernsoftime/collision-040.glb", + "checksum": "4ae9455d9412d6ad65e079e64e649c1b6b8ab9dd70734ceda8e66e45333b2833" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/269-cavernsoftime/navigation.glb", + "checksum": "0f84858ce2becdd3640c625a4a8686b67f37935f6773498399b797ca7734dc8b", + "size": 1341420, + "triangleCount": 122696 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "escape-from-durnholde": { + "collision": [ + { + "url": "/assets/game/manastorm/560-hillsbradpast/collision-000.glb", + "checksum": "1126701e5a65a41983831e1a1561c75282e77fe60cf9484706f08776a9964ef4" + }, + { + "url": "/assets/game/manastorm/560-hillsbradpast/collision-001.glb", + "checksum": "29c67a9c1fc0d3995b6712b24758b99311c0dc93bdceb90450c575d0e665f1c2" + }, + { + "url": "/assets/game/manastorm/560-hillsbradpast/collision-002.glb", + "checksum": "bd834c286000959a23fd3d5fe81683a10a5c1de9985e2e56af8d4ba409ac9816" + }, + { + "url": "/assets/game/manastorm/560-hillsbradpast/collision-003.glb", + "checksum": "e03c45f7057dd273445b44fe56db2a6d884d949e42d1f3a0c5cd8a345338e4cb" + }, + { + "url": "/assets/game/manastorm/560-hillsbradpast/collision-004.glb", + "checksum": "8c79e9adde36f3dd8eee687fb52c0f8c1fd2a5e200afa0d3a832ce6ad105311c" + }, + { + "url": "/assets/game/manastorm/560-hillsbradpast/collision-005.glb", + "checksum": "54286b188bbb328feaad9700d37928b04edfded7b349a07c93f2b06949070007" + }, + { + "url": "/assets/game/manastorm/560-hillsbradpast/collision-006.glb", + "checksum": "44bcf1f4d09a7cb89c8661cf17e54cee58eb2e3b0985fca9fcc9ff40bf2a885f" + }, + { + "url": "/assets/game/manastorm/560-hillsbradpast/collision-007.glb", + "checksum": "c341dd602d1eba7b0bd0e1b52b4093cb898791e22e785ee3b5f4c4e3ac593e71" + }, + { + "url": "/assets/game/manastorm/560-hillsbradpast/collision-008.glb", + "checksum": "cafadbe8cefcd6aba2358cd084c7a9a63d7ec8b0bc85b2577ca4548f8b892789" + }, + { + "url": "/assets/game/manastorm/560-hillsbradpast/collision-009.glb", + "checksum": "388ab690649b35e2a54cbd970e58af4202304e0651574fafdef811994ab3297e" + }, + { + "url": "/assets/game/manastorm/560-hillsbradpast/collision-010.glb", + "checksum": "73c91175d1578989b67445fe898613aaf7045ad93b2edb1a73272aa0cb5a191e" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/560-hillsbradpast/navigation.glb", + "checksum": "9ed694e1702f3902ad27dccf60641a2ab8636d570d9baaca6b467bd6db218432", + "size": 11508184, + "triangleCount": 967781 + }, + "ramps": [ + { + "id": "stair-flight-2", + "start": [ + 114.80567296150537, + 89.4527359008789, + 2219.6409555727805 + ], + "end": [ + 109.18475603913623, + 82.66084289550781, + 2229.7813581291475 + ], + "width": 2.8900646861493624 + }, + { + "id": "stair-flight-3", + "start": [ + 999.1321801757813, + 4.884237766265869, + 1822.1172485351562 + ], + "end": [ + 1010.725064086914, + 11.675580024719238, + 1822.1172485351562 + ], + "width": 2.8494677734375 + }, + { + "id": "stair-flight-8", + "start": [ + -390.8291990675124, + 4.369935989379883, + 590.4183901239909 + ], + "end": [ + -398.43488141297655, + 0.4137204587459564, + 592.5992844420999 + ], + "width": 3.737249301036304 + }, + { + "id": "stair-flight-13", + "start": [ + -430.4859333069672, + 7.6978864669799805, + 600.9268824273265 + ], + "end": [ + -428.46723194869065, + 12.684113502502441, + 607.9669307019813 + ], + "width": 3.736192979078187 + }, + { + "id": "stair-flight-15", + "start": [ + 128.13827594373242, + 98.28018188476562, + 2243.2346094605173 + ], + "end": [ + 134.42949079726372, + 102.48487854003906, + 2246.721886798464 + ], + "width": 2.7704477242218855 + }, + { + "id": "stair-flight-17", + "start": [ + 2274.512103751117, + 69.8009262084961, + 3681.8969861113455 + ], + "end": [ + 2267.324408491658, + 74.00244140625, + 3682.1479859606784 + ], + "width": 2.7720221766244504 + }, + { + "id": "stair-flight-19", + "start": [ + 2313.3323232087705, + 74.00244140625, + 3680.541611923656 + ], + "end": [ + 2306.1450742255583, + 69.8009262084961, + 3680.7925961886785 + ], + "width": 2.77202217662536 + }, + { + "id": "stair-flight-21", + "start": [ + 1383.6608231540897, + 165.550537109375, + 3128.5259449726236 + ], + "end": [ + 1387.036939674323, + 161.34463500976562, + 3134.8754966623196 + ], + "width": 2.770569834628095 + }, + { + "id": "stair-flight-27", + "start": [ + 94.1687951536037, + 102.48487854003906, + 2224.404449085453 + ], + "end": [ + 100.4561677328553, + 98.28018188476562, + 2227.889596615988 + ], + "width": 2.7719658930216338 + }, + { + "id": "stair-flight-31", + "start": [ + 1401.8976452667464, + 161.34463500976562, + 3162.8229198335803 + ], + "end": [ + 1405.271582079346, + 165.550537109375, + 3169.1683720894425 + ], + "width": 2.7695567213393315 + }, + { + "id": "stair-flight-37", + "start": [ + -203.78741999913612, + 83.07231140136719, + 1007.3592043545034 + ], + "end": [ + -203.67002136267553, + 80.6257095336914, + 1014.084967732834 + ], + "width": 10.872130220857844 + }, + { + "id": "stair-flight-49", + "start": [ + 1337.5200214106678, + 172.98927307128906, + 3149.6762722803087 + ], + "end": [ + 1340.3822651528244, + 176.5464630126953, + 3155.0593698364546 + ], + "width": 2.896727427296146 + }, + { + "id": "stair-flight-50", + "start": [ + 2272.1741994222184, + 81.44281005859375, + 3684.7442205288116 + ], + "end": [ + 2266.0832279364254, + 84.99939727783203, + 3684.956921940048 + ], + "width": 2.8988243810095082 + }, + { + "id": "stair-flight-52", + "start": [ + 91.65332944490297, + 113.4801254272461, + 2226.170551286906 + ], + "end": [ + 96.98249941594793, + 109.9218978881836, + 2229.1245584385865 + ], + "width": 2.8996003598661173 + }, + { + "id": "stair-flight-53", + "start": [ + 2314.5485047767643, + 81.44281005859375, + 3629.7989571990665 + ], + "end": [ + 2308.4593366338695, + 84.99939727783203, + 3630.011595636181 + ], + "width": 2.8975691247810755 + }, + { + "id": "stair-flight-54", + "start": [ + 2314.7674734023467, + 84.99939727783203, + 3683.255547051767 + ], + "end": [ + 2308.678662896245, + 81.44281005859375, + 3683.4681729999293 + ], + "width": 2.8958351031801794 + }, + { + "id": "stair-flight-55", + "start": [ + 1359.0751916068712, + 176.5464630126953, + 3190.2154972529047 + ], + "end": [ + 1361.9352986455988, + 172.98927307128906, + 3195.5945762543633 + ], + "width": 2.8985096395131404 + }, + { + "id": "stair-flight-57", + "start": [ + 156.2760078352161, + 113.4801254272461, + 2200.8995268890117 + ], + "end": [ + 161.60398182046572, + 109.9218978881836, + 2203.8528710949404 + ], + "width": 2.8968935714663258 + }, + { + "id": "stair-flight-59", + "start": [ + 128.93384904427035, + 109.9218978881836, + 2246.8361322671244 + ], + "end": [ + 134.2607776207908, + 113.4801254272461, + 2249.788896993532 + ], + "width": 2.8968935714660984 + }, + { + "id": "stair-flight-61", + "start": [ + 1405.4821293316845, + 172.98927307128906, + 3163.6785955924033 + ], + "end": [ + 1408.341114651521, + 176.5464630126953, + 3169.0555649474563 + ], + "width": 2.9008334640873454 + }, + { + "id": "stair-flight-64", + "start": [ + 2421.983047637941, + 63.57988357543945, + 2539.471059320961 + ], + "end": [ + 2416.1398825734054, + 66.02464294433594, + 2540.188509777693 + ], + "width": 7.194879433297519 + }, + { + "id": "stair-flight-66", + "start": [ + 1086.9027490234375, + 10.908981323242188, + 1805.4789428710938 + ], + "end": [ + 1092.7830900969327, + 13.354680061340332, + 1805.4789428710938 + ], + "width": 7.1897998046875 + }, + { + "id": "stair-flight-67", + "start": [ + 1680.6065477573973, + 56.61432647705078, + 2165.9043705575314 + ], + "end": [ + 1686.4780096934894, + 59.06131362915039, + 2166.212080838747 + ], + "width": 7.226389660364948 + }, + { + "id": "stair-flight-71", + "start": [ + 1342.8012726203983, + 169.7564239501953, + 3144.7350200401006 + ], + "end": [ + 1337.9070282673051, + 172.98927307128906, + 3147.337335923496 + ], + "width": 2.7692208149051294 + }, + { + "id": "stair-flight-72", + "start": [ + 130.66859959390695, + 106.68956756591797, + 2239.813117988966 + ], + "end": [ + 127.98139982945543, + 109.9218978881836, + 2244.660954691991 + ], + "width": 2.769356884369263 + }, + { + "id": "stair-flight-73", + "start": [ + 2260.7410617407268, + 81.44281005859375, + 3633.119424426669 + ], + "end": [ + 2260.9344962535943, + 78.20869445800781, + 3638.6586641307963 + ], + "width": 2.7723522945762125 + }, + { + "id": "stair-flight-74", + "start": [ + 1387.945757144073, + 172.98927307128906, + 3133.764358860385 + ], + "end": [ + 1383.0530914974092, + 169.7564239501953, + 3136.3658353306823 + ], + "width": 2.770301732364278 + }, + { + "id": "stair-flight-75", + "start": [ + 1403.3246843971053, + 172.98927307128906, + 3162.6876166449365 + ], + "end": [ + 1398.4323102391184, + 169.7564239501953, + 3165.288938127955 + ], + "width": 2.7692208149033104 + }, + { + "id": "stair-flight-76", + "start": [ + 2273.814403746157, + 78.20869445800781, + 3677.6994187801315 + ], + "end": [ + 2274.0077733460853, + 81.44281005859375, + 3683.2367996208864 + ], + "width": 2.7715499063315474 + }, + { + "id": "stair-flight-77", + "start": [ + 2306.551502873561, + 78.20869445800781, + 3676.5570143343384 + ], + "end": [ + 2306.7448530754805, + 81.44281005859375, + 3682.0938396888187 + ], + "width": 2.7715019556835796 + }, + { + "id": "stair-flight-78", + "start": [ + 113.77272144933113, + 109.9218978881836, + 2178.9884819090157 + ], + "end": [ + 111.08678213537462, + 106.68956756591797, + 2183.8340446991547 + ], + "width": 2.7714875301733355 + }, + { + "id": "stair-flight-79", + "start": [ + 1368.9852895592935, + 169.7564239501953, + 3193.981756410062 + ], + "end": [ + 1364.093712083915, + 172.98927307128906, + 3196.5826542894238 + ], + "width": 2.7707922333979877 + }, + { + "id": "stair-flight-81", + "start": [ + 2316.482325766937, + 81.44281005859375, + 3631.172818767959 + ], + "end": [ + 2316.6756578523755, + 78.20869445800781, + 3636.709125334296 + ], + "width": 2.774647772104363 + }, + { + "id": "stair-flight-82", + "start": [ + 102.01788194808762, + 106.68956756591797, + 2223.931702102425 + ], + "end": [ + 99.33252322994304, + 109.9218978881836, + 2228.776217469992 + ], + "width": 2.770916861888072 + }, + { + "id": "stair-flight-83", + "start": [ + 162.55476630950125, + 109.9218978881836, + 2206.0320791404065 + ], + "end": [ + 159.86998005416365, + 106.68956756591797, + 2210.8755617577317 + ], + "width": 2.771379671227369 + }, + { + "id": "stair-flight-84", + "start": [ + -389.81161179110927, + 4.780231475830078, + 594.8454419956896 + ], + "end": [ + -395.09828496946716, + 7.572518348693848, + 596.3613711355986 + ], + "width": 3.302867567976382 + }, + { + "id": "stair-flight-96", + "start": [ + -223.08446352050498, + 80.48719024658203, + 3627.1316440492456 + ], + "end": [ + -224.61676101884007, + 81.39061737060547, + 3631.5817591117457 + ], + "width": 3.7786059607760945 + }, + { + "id": "stair-flight-97", + "start": [ + -233.1266456119672, + 80.48719024658203, + 3630.2031848121132 + ], + "end": [ + -229.36918590280038, + 81.39061737060547, + 3633.0346337940546 + ], + "width": 3.776753271112757 + }, + { + "id": "stair-flight-98", + "start": [ + -234.92430342691077, + 80.48719024658203, + 3635.423316062632 + ], + "end": [ + -230.22079127636908, + 81.39061737060547, + 3635.50541617261 + ], + "width": 3.778937387588958 + }, + { + "id": "stair-flight-99", + "start": [ + -228.60470520771207, + 80.48719024658203, + 3627.0366375728795 + ], + "end": [ + -227.22938460340492, + 81.39061737060547, + 3631.5351085727334 + ], + "width": 3.778255499493316 + }, + { + "id": "stair-flight-100", + "start": [ + -218.67594497466962, + 80.48719024658203, + 3630.455298259263 + ], + "end": [ + -222.52847589287103, + 81.39061737060547, + 3633.1528694493736 + ], + "width": 3.77699163912941 + }, + { + "id": "stair-flight-101", + "start": [ + -530.2727528775641, + 113.78401947021484, + 3037.6499616966034 + ], + "end": [ + -534.7764079935852, + 116.84309387207031, + 3038.6897124066927 + ], + "width": 4.251998551475044 + }, + { + "id": "stair-flight-102", + "start": [ + -481.9664755814032, + 116.83551025390625, + 3026.496556810882 + ], + "end": [ + -486.46938874290674, + 113.78401947021484, + 3027.536136227274 + ], + "width": 4.249629413202019 + }, + { + "id": "stair-flight-117", + "start": [ + 656.1207529629453, + 56.065460205078125, + 2628.774324245119 + ], + "end": [ + 659.7492331725855, + 54.431915283203125, + 2630.023710175573 + ], + "width": 4.0476430822241305 + }, + { + "id": "stair-flight-118", + "start": [ + 1631.9600671464134, + 49.792884826660156, + 2103.8049165361535 + ], + "end": [ + 1629.7352577913578, + 48.07563018798828, + 2106.8671039089472 + ], + "width": 2.2509339378734876 + }, + { + "id": "stair-flight-121", + "start": [ + -405.1821666363046, + 178.13038635253906, + 2921.122510917332 + ], + "end": [ + -402.38227032828445, + 179.23939514160156, + 2923.6435488766874 + ], + "width": 2.8351338130901422 + }, + { + "id": "stair-flight-122", + "start": [ + -439.01753592372665, + 178.13038635253906, + 2958.69318232704 + ], + "end": [ + -436.21853359349325, + 179.23939514160156, + 2961.2134153451816 + ], + "width": 2.8331380628265106 + }, + { + "id": "stair-flight-134", + "start": [ + -211.59535656343516, + 83.08850860595703, + 982.4076921690493 + ], + "end": [ + -214.8042875261863, + 84.31383514404297, + 982.4637042673543 + ], + "width": 4.652837368666628 + }, + { + "id": "stair-flight-135", + "start": [ + 653.3065359547196, + 166.03318786621094, + 3320.58887272726 + ], + "end": [ + 650.5753933708748, + 164.60186767578125, + 3322.165698633238 + ], + "width": 2.2307300610518905 + }, + { + "id": "stair-flight-136", + "start": [ + -228.24395226870024, + 81.99290466308594, + 3637.1541356065063 + ], + "end": [ + -230.7646519946311, + 81.08545684814453, + 3638.9191485561664 + ], + "width": 2.4618690414663615 + }, + { + "id": "stair-flight-137", + "start": [ + -226.88868483430883, + 81.99290466308594, + 3638.1774819627653 + ], + "end": [ + -227.8893041377048, + 81.08545684814453, + 3641.0834914281004 + ], + "width": 2.4631438865570954 + }, + { + "id": "stair-flight-138", + "start": [ + -223.2431335589831, + 81.99290466308594, + 3635.627146137029 + ], + "end": [ + -220.1719412711071, + 81.08545684814453, + 3635.680753997821 + ], + "width": 2.4656409754748165 + }, + { + "id": "stair-flight-139", + "start": [ + -225.1906679428306, + 81.99290466308594, + 3638.210045698083 + ], + "end": [ + -224.29270619462613, + 81.08545684814453, + 3641.1471462334966 + ], + "width": 2.4637729339385896 + }, + { + "id": "stair-flight-140", + "start": [ + -223.79714310189775, + 81.99290466308594, + 3637.234935081869 + ], + "end": [ + -221.34483882026507, + 81.08545684814453, + 3639.0828789053776 + ], + "width": 2.465296941219567 + }, + { + "id": "stair-flight-143", + "start": [ + -538.723115257879, + 120.45060729980469, + 3031.0132954278215 + ], + "end": [ + -537.4946757195767, + 116.84309387207031, + 3036.334251650995 + ], + "width": 4.249656873048043 + }, + { + "id": "stair-flight-155", + "start": [ + 676.2907384525002, + 164.60186767578125, + 3305.323554870054 + ], + "end": [ + 674.0930586992235, + 165.75198364257812, + 3306.592385867201 + ], + "width": 2.236377243559691 + }, + { + "id": "stair-flight-156", + "start": [ + 654.9326561359227, + 166.31756591796875, + 3322.2340687711785 + ], + "end": [ + 651.6778504153876, + 164.60186767578125, + 3324.113231730089 + ], + "width": 2.2203924619431774 + }, + { + "id": "stair-flight-157", + "start": [ + 2233.1844482421875, + 53.454490661621094, + 3646.373330078125 + ], + "end": [ + 2233.1844482421875, + 54.604034423828125, + 3648.908447265625 + ], + "width": 4.484599609375 + }, + { + "id": "stair-flight-163", + "start": [ + 653.2149879679267, + 165.3271942138672, + 3402.380322132393 + ], + "end": [ + 653.2574253772406, + 166.22633361816406, + 3404.8115596836883 + ], + "width": 3.1138337147415496 + }, + { + "id": "stair-flight-164", + "start": [ + 1290.2509086887458, + 151.98583984375, + 3281.857513561257 + ], + "end": [ + 1292.6710993766585, + 152.88497924804688, + 3281.9420284823955 + ], + "width": 3.0995400956071717 + }, + { + "id": "stair-flight-165", + "start": [ + -181.62511127355364, + 83.406982421875, + 961.3304934643795 + ], + "end": [ + -181.58288703552867, + 84.30843353271484, + 963.749518440718 + ], + "width": 4.6909842729861175 + }, + { + "id": "stair-flight-166", + "start": [ + 1707.1570029315037, + 58.15296173095703, + 2152.860355617273 + ], + "end": [ + 1709.570474993302, + 59.06131362915039, + 2152.986840328393 + ], + "width": 3.1023396446849074 + }, + { + "id": "stair-flight-167", + "start": [ + 1703.856634716913, + 58.15296173095703, + 2159.09297315326 + ], + "end": [ + 1703.7304155820925, + 59.06131362915039, + 2161.501377717377 + ], + "width": 3.0549714479950763 + }, + { + "id": "stair-flight-168", + "start": [ + 1109.704393814389, + 12.454663276672363, + 1797.285825776226 + ], + "end": [ + 1109.7463551284493, + 13.354680061340332, + 1799.689787848719 + ], + "width": 3.0604194192265095 + }, + { + "id": "stair-flight-169", + "start": [ + 2289.738581049819, + 74.89869689941406, + 3664.40847975772 + ], + "end": [ + 2289.8168083419146, + 76.19329071044922, + 3666.648616307807 + ], + "width": 6.182867304961309 + }, + { + "id": "stair-flight-170", + "start": [ + 1381.4814466929927, + 167.7412109375, + 3155.752024903935 + ], + "end": [ + 1379.5036249606128, + 166.44381713867188, + 3156.8036513731863 + ], + "width": 6.180677907471836 + }, + { + "id": "stair-flight-171", + "start": [ + 122.51427743295767, + 103.37710571289062, + 2220.742884250416 + ], + "end": [ + 121.4292412393421, + 104.67003631591797, + 2222.700341359896 + ], + "width": 6.184927084054625 + }, + { + "id": "stair-flight-177", + "start": [ + -207.0502619806908, + 101.54533386230469, + 966.418824437181 + ], + "end": [ + -207.01151259614258, + 100.41177368164062, + 968.6387751911641 + ], + "width": 5.873769772217845 + }, + { + "id": "stair-flight-183", + "start": [ + 351.455458785598, + 45.183349609375, + 1302.3848297434583 + ], + "end": [ + 349.8491357394987, + 43.79355239868164, + 1303.781185064081 + ], + "width": 4.2410604358836 + }, + { + "id": "stair-flight-184", + "start": [ + 347.92549840930377, + 38.79465103149414, + 1372.8360108474335 + ], + "end": [ + 349.3216899357081, + 40.17716979980469, + 1374.4421454698386 + ], + "width": 3.037193949216903 + }, + { + "id": "stair-flight-185", + "start": [ + 385.3229876482765, + 38.79465103149414, + 1340.8965208185464 + ], + "end": [ + 386.7191258680985, + 40.17716979980469, + 1342.5025941187434 + ], + "width": 3.0372652164465035 + }, + { + "id": "stair-flight-186", + "start": [ + -527.0684284172729, + 121.8397216796875, + 2985.9075966376927 + ], + "end": [ + -529.1392040294935, + 123.22882843017578, + 2986.385672857513 + ], + "width": 3.6528751403298156 + }, + { + "id": "stair-flight-187", + "start": [ + 383.3122914111828, + 37.40485382080078, + 1331.3853461726403 + ], + "end": [ + 384.67787208995657, + 38.79465103149414, + 1333.0127818525461 + ], + "width": 4.240606979343983 + }, + { + "id": "stair-flight-189", + "start": [ + 338.85426404385703, + 37.40485382080078, + 1369.3412722297903 + ], + "end": [ + 340.24753528109557, + 38.79465103149414, + 1370.944047443799 + ], + "width": 4.240534273470093 + }, + { + "id": "stair-flight-191", + "start": [ + -536.1356725608467, + 121.8397216796875, + 3021.6411340450145 + ], + "end": [ + -535.6580604926113, + 120.45060729980469, + 3023.7098991958433 + ], + "width": 3.046081637335892 + }, + { + "id": "stair-flight-193", + "start": [ + -488.2177976345737, + 121.8397216796875, + 3010.578730701293 + ], + "end": [ + -487.7402301597573, + 120.45060729980469, + 3012.647302696803 + ], + "width": 3.045147858882974 + }, + { + "id": "stair-flight-194", + "start": [ + -521.3275518993753, + 125.45291900634766, + 3073.0263064010514 + ], + "end": [ + -523.3943612669377, + 126.84203338623047, + 3073.503466941142 + ], + "width": 4.253463178579313 + }, + { + "id": "stair-flight-195", + "start": [ + 396.7742402250429, + 41.56696701049805, + 1377.00813380954 + ], + "end": [ + 395.1772964993168, + 40.17716979980469, + 1378.3963358113524 + ], + "width": 3.6408646266964024 + }, + { + "id": "stair-flight-204", + "start": [ + 341.5535293391379, + 44.885013580322266, + 1320.0630931495186 + ], + "end": [ + 342.8818488912016, + 43.706233978271484, + 1321.5911499969066 + ], + "width": 1.5666554768349852 + }, + { + "id": "stair-flight-206", + "start": [ + -496.7897966174054, + 125.36183166503906, + 3047.299142302652 + ], + "end": [ + -496.3365196112993, + 126.53839874267578, + 3049.262500718968 + ], + "width": 1.733739773479574 + }, + { + "id": "stair-flight-207", + "start": [ + -496.70614760332455, + 125.36942291259766, + 3058.0900878765005 + ], + "end": [ + -496.2531950229321, + 126.53839874267578, + 3060.0520410506647 + ], + "width": 1.5635485387472454 + }, + { + "id": "stair-flight-208", + "start": [ + -506.90662365843946, + 125.36942291259766, + 3060.4449212415584 + ], + "end": [ + -506.45367998145014, + 126.54598999023438, + 3062.4068358508475 + ], + "width": 1.569285010700487 + }, + { + "id": "stair-flight-209", + "start": [ + -523.3218364776241, + 135.0931854248047, + 3052.829666665547 + ], + "end": [ + -522.8689798091381, + 136.26976013183594, + 3054.791204399603 + ], + "width": 1.5466630338994674 + }, + { + "id": "stair-flight-211", + "start": [ + -494.19744102407003, + 125.36942291259766, + 3057.5117485451888 + ], + "end": [ + -493.7447806321335, + 126.53839874267578, + 3059.4724361121057 + ], + "width": 1.6001051656803793 + }, + { + "id": "stair-flight-212", + "start": [ + -512.4124099126757, + 135.08560180664062, + 3066.19271596654 + ], + "end": [ + -514.3720736592129, + 136.26217651367188, + 3066.6451399909174 + ], + "width": 1.6000914357587317 + }, + { + "id": "stair-flight-219", + "start": [ + 333.60471771205465, + 44.87773895263672, + 1326.8740095975052 + ], + "end": [ + 334.9062917281446, + 43.706233978271484, + 1328.3712992252747 + ], + "width": 1.551452147901632 + }, + { + "id": "stair-flight-220", + "start": [ + 327.0002772940418, + 53.42753601074219, + 1329.3998414916177 + ], + "end": [ + 325.50331837360466, + 54.599037170410156, + 1330.7011280282095 + ], + "width": 1.5931159592046424 + }, + { + "id": "stair-flight-227", + "start": [ + -201.16948699951172, + 101.3833999633789, + 966.9039159004177 + ], + "end": [ + -201.16948699951172, + 100.41177368164062, + 968.6218166366647 + ], + "width": 5.871333618164062 + }, + { + "id": "stair-flight-228", + "start": [ + -288.1414489746094, + 70.24749755859375, + 1137.7443237304688 + ], + "end": [ + -286.43042846679685, + 69.55296325683594, + 1137.7443237304688 + ], + "width": 1.1895556640625 + }, + { + "id": "stair-flight-229", + "start": [ + 668.7772164940196, + 164.95211791992188, + 3354.613535818824 + ], + "end": [ + 670.4844804211679, + 164.32479858398438, + 3354.643336221522 + ], + "width": 3.0013154758753626 + }, + { + "id": "stair-flight-230", + "start": [ + -326.9347558808617, + 82.19376373291016, + 972.6259530327194 + ], + "end": [ + -325.2276917837997, + 81.70648956298828, + 972.6557499473703 + ], + "width": 3.004360526057799 + }, + { + "id": "stair-flight-231", + "start": [ + 1271.5578739025707, + 154.36875915527344, + 3172.872008504715 + ], + "end": [ + 1272.437172835368, + 153.67422485351562, + 3174.335407677422 + ], + "width": 2.375786130141496 + }, + { + "id": "stair-flight-232", + "start": [ + 630.4822028164423, + 56.723880767822266, + 2619.9474457965143 + ], + "end": [ + 632.095406600993, + 56.02934646606445, + 2620.5029164053985 + ], + "width": 2.37604203358389 + }, + { + "id": "stair-flight-233", + "start": [ + 382.51187736048024, + 40.45367431640625, + 1389.198921620185 + ], + "end": [ + 381.2245981980627, + 41.56696701049805, + 1390.317936323942 + ], + "width": 3.6436075172191704 + }, + { + "id": "stair-flight-237", + "start": [ + -286.4237303047009, + 69.55296325683594, + 1138.9132769701819 + ], + "end": [ + -288.1256674290075, + 70.24749755859375, + 1138.9429843931905 + ], + "width": 1.189705926343113 + }, + { + "id": "stair-flight-239", + "start": [ + 2514.27218913866, + 67.17261505126953, + 2466.388102390576 + ], + "end": [ + 2514.852553519895, + 66.68590545654297, + 2467.982640423076 + ], + "width": 3.013484315033129 + }, + { + "id": "stair-flight-240", + "start": [ + 1103.6079005817492, + 18.839223861694336, + 1893.7159187610555 + ], + "end": [ + 1104.9805594690674, + 19.32731056213379, + 1894.7132138191364 + ], + "width": 3.010733689081253 + }, + { + "id": "stair-flight-241", + "start": [ + -60.10855054820502, + 68.69689178466797, + 1120.4470632748596 + ], + "end": [ + -58.790261059900104, + 69.1839828491211, + 1121.5145930536178 + ], + "width": 3.00443027511496 + }, + { + "id": "stair-flight-242", + "start": [ + 2204.081787109375, + 55.32929611206055, + 3620.71630859375 + ], + "end": [ + 2205.77779296875, + 54.84212112426758, + 3620.71630859375 + ], + "width": 3.014140625 + }, + { + "id": "stair-flight-243", + "start": [ + 2395.3555078125, + 64.93224334716797, + 2700.5263671875 + ], + "end": [ + 2397.051513671875, + 65.41941833496094, + 2700.5263671875 + ], + "width": 3.014140625 + }, + { + "id": "stair-flight-244", + "start": [ + 1025.2678833007812, + 11.686504364013672, + 1811.3285522460938 + ], + "end": [ + 1026.9629736328125, + 11.063813209533691, + 1811.3285522460938 + ], + "width": 3.0132861328125 + }, + { + "id": "stair-flight-245", + "start": [ + 1035.328408203125, + 11.063813209533691, + 1811.3285522460938 + ], + "end": [ + 1037.0234985351562, + 11.690146446228027, + 1811.3285522460938 + ], + "width": 3.0132861328125 + }, + { + "id": "stair-flight-246", + "start": [ + 1967.857790638867, + 43.419715881347656, + 2759.2672090834794 + ], + "end": [ + 1966.598166690445, + 43.906044006347656, + 2760.4013795809333 + ], + "width": 3.011524368053433 + }, + { + "id": "stair-flight-247", + "start": [ + 658.2526900547863, + 57.152034759521484, + 2664.3548292831756 + ], + "end": [ + 657.5098409381961, + 57.63869094848633, + 2665.8778956806345 + ], + "width": 3.001336537264724 + }, + { + "id": "stair-flight-248", + "start": [ + 2377.9188274196804, + 65.23196411132812, + 2502.1762684777655 + ], + "end": [ + 2379.594837038626, + 64.74539184570312, + 2502.411816268582 + ], + "width": 3.0115232236985503 + }, + { + "id": "stair-flight-250", + "start": [ + 1601.7113494758983, + 55.50098419189453, + 2329.367209618662 + ], + "end": [ + 1600.6517555662062, + 55.98740005493164, + 2330.6756991248226 + ], + "width": 3.0044614837829795 + }, + { + "id": "stair-flight-251", + "start": [ + 678.8487087591498, + 164.32479858398438, + 3354.8601212534213 + ], + "end": [ + 680.5309656369957, + 164.94847106933594, + 3354.9188669580785 + ], + "width": 3.0020274786225127 + }, + { + "id": "stair-flight-252", + "start": [ + 603.4143431099777, + 165.57391357421875, + 3351.654377282604 + ], + "end": [ + 601.9565902084797, + 166.06236267089844, + 3352.496011312696 + ], + "width": 3.0035648153812144 + }, + { + "id": "stair-flight-253", + "start": [ + -320.33020134014896, + 69.42839813232422, + 1021.947574817673 + ], + "end": [ + -320.30083279233, + 68.94097900390625, + 1023.6300977953719 + ], + "width": 3.0031585648976784 + }, + { + "id": "stair-flight-254", + "start": [ + 1816.2765624717738, + 41.702945709228516, + 2859.89113664081 + ], + "end": [ + 1815.195075408977, + 42.190589904785156, + 2861.1800027332424 + ], + "width": 3.0040787943530813 + }, + { + "id": "stair-flight-262", + "start": [ + -476.9724687093974, + 126.28031158447266, + 3062.7863540273324 + ], + "end": [ + -478.2330449265676, + 125.45291900634766, + 3063.0773809783664 + ], + "width": 4.25299710683932 + }, + { + "id": "stair-flight-263", + "start": [ + 316.2220840357115, + 43.79355239868164, + 1332.4887090565715 + ], + "end": [ + 315.24574528075726, + 44.62306594848633, + 1333.3374273878692 + ], + "width": 4.251769452067965 + }, + { + "id": "stair-flight-271", + "start": [ + 381.9406034234937, + 35.176448822021484, + 1328.170298375279 + ], + "end": [ + 382.7755271822632, + 36.01323699951172, + 1329.1307682898055 + ], + "width": 2.1338007047627525 + }, + { + "id": "stair-fallback-30", + "start": [ + 681.5607775766925, + 170.117431640625, + 3358.3143609388876 + ], + "end": [ + 684.9251345068719, + 168.1771240234375, + 3358.4318468717347 + ], + "width": 4.010955035058959 + }, + { + "id": "stair-fallback-109", + "start": [ + -481.7827667323752, + 120.45060729980469, + 3017.868417344404 + ], + "end": [ + -481.117505256987, + 118.50736999511719, + 3020.7499813751388 + ], + "width": 4.2518041334954155 + }, + { + "id": "stair-fallback-110", + "start": [ + -481.87678176638417, + 117.6723861694336, + 3022.166926599465 + ], + "end": [ + -481.5872106424488, + 116.83551025390625, + 3023.4211969366816 + ], + "width": 2.1319078452215945 + }, + { + "id": "stair-fallback-122", + "start": [ + -487.6114164259377, + 112.6170883178711, + 2727.361259534588 + ], + "end": [ + -487.5325124986229, + 113.7287368774414, + 2729.6207723821913 + ], + "width": 3.5894770390407484 + }, + { + "id": "stair-fallback-123", + "start": [ + -487.5913900106856, + 113.03395080566406, + 2727.915898436433 + ], + "end": [ + -487.49310853979273, + 114.28254699707031, + 2730.730311529938 + ], + "width": 2.4803734833713134 + }, + { + "id": "stair-fallback-222", + "start": [ + 337.7069743726235, + 34.0704345703125, + 1362.1079896275273 + ], + "end": [ + 336.43392052874646, + 35.176448822021484, + 1363.2146384505356 + ], + "width": 4.2472016902982155 + }, + { + "id": "stair-fallback-225", + "start": [ + 1395.5961931802804, + 145.73074340820312, + 3156.4612830174633 + ], + "end": [ + 1388.2741927693517, + 150.5813446044922, + 3160.3544596945835 + ], + "width": 2.8463833960400007 + } + ], + "candidateCount": 533, + "rejectedCount": 413 + }, + "utgarde-keep": { + "collision": [ + { + "url": "/assets/game/manastorm/574-valgarde70/collision-000.glb", + "checksum": "9cc8d92f03bfe55c125741c66f61e0681b7890b481e7bbffc0376e30a0f7b2b0" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-001.glb", + "checksum": "207f0cfdad105a5e0f59636e217fb7154bdee9e831a6636f80217bc0792b3b74" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-002.glb", + "checksum": "1c1b1cb36e509d8c7ae03ff8b587d07c46caacf53fc7dcdec9b8c86947ea627d" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-003.glb", + "checksum": "ce4d3f839a67412c6f8230718482c40cfa8af0adbde5fb568aa4f94d379daf3b" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-004.glb", + "checksum": "fe70a10b0ba2f5f703bbe01b332bce0034eaadfc23f51c85e20df225a4eaa1e8" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-005.glb", + "checksum": "a3f40c09f7d56398c81328b03cf985faae8fa28993e5f925c84da5b1391cdcdc" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-006.glb", + "checksum": "d8d945ad209a4357637d98f4e9005f434413d130458640f3e6a4636f897383e7" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-007.glb", + "checksum": "98cd26ec8b2062c3682f5faff0f064efcbcac0fab314c715c365cadfaaae6acb" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-008.glb", + "checksum": "8a345f470817e81822aa87a8ad53ec780c315132975cb6160ccd54ae940bed0d" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-009.glb", + "checksum": "482d8241e58dac3e7a02d40adac6a1ffd68c6e9e622ca88790d19ddb26a51810" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-010.glb", + "checksum": "9c6a68d5535211684ed18fce022715320dcf348c38bd6479547303bd183d0cd9" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-011.glb", + "checksum": "0bef57917a2bfa0dcef034110b2500f45c991df64372166c627061f0df5fd82e" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-012.glb", + "checksum": "632b90e5e1e78c3cbc0f3920bc19d7551d5a5e155e62ab5ae646f9101e68c05d" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-013.glb", + "checksum": "049edba3281c6422fa5e9acf6a52c7183931bfca79aa845f2dd0de703786b129" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-014.glb", + "checksum": "e63aabff9b1180f82966bc83fd23389b0ce08a90995d139d93f9ac5541675b45" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-015.glb", + "checksum": "7d08930480a264a200869a3f433f358346b6bdf10312b5fdf3844e929f241d10" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-016.glb", + "checksum": "64f49b4f9095074f4fd097108ce39ee441831514cc587ec7733a5794db87ff6c" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-017.glb", + "checksum": "1414291e418efbf553711c52fef9cc17a96fc4281d6efa8de01323ff266f43a3" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-018.glb", + "checksum": "e0394910059f029474797ea1da0ee10f34d8a8a8ea62902a7574856bbb2d78cb" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-019.glb", + "checksum": "faef3bd55e854c73283ae17872b2e09053e537ce6d7d76a7ffdf32f408d27b42" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-020.glb", + "checksum": "5067c9895075a4ca7efb12d5b0f5db173070ce7099269cf605dc7c9dfaf23b0c" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-021.glb", + "checksum": "954c479b1fe7008bb4a5cf5f7efb05834fb2f594f9f4b5dc8a81ce701023add2" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-022.glb", + "checksum": "576cb42449b0a03e8fe0e4f390d3b0da3c579e92e26a186ab0642e77daac425b" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-023.glb", + "checksum": "e00dfb494a82633a47a167400aa29ea5978c2efa0e1814855cf7a5bd69d91b14" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-024.glb", + "checksum": "172376a1ad604a10006ab550128d072c0379a19eeab933db9c6f3b4335ecc2e1" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-025.glb", + "checksum": "c891b194ed61cad9e451af8bbbf78d6fca7d8a639e6c21896f376754df481523" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-026.glb", + "checksum": "de7110d091fca22bded6afdb20bd72a77f37af97c2c8f92fd965e592d5181229" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-027.glb", + "checksum": "85e720ccdf6ccb03f78f66273ebff357ccc37d976dcd1f19f645f3735aa08826" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-028.glb", + "checksum": "ae718a831f1375c3a36b47f822bcea4ddc19615c1b9f0a261350026fdd88e8b4" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-029.glb", + "checksum": "4f262d3bc181b0ad9b4923828a1e33b4be0f8a4a300211f68209d4069749ca7e" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-030.glb", + "checksum": "7cb4e4112d88e6dbed663fc60f127f03ee79e0979f05976bbe21decbf76fd39d" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-031.glb", + "checksum": "e2a009d212351d176c98780537c36093c83b88b0b8cbc0f04bb115603f19109a" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-032.glb", + "checksum": "f6c7ff4aa741f2a1e55e6e810d13e37f9a7cf2d8fd28e99e269b186f61a0f9a4" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-033.glb", + "checksum": "3b6cda1718f195345bf8863d3efec82a240a05d62db843ffd47dfd91b73ceee7" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-034.glb", + "checksum": "88a6669b09ffbc32e282d7ebf53c8105198a67eb97e59c4a00ecb0f1a4cd23c3" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-035.glb", + "checksum": "c7c96b111ea6bae10715b21d040fd5c3a50aa78819ef2f6952377b59ea291221" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-036.glb", + "checksum": "1ecbe6765ac61ed97eb17be3a5d95ccb28afda76469623340eeae6e005f7514b" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-037.glb", + "checksum": "a1c802c8f7a07bd1b0f32f1ffda5eb5cb4234e50ec55aefd3febe8116aa3ad61" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-038.glb", + "checksum": "05a0a1c4f7ae6029c37375858885481c0e1d1242a0e6f237eb5d5258731d3619" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-039.glb", + "checksum": "f0255ce1aab088dbd3fb5fb37b15ac4b6bdef95e789a9531c807ae130d003e59" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-040.glb", + "checksum": "634e8f2d0793dc260ba94fdacdfa47558372582a5d0c10e5bba5a607fe0d2b11" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-041.glb", + "checksum": "3b9d05240f226717686692a0cf16ae323742993c83ebadcfd67650a3f48aca06" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-042.glb", + "checksum": "47e11e2ef7c13f3be7f1f88bcacbd9ebd21ce945eab5e607e8b6730832b878af" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-043.glb", + "checksum": "9a3ddcd66a53f97d94848f8e46ebbf33fa39aac479ef2f559d198d28adaf5b7a" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-044.glb", + "checksum": "1dd767c8dfd32696abf7aad13f551676adc52e76718acf9125ae08871ab8566f" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-045.glb", + "checksum": "7860590d3f9c51ac92f437f7af24a0469cb25c8c2a26374b8955824f38390c93" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-046.glb", + "checksum": "8591b7f6f1fcac7240c06673ffc74c111a4e5901fd1a360db42cd7a42613829e" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-047.glb", + "checksum": "1224c88dbb93271edad140fc4a1d336d0f5595b4dd325d24f87993bbe4f7b9e8" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-048.glb", + "checksum": "f097c8069d6abebf16f8f1152285331da5292b7770b394f2f24b5ca6dfd5b0e0" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-049.glb", + "checksum": "779028836e5f50a540b422f4d06fcb2563eff2a494ab07bfd650ca916fd00ee3" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-050.glb", + "checksum": "c15877275204f059aec8aac2f37c6a5eac2e05fb0c2f3a929060914aacd26401" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-051.glb", + "checksum": "593afbacac1319477327a4838203b9428818ce3d4a939cedb062c23296667191" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-052.glb", + "checksum": "c57299f3a28009ab2e3ff1d13d68a1636eceeec8dc4602df9d9ca425672d9d08" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-053.glb", + "checksum": "c4657f3f3ef184d0fb561c3343a7745d58f4f8dcfe944724e67570bc68f1a438" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-054.glb", + "checksum": "ed5709f068a259245feb6f07394e4c391a7c4bd51a94a533d1ed57ac6377fcc1" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-055.glb", + "checksum": "f36c2b8015cca9efad3ccfeebbdba9b41db3a508dc4d5b76b399c3ffa26705ee" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-056.glb", + "checksum": "e6ca0a3cd44b7252f32fc063ecf244c89f7d0aadd30eba08e978651cbb6d46e5" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-057.glb", + "checksum": "07fa1ffea8ed8cbc97d3449b27975a31433ec7e9d37aa2a0bfff99a8a5809675" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-058.glb", + "checksum": "50c1d297fa2bf6b779222d14723ddbb269c768ca513561f20348b01a9dc8aa29" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-059.glb", + "checksum": "a6167c7a94695427cbc19fe49a7f18b5ec31ba52e42557dcdd3009114de24213" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-060.glb", + "checksum": "70bf98c545fbdbb8cb02f299ef4238d7d45dc22d226548f01e0fad4d7bdbd843" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-061.glb", + "checksum": "052436d06a8ce58dc7ef3853cb499ea5ad5fbe5b28d4731b0913c77d7799bc50" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-062.glb", + "checksum": "6b05c3aa7ea80e5f7ca89aaf4e81999532b044d1681a2db016da2697ba358f58" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-063.glb", + "checksum": "a33dc7fcadfc82dcb471a8022dba4c0d377dec0c15c351220b4e6ee91c52e350" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-064.glb", + "checksum": "3a6cde6fba385b0de938b41b9a0de15dd86385cab8a9f77c79457df508fd0251" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-065.glb", + "checksum": "c1b3dcd9917887c7da183dc71ebeec7c6d755b1690ae21e87b87f9df2e2298ea" + }, + { + "url": "/assets/game/manastorm/574-valgarde70/collision-066.glb", + "checksum": "2dca6d74a097b7835fc90f0c87df92588d45832a325610298b813c02f419fda7" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/574-valgarde70/navigation.glb", + "checksum": "0d0a8c1f5b17f2ed95049bdcf0dfc425cd90135c023919fa42bdc3cf1e001662", + "size": 5011536, + "triangleCount": 434783 + }, + "ramps": [ + { + "id": "stair-flight-9", + "start": [ + 2000.887896405447, + 28.77354621887207, + 1112.9887979762354 + ], + "end": [ + 1996.5632883829871, + 27.202600479125977, + 1114.0670438575548 + ], + "width": 3.4863896098250007 + }, + { + "id": "stair-flight-10", + "start": [ + -132.44315530768563, + 28.7735538482666, + -487.0082673711928 + ], + "end": [ + -136.76772185483264, + 27.202611923217773, + -485.9300318308302 + ], + "width": 3.4863785355195387 + }, + { + "id": "stair-flight-13", + "start": [ + -145.33359229561756, + 27.157150268554688, + -479.29621994576775 + ], + "end": [ + -147.8853640404938, + 25.586206436157227, + -478.6599917929061 + ], + "width": 6.571893954360593 + }, + { + "id": "stair-flight-14", + "start": [ + 1987.9974111438814, + 27.157140731811523, + 1120.700762080451 + ], + "end": [ + 1985.445664471492, + 25.58619499206543, + 1121.3369839820398 + ], + "width": 6.571890262925553 + }, + { + "id": "stair-flight-15", + "start": [ + -175.5138378979427, + 10.110588073730469, + -401.19769138355076 + ], + "end": [ + -174.5448721680947, + 11.085017204284668, + -399.64702206861756 + ], + "width": 3.565317961201431 + }, + { + "id": "stair-flight-16", + "start": [ + 1957.8194576536719, + 10.110599517822266, + 1198.7998847706708 + ], + "end": [ + 1958.7883822399933, + 11.085027694702148, + 1200.350488242198 + ], + "width": 3.5707755074128227 + }, + { + "id": "stair-flight-17", + "start": [ + -172.99440839682123, + 10.110588073730469, + -402.7921559192155 + ], + "end": [ + -172.00463296774504, + 11.061698913574219, + -401.2680354139365 + ], + "width": 2.43927513944334 + }, + { + "id": "stair-flight-18", + "start": [ + 1960.3365364344252, + 10.110599517822266, + 1197.206882971139 + ], + "end": [ + 1961.326263255349, + 11.0617094039917, + 1198.730928626427 + ], + "width": 2.439227605742435 + } + ], + "candidateCount": 32, + "rejectedCount": 24 + }, + "utgarde-pinnacle": { + "collision": [ + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-000.glb", + "checksum": "66aef2779188c849dec4d473109684a155f3a6f69e722ae042f63b7c21af61d0" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-001.glb", + "checksum": "e69d8a363a52740867d11569e60f8b6dfa3aa3cf6e4e871cd48ca348b9479b6c" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-002.glb", + "checksum": "934146c8e49d6f639579481c284f7b5dc54ed7995af18bf26b0432b1cf225112" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-003.glb", + "checksum": "71891e04a463c38ff7d2318c8a2d554b07a45456a37ef5fd2897c4a966760585" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-004.glb", + "checksum": "bf61579e1ededdc3905484fee669b2d90fc417afb8cf5b13eef3fcaa3ee0883f" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-005.glb", + "checksum": "53d04607b06ef4a78730d2bbe34ba26e0a8980447579c165e8e6cae8b4ef326c" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-006.glb", + "checksum": "f591b81dfbce86c62c4086cdb5e59b24f953549d45ec73df70976a228c0886d0" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-007.glb", + "checksum": "5eb8549139c333c17ae9ae9a8c9eec4ba393818fd985b4f5077f03abd9b1e96b" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-008.glb", + "checksum": "9f6f68b736d5e62f67e30aaa1cca4d1f1d147642f8319c70b2aefe1cb7644634" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-009.glb", + "checksum": "e523831ee8176ac4de97d24e0f8ca3b14d3503a924a4d8c1917dd4a82b27d58e" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-010.glb", + "checksum": "3fc90ba89ac6d34eee31b286a013f6cf4e36f18f997594fa51fb124e6233a3f2" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-011.glb", + "checksum": "ff702e024223d933f7a1dce1d15904e41eebb52d626d9c9665fe7a79811f2756" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-012.glb", + "checksum": "6423565ab9f34575e0dbddf21f99ccd326c8e56cacaccb1d987824f132b58b6e" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-013.glb", + "checksum": "912d1ca69dde885558d466450fdc9757a3b1197e02b3dfe4286e1e6151342c88" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-014.glb", + "checksum": "5e68995b5e0cbfc7d9b52d3ea48f38b52cb199c81587d6eac657ac15af7ae674" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-015.glb", + "checksum": "89bc9b5c55d626ba506f22e9d35bcda5ece5efc82007c8ea7335dbfccf41507b" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-016.glb", + "checksum": "9a92770e899247935fe7ff97984b349d937572bcc382ff4352dc9d3b3495e0b2" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-017.glb", + "checksum": "ba297d1e6ed440a56fd0c28860ee3191e668e10ae47422f68eb6eb5349aa4d3c" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-018.glb", + "checksum": "feac4edb76820d9527a41ad0baa861d513edc8592177361a50de5d7c5de0db93" + }, + { + "url": "/assets/game/manastorm/575-utgardepinnacle/collision-019.glb", + "checksum": "1ff2a53865168480ae042e37f670b81bbe596e4b92019dd5d88af6ecead95d95" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/575-utgardepinnacle/navigation.glb", + "checksum": "5cfd9cf7030972f813d362a9d7375df13f07ee9762002a9db3592f5caf17f050", + "size": 9771420, + "triangleCount": 842326 + }, + "ramps": [ + { + "id": "stair-flight-6", + "start": [ + 1171.647863804029, + -179.92788696289062, + 283.84307170269534 + ], + "end": [ + 1164.5646277604526, + -174.8220672607422, + 292.2845437048202 + ], + "width": 1.6866859091283686 + }, + { + "id": "stair-flight-9", + "start": [ + 895.9189913219125, + -346.5899658203125, + -628.3119969274394 + ], + "end": [ + 893.866252558258, + -342.8628845214844, + -621.1532461136389 + ], + "width": 3.683319691429365 + }, + { + "id": "stair-flight-15", + "start": [ + -227.04161238877464, + -100.1551513671875, + -708.772980595613 + ], + "end": [ + -232.67625881204157, + -99.64861297607422, + -706.145501817448 + ], + "width": 1.4056391397995367 + }, + { + "id": "stair-flight-16", + "start": [ + -215.3931915030752, + -99.64861297607422, + -717.8033725972326 + ], + "end": [ + -219.93999623303017, + -100.1551513671875, + -713.5634085928275 + ], + "width": 1.4056078698804595 + }, + { + "id": "stair-flight-17", + "start": [ + -226.28256520740837, + -100.1551513671875, + -707.6492184616325 + ], + "end": [ + -230.82920198955853, + -99.64861297607422, + -703.4094110710892 + ], + "width": 1.4056078698806869 + }, + { + "id": "stair-flight-18", + "start": [ + -227.57167644800654, + -99.64861297607422, + -720.1702852237635 + ], + "end": [ + -224.94456587040196, + -100.1551513671875, + -714.5364284091468 + ], + "width": 1.4071964287680783 + }, + { + "id": "stair-flight-19", + "start": [ + -221.2781056383871, + -100.1551513671875, + -706.6760960049158 + ], + "end": [ + -218.65114701661997, + -99.64861297607422, + -701.0425650606439 + ], + "width": 1.407196428767993 + }, + { + "id": "stair-flight-20", + "start": [ + -213.5479079614957, + -99.64861297607422, + -715.0663253432115 + ], + "end": [ + -219.18052326173768, + -100.1551513671875, + -712.4397936932677 + ], + "width": 1.405639139799423 + }, + { + "id": "stair-flight-21", + "start": [ + -230.30825141286343, + -99.64861297607422, + -718.324081134822 + ], + "end": [ + -226.07009194243685, + -100.1551513671875, + -713.7792115306418 + ], + "width": 1.4056078698805732 + }, + { + "id": "stair-flight-22", + "start": [ + -220.15257311982344, + -100.1551513671875, + -707.4333141518889 + ], + "end": [ + -215.91462488443494, + -99.64861297607422, + -702.888671069554 + ], + "width": 1.4056190294524709 + }, + { + "id": "stair-flight-26", + "start": [ + 891.0877156334138, + -339.66461181640625, + -627.9566843796972 + ], + "end": [ + 889.5719082707753, + -342.45458984375, + -622.6704358891513 + ], + "width": 3.261197497261055 + }, + { + "id": "stair-flight-32", + "start": [ + 1169.6693034071843, + -170.50961303710938, + 265.5205905658676 + ], + "end": [ + 1173.0835525828984, + -168.9414520263672, + 268.38548578995636 + ], + "width": 3.4883197857289088 + }, + { + "id": "stair-flight-33", + "start": [ + -632.4431357154136, + -348.12506103515625, + -587.0101624437168 + ], + "end": [ + -636.7677118311465, + -349.69598388671875, + -585.9319245176379 + ], + "width": 3.4863748440843847 + }, + { + "id": "stair-flight-41", + "start": [ + -645.3335873356602, + -349.741455078125, + -579.2981380831808 + ], + "end": [ + -647.8853483351853, + -351.3124084472656, + -578.6619126094361 + ], + "width": 6.571890262925326 + }, + { + "id": "stair-flight-42", + "start": [ + 1157.2541018803054, + -172.1314239501953, + 260.79816172259643 + ], + "end": [ + 1159.2627815482751, + -170.5137481689453, + 262.4836440911432 + ], + "width": 6.569490619075527 + }, + { + "id": "stair-flight-45", + "start": [ + -757.2365740280056, + -93.38816833496094, + 1328.0234533598518 + ], + "end": [ + -757.4379477729656, + -92.13705444335938, + 1330.3251657971464 + ], + "width": 2.43395769407427 + }, + { + "id": "stair-flight-46", + "start": [ + -759.6397814174622, + -93.38816833496094, + 1327.7877751328697 + ], + "end": [ + -759.8809935682474, + -92.13705444335938, + 1330.0827554462264 + ], + "width": 2.4381247883036576 + }, + { + "id": "stair-flight-49", + "start": [ + 997.4984234917309, + -341.3945617675781, + -1197.2349768066172 + ], + "end": [ + 995.3417150946626, + -342.5594482421875, + -1196.4499991460925 + ], + "width": 1.0542205426748024 + }, + { + "id": "stair-flight-56", + "start": [ + -209.10100638786224, + -100.17450714111328, + -720.1465001527613 + ], + "end": [ + -211.0391637696707, + -99.39695739746094, + -718.9354050056924 + ], + "width": 1.2651131949172805 + }, + { + "id": "stair-flight-57", + "start": [ + -675.5133392413293, + -367.5, + -501.19920689771277 + ], + "end": [ + -674.5443512271963, + -366.52557373046875, + -499.648501920469 + ], + "width": 3.5710632019166906 + }, + { + "id": "stair-flight-58", + "start": [ + -843.5264473489532, + -92.54240417480469, + 1311.505450383126 + ], + "end": [ + -842.4313884014523, + -91.67697143554688, + 1312.958642688776 + ], + "width": 2.449498591523952 + }, + { + "id": "stair-flight-59", + "start": [ + -845.9195896066124, + -92.54240417480469, + 1313.3092183545423 + ], + "end": [ + -844.8249275530467, + -91.65303802490234, + 1314.7618839641511 + ], + "width": 3.583068913369989 + }, + { + "id": "stair-flight-60", + "start": [ + -672.9963463341261, + -367.5, + -502.79215256998856 + ], + "end": [ + -672.0065477325336, + -366.54888916015625, + -501.26799638216363 + ], + "width": 2.439245777760558 + }, + { + "id": "stair-flight-81", + "start": [ + -89.7011375729179, + -92.82701110839844, + -705.5694912869121 + ], + "end": [ + -90.89263531498781, + -92.0538101196289, + -704.9623928643124 + ], + "width": 1.0250365235933896 + }, + { + "id": "stair-flight-86", + "start": [ + -109.84467046719189, + -93.9341049194336, + -692.8067721917433 + ], + "end": [ + -110.9632445221813, + -94.72137451171875, + -692.0803617068796 + ], + "width": 1.058606244984976 + }, + { + "id": "stair-flight-95", + "start": [ + -109.56201022212801, + -94.24339294433594, + -691.4991258843354 + ], + "end": [ + -110.71270415502397, + -95.01190948486328, + -690.834772432423 + ], + "width": 1.3622775565974234 + }, + { + "id": "stair-flight-99", + "start": [ + -238.76473097037456, + -95.89556121826172, + -697.5326170978851 + ], + "end": [ + -238.07330220618957, + -95.18253326416016, + -696.615060136854 + ], + "width": 1.0556882700398842 + }, + { + "id": "stair-flight-100", + "start": [ + -235.731688839419, + -98.603271484375, + -701.0933131142044 + ], + "end": [ + -236.64924005309155, + -97.89025115966797, + -700.4018886809647 + ], + "width": 1.0556547146498088 + }, + { + "id": "stair-fallback-41", + "start": [ + -108.50955435631352, + -92.0538101196289, + -695.2556439508085 + ], + "end": [ + -109.55316148087036, + -92.88324737548828, + -694.4105475644138 + ], + "width": 1.4602296079508688 + }, + { + "id": "stair-fallback-43", + "start": [ + -107.70157983763977, + -92.0538101196289, + -694.2346067500507 + ], + "end": [ + -108.7631257084206, + -92.82701110839844, + -693.434674559754 + ], + "width": 1.0321620930984818 + }, + { + "id": "stair-fallback-59", + "start": [ + -208.5887622369745, + -99.96156311035156, + -718.5676000751507 + ], + "end": [ + -209.72377798400868, + -99.40985870361328, + -717.7728534930907 + ], + "width": 1.920564468919356 + }, + { + "id": "stair-fallback-62", + "start": [ + -110.95537672559885, + -94.29025268554688, + -693.6993666250229 + ], + "end": [ + -112.07114589600346, + -95.02597045898438, + -692.9467708167548 + ], + "width": 1.6162206522602856 + } + ], + "candidateCount": 210, + "rejectedCount": 178 + }, + "the-nexus": { + "collision": [ + { + "url": "/assets/game/manastorm/576-nexus70/collision.glb", + "checksum": "317f75ab6ec32b435597bb48889f06293617e7decf92c3f53074de9630012e2c" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/576-nexus70/navigation.glb", + "checksum": "330c19793cb0e66de51fc127e0a7054287e6cff2d7814c0b8e09fcd4a32d0644", + "size": 206484, + "triangleCount": 15741 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "the-oculus": { + "collision": [ + { + "url": "/assets/game/manastorm/578-nexus80/collision-000.glb", + "checksum": "5fdbc8689fecd44099f3f3e0e0b015e4bcc539b6d03029435ec63506977e0a49" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-001.glb", + "checksum": "a40d68b43759310099358d5bac38266bfe7810e355ed784fb2887643f416e5b3" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-002.glb", + "checksum": "c2b33a94971cb281dc7dd13f09cbd53ff0243ade0cc74e5fcb424aaa9619966d" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-003.glb", + "checksum": "c254f95b8e67851f43717a43a5fe565f5b72ea807ccc63bbbfbf46d9742e0925" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-004.glb", + "checksum": "f6b0fb24163497b956e5105a76856830867f7b903ed031e4d1d41c18503b4c42" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-005.glb", + "checksum": "320bcedbb9f79f8a3b441e42f53a645fef839b552ba29f363a9059c0b42c7eb1" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-006.glb", + "checksum": "ccbad97cc28982c90deebde92fc61ae5308d69321166416c5ba88f808c0b9d4c" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-007.glb", + "checksum": "f025c61e3f9433b166c7a0f5d7a960d2b2a3e52c22f07c4483104c0921ba7494" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-008.glb", + "checksum": "68c1535bc1ab85a909e1eb8f031774a1abc5b84c6b632dac4e59b87e30d3737d" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-009.glb", + "checksum": "1aa00cfead906490a8b22cbb3af14066d0e9e48814989c7a108a18503887152a" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-010.glb", + "checksum": "8e6751250cbf7c7407d073ba2284485427022cd11b8f578e9d2323aa4027e573" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-011.glb", + "checksum": "571d17e71c30097d3a5baaa194d976ee22a43288e4207b165ce706dcf2562452" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-012.glb", + "checksum": "4300cb53e1c114f051d8c68acd6847b776084f8d1aa7daa880a6a8a5b4827f64" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-013.glb", + "checksum": "2d6080cf41f37333e5b2df1eb6558cde603293033aac5ef5a42295f1bf7fb270" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-014.glb", + "checksum": "84621caf2100b07c51b1d04534dabf116253bb6848844cb054e0e5856faf4747" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-015.glb", + "checksum": "5d166d346ebd3b063c67f5b7bbb297bc16c0cd24d45594bda72f3dc971ee6a3f" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-016.glb", + "checksum": "a91f3e9e33cf9235538d4561da4b5e2f8c028a133b760d2d3cb7098c6a7e3e65" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-017.glb", + "checksum": "e378d42133f0e992fbbca9fedb3c3b46c6a66df8ceeb4a0b191f95fca8d92ecd" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-018.glb", + "checksum": "b709d2e8f1e0bd3ec968a71c788dfd331cc7b6142f847b8e265d69f30a02b71a" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-019.glb", + "checksum": "0d9cb5f559cc6eac5212018387c6bb3cf12e320d8fb217df2e5017a876d953c8" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-020.glb", + "checksum": "64a21aa7d2dbc2e41cb68f7ca307c762ffc55c1889ef0153a4fed3795f7c70cc" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-021.glb", + "checksum": "4f8f2712279b9991e09426064ea937d9f997a85c197d6492b3b827aedce93067" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-022.glb", + "checksum": "748c8cf88fe9219356ddb7c7f1288185b491d60df5ba92b0b9bd261db93f4e52" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-023.glb", + "checksum": "a9c0772c7d398c2bd22b38d87c0c138771b4c5cb6fb2461c23ff9426efac657a" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-024.glb", + "checksum": "6a2cddae0f852ac8d788a41b0f14bc5486f629d32bd605fecbf60733f22ae9bf" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-025.glb", + "checksum": "280471da81030e03310954a50c2876cb8211f490a047118e81b0b07b4dfa012c" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-026.glb", + "checksum": "241002c3cc87dd95ec383ff20dcb67a6a8b0b48d7fb3b9eb9b4943a00b2ef621" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-027.glb", + "checksum": "963184a5ff89f559df092685292c50498c649ffce53366e458cb8ff23f33c3dc" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-028.glb", + "checksum": "02b8a46a52f43c08d94dfb92435cf61fcbc2911d645f2c44d0c639704b675fa9" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-029.glb", + "checksum": "7255b288f7b5220491373155c01bf050ee1c9ef25fe385983318aec9ce2c9640" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-030.glb", + "checksum": "b4fc17e3f54e0529fb3ce1b86669482c6414f2f17dd1db9822b83b3018ef41e1" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-031.glb", + "checksum": "9df7821c43fbccb75ce0e2fd426ce2a848d4f52c789ee4813c62053ef5c844d3" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-032.glb", + "checksum": "598de8d9e25db56f9f9c4be225f6d065080ccb5dfcb0d71d755a40b03a87c629" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-033.glb", + "checksum": "5c20e23ec40c1c0478fb60f5c69256e13ccb081afce0c9a89479a66818340dd9" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-034.glb", + "checksum": "39356d21d33d9085a1a1f2d74dc877e95e55d8bd0b2a59c5da93d0d4148adeb0" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-035.glb", + "checksum": "eda30fe14e56ff70a1588677d1074740dfc3a12e0184d9286f67126426c2e4bb" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-036.glb", + "checksum": "3b54c558185b3827c0e4101d703a2fc5c3e46362919c91e0697fbbd39f9612eb" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-037.glb", + "checksum": "2e2eda2d16bb2b046d27e5901f551ff68be674bc3c02ae1b4bf357f0ff60af04" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-038.glb", + "checksum": "14bf46cc507a9ae68b66a5045414fb3770b519b0e8b33d562451efe74a1249a7" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-039.glb", + "checksum": "777185831a03dadd2af139f8ea17d08305911c2df4df8b275bd9b9bde61cc15c" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-040.glb", + "checksum": "1494ef439a7aa94b0dd5d56600a9e8a49a318e90d1d7a68a880eb6a293f5f1d5" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-041.glb", + "checksum": "b45e149d584a6ed8ca2c4cc49ae99611ec2ddee885a809f51018977fa4be25a3" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-042.glb", + "checksum": "0e1349aafc37c5e2b2768a4bc45a6da1420291daff89e20b4756b1cce4fe7dff" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-043.glb", + "checksum": "a67420bd61a4332e79aedf9011446d4f5dc19e4921d44bef467a992027c16745" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-044.glb", + "checksum": "47376cbca65502842bd01ef6c879aabc45c7f7e9b109021045cc599a994bc312" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-045.glb", + "checksum": "7c591aa7d196a5a2ab65854ab92f3c2b5481de318b7b08ecf563470e000501ba" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-046.glb", + "checksum": "143b5f3b6083e4fd445beb0b2b77d2a68e9d8ebda83023573f4c9b15b929631f" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-047.glb", + "checksum": "a8bb07ecd036a3975ddb8199d3b05386bba5cbdd36af712c8579354c7a7e8272" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-048.glb", + "checksum": "2f6fe3283982e667aed7de06fe73363cbca128677c5ce9853e75bd4a774e962e" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-049.glb", + "checksum": "f610bb194f944e2274ce1cfa87d41b6f728f4997df42b6bb03707e87c74fd55e" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-050.glb", + "checksum": "49a02418a000303f148eff46d91ce28379478df1b7d1250676843f6450489319" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-051.glb", + "checksum": "709500f1ecd97cdde86c305662b7a759db4deb74422cc5f4ebcc12ecd7667157" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-052.glb", + "checksum": "ab06d286abc8dede964a003049dede8f2f179d446213e96b2bc0759af3a43716" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-053.glb", + "checksum": "0fa6180bb0e3dfba9cf1b7d5b16f04b137652c5ca7a43e5b8805b3a829049b13" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-054.glb", + "checksum": "fe5286ff7af31b0a6f2e0765d444b5d0cded2820b714ef96305b7fd59ebc4d1f" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-055.glb", + "checksum": "1d45a240e57ff39630b7baa57d4ebeb609788ee7f5db8281ea4333c3366abea5" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-056.glb", + "checksum": "234a6291eef8acb55c63dd10b6e72014eea2158a2ebf30be7722a9e3927fc58c" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-057.glb", + "checksum": "20095df5365120f48b6bbe61b3ef3d58eb03e67165bfff4b457dca93f51e5a36" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-058.glb", + "checksum": "a915b08983bfd380c8f316706417c18d40183a2963103d76db2a41612f1bf616" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-059.glb", + "checksum": "8f99b40c677a264c5fe35b3fb1d53fdb2e87ea4de59da7cb9014c2d673ed87ae" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-060.glb", + "checksum": "51c6815b9be206e32f80fb62d51702d0d35f07ea41ef1a2564814c8bec5b3a7d" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-061.glb", + "checksum": "8465774198b5a8ee62518b5178d66c48338eb33f9774598f38ac7075a42db6cc" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-062.glb", + "checksum": "f12cc77f7aa56040b42759d75f56b21a155148ace1a6827e45468c27bef72484" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-063.glb", + "checksum": "bc8be589af2ff8816e6f19635b66d913213bedb75afc807a9aa36669c980cc1f" + }, + { + "url": "/assets/game/manastorm/578-nexus80/collision-064.glb", + "checksum": "4f19168ba32aa4e4c7d1c85884ffec3d541e3d9fe944a2039aa62067fed490db" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/578-nexus80/navigation.glb", + "checksum": "df86c214e916014a658e9910a3419248dfa5e3203de9a73a0ab4a813b1052e8a", + "size": 2866492, + "triangleCount": 249392 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "culling-of-stratholme": { + "collision": [ + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-000.glb", + "checksum": "5aed8060afb139ace8ddd0f0328deebfa582b61424d7b53fe197ed9f3a07f6a8" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-001.glb", + "checksum": "c7ae560092449aacc82becf53a14091dbf3fd0c5ef67ce24a0ad5a7cdb447d11" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-002.glb", + "checksum": "948552fb30c8eabc880cea1f1eff7cba31aef3df69e1775ef84d9e01ed646939" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-003.glb", + "checksum": "cf9f5132a24846b2da286372c01ed557d5ae21d16a9b71b9546972c4a39e802e" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-004.glb", + "checksum": "9e6b9ea79d5477775ab8eeb4cb16b7673d9b128c35a228db56ce57b4974ee8eb" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-005.glb", + "checksum": "140c744923e922c4a2770649ee05a538a4bd5bc745b2155047c611d056d998a7" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-006.glb", + "checksum": "9d75be8623f2b63a8ab2856105bd8acee6f74ba3076a6c73172558b4030d9370" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-007.glb", + "checksum": "ee4791432ad79d05ce9e39159fe820d2a809cf676d9189e45c0528c4287e17ee" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-008.glb", + "checksum": "3cd7acde6dbf121f8602425be912edc63645a94fb4052fde7135beba3488de6a" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-009.glb", + "checksum": "f27e44025370140ba97da752c2272c354b9239e4df674aa8f9381c8b65467c61" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-010.glb", + "checksum": "a49419071329d6e8ae16fb12a20d0c715842d278293cb88a6281bf55229cd6e9" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-011.glb", + "checksum": "ad9a45030603571d8d82047f5b2e06cee96a049ee41eadfc82ae9ab23efa6ff9" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-012.glb", + "checksum": "650ca9b292b3ddaf4e4dd044d968418ff931acd371737045b0d7a8216fabf8b9" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-013.glb", + "checksum": "474ae5c6fa643228d0533a6c64684fe4f3e2ca2aec3a9a7c2743884f958148de" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-014.glb", + "checksum": "ae21bbdf082e589bb44c7e1c2acd9a85cdb7ff6997fac7921e189b4b6e0add96" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-015.glb", + "checksum": "25ad4eadf2615263e3ffdebdce9dddcc6122ab98811b1d87fc82f12534963db2" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-016.glb", + "checksum": "b2b7875e4e088b241655d488aa9a0c119e5f9a08ab1babcb4aacff2b95cd20be" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-017.glb", + "checksum": "76c928b641c5fcf1fc6c67696bc2846abd8ec8f5957f75e5e42decc066c75f38" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-018.glb", + "checksum": "8668fcc1617c8eb16e933d17e4e3657b086191a19179ca37680247613dec0b4a" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-019.glb", + "checksum": "8c2483fb891ab87d728460acdbec3a57506163f3f0dc9c5d117b0db2ab0da6b0" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-020.glb", + "checksum": "b457b6fcb7d4e5d90a457602be902acac2c195cf47cb6ef9767b4e045605045e" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-021.glb", + "checksum": "a00b37db9815acff9774ad2bb9782d5f465030379750b852726f288dae510e21" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-022.glb", + "checksum": "13a7cd059fbac458969a4b25bba9bbfbf2da87eb6c25c66df02babd6d341ed3c" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-023.glb", + "checksum": "b07ec0b5f7215371286d0f74833e51bc19bf70494c448e8dbf206e5a8e149e90" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-024.glb", + "checksum": "fcb8e773a8ae0fc5aa8fcc54ac7c74475dd1e5b4c9ec1634833aa2640924b87b" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-025.glb", + "checksum": "eab65383c3f5227c47c7173333a552b552876075dc82f8d80d4bbb6edc4f3c06" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-026.glb", + "checksum": "a6ee307cfa4a5bca8af17ab8b0d954272d9064f697efc5d34902c75980f4f468" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-027.glb", + "checksum": "590dbe51387ce32879e32b48d3309c1e6a334c7a6ad1ee1dd2cae616bee2c823" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-028.glb", + "checksum": "ec520dcb625524fc394a0838721323e3f4909782021190738bef640fa0f9d9cf" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-029.glb", + "checksum": "4eb84964552ad26b3c63c9ec06978d30cb0e00e2f216109319846fecebed31aa" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-030.glb", + "checksum": "2a5020bac3a11058582545459b2b4b5168f102ff8ab60ae6eaa340ad0aa2b0b5" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-031.glb", + "checksum": "9649b189d4fff6e06a15869a0e87f634a8602ed1c80c9d2d9ac62a99892d0461" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-032.glb", + "checksum": "d025d8166acd13873f5f26b17516f5401a79365f4f4242d5fa94d47986cb7d91" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-033.glb", + "checksum": "d47ab47704a31d0041ca7d6f29003b95e6835931063c989da0390d2dc1048c0c" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-034.glb", + "checksum": "220d4e3504c0698f8f158aafb7f7883c0ab8c6e7ae3079bd32c57713a4757351" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-035.glb", + "checksum": "b51203bcc23018b10123fed9248f15a650b6c807244ea29ae2ea0076714003bf" + }, + { + "url": "/assets/game/manastorm/595-stratholmecot/collision-036.glb", + "checksum": "f66750a447083b7dee1e4af9e6fda569d7109b358a98b5ca9a0c9a8f39ddbc96" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/595-stratholmecot/navigation.glb", + "checksum": "0cc6336d4d40f8fd047a684e6562040c604ce0ab8e9df524e702952e59b668e0", + "size": 4762616, + "triangleCount": 406563 + }, + "ramps": [ + { + "id": "stair-flight-5", + "start": [ + 1792.2702222834305, + 107.63160705566406, + 1606.5968066288674 + ], + "end": [ + 1792.3771548985903, + 104.00018310546875, + 1612.7229720484472 + ], + "width": 3.9831960602187064 + }, + { + "id": "stair-flight-13", + "start": [ + 1337.6960795592, + 201.20672607421875, + 1807.22420378162 + ], + "end": [ + 1340.095550270547, + 196.4315643310547, + 1814.192772722178 + ], + "width": 5.483829395319407 + }, + { + "id": "stair-flight-21", + "start": [ + 597.4848827742499, + 99.77510070800781, + 1558.0378277218383 + ], + "end": [ + 599.0038727805149, + 99.14602661132812, + 1558.8117917875581 + ], + "width": 2.99055607329321 + }, + { + "id": "stair-flight-22", + "start": [ + 1837.2796630859375, + 106.15483093261719, + 1649.928505859375 + ], + "end": [ + 1837.2796630859375, + 106.64200592041016, + 1651.6246948242188 + ], + "width": 3.014140625 + }, + { + "id": "stair-flight-23", + "start": [ + 1795.4617614886456, + 100.77579498291016, + 1596.4258554896728 + ], + "end": [ + 1795.4913563103596, + 100.1493148803711, + 1598.1213416901444 + ], + "width": 3.010791515397632 + }, + { + "id": "stair-flight-24", + "start": [ + 1795.6382981732695, + 100.1493148803711, + 1606.485177697651 + ], + "end": [ + 1795.6678929949837, + 100.77215576171875, + 1608.1806638981222 + ], + "width": 3.010791515397177 + }, + { + "id": "stair-flight-25", + "start": [ + 727.5177116469581, + 113.54137420654297, + 1651.9170951650422 + ], + "end": [ + 727.6064576264502, + 114.02951049804688, + 1653.6104693302157 + ], + "width": 3.0035010932088153 + }, + { + "id": "stair-flight-26", + "start": [ + 1766.8139655307136, + 101.33837890625, + 1533.6758264742984 + ], + "end": [ + 1767.6091364712324, + 100.84974670410156, + 1535.1713255066063 + ], + "width": 3.0111847541286534 + }, + { + "id": "stair-flight-27", + "start": [ + 606.42331281078, + 99.14602661132812, + 1562.6801056026939 + ], + "end": [ + 607.9180396959786, + 99.77510070800781, + 1563.474865985312 + ], + "width": 3.0078210403426238 + }, + { + "id": "stair-flight-28", + "start": [ + 737.8128255208333, + 114.36767578125, + 1635.313720703125 + ], + "end": [ + 739.1016658528646, + 113.62348937988281, + 1635.313720703125 + ], + "width": 3.455791015625 + }, + { + "id": "stair-fallback-20", + "start": [ + 1204.8078788895368, + 200.63766479492188, + 1811.484993370888 + ], + "end": [ + 1204.5509231037088, + 201.63223266601562, + 1812.9422620474995 + ], + "width": 5.4905228467908 + } + ], + "candidateCount": 57, + "rejectedCount": 46 + }, + "halls-of-stone": { + "collision": [ + { + "url": "/assets/game/manastorm/599-ulduar70/collision-000.glb", + "checksum": "5fe71c91e8a5c207b9665c179726ae487f4890beaa8dbb2cb09f918ccad1e10b" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-001.glb", + "checksum": "da9197c1a605d2d7a16171185d9e73a6d4ca6ee478ee9b38f118aa3b60ae5a29" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-002.glb", + "checksum": "3c4c628ff78bd541d9e1b4f1e2a9f9de17c68ac0aa7fac655942eba6906605d3" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-003.glb", + "checksum": "28ee5a1b09b3e7fcfe40fe617bb74712fd96f875a5cc913f7afd4eaed926fa2c" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-004.glb", + "checksum": "a3556fcb2fc8990b5637da5646dc64fae68c54d35d0d5201437c6586f0edc77a" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-005.glb", + "checksum": "26ba2e420f01e3c7a2a4cae1b30a65a60560d7c95bdb9599764b39ab99927ab7" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-006.glb", + "checksum": "cb19e3d0bf642e98c0d22f2420329c3f411eb885629c7ab172fc8a4be4d5afad" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-007.glb", + "checksum": "2458ea457881c868f9d5c48c308313a50c7680ee017932188974c1649ce160bf" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-008.glb", + "checksum": "f20ffafd1bfad7c4445496acad56670a6cbdac47f7ac873722ce43df66ffb629" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-009.glb", + "checksum": "55d5429b0a734b84a06a7714a107cef0480a826b0af3dc9f860f8c5b3a2a7116" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-010.glb", + "checksum": "bd148b6c635308dd74bf1663af18cfa0a170e7741e1e5529f9eda23e56beef03" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-011.glb", + "checksum": "e5e6789c1cae31dd0a3613dddf6d07f28a646dcc680532c81cbaa97fedcc2434" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-012.glb", + "checksum": "8aeb4c265bec3f8092ca4054dff636f79da1a3c44d22756fb2222c0be11389d6" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-013.glb", + "checksum": "06b7b8c8847c6604dd82f878777d5f2bc72a3e7f00d47863362af0919cd68ad7" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-014.glb", + "checksum": "50de5d729f4dbcc086649c52b78cf7e646f731f83287f1b517d9b1e71c6d8166" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-015.glb", + "checksum": "a0b84808380384ad2cbab0aee59a3f494264d4e87b3a3468a760a4aa9ac0017a" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-016.glb", + "checksum": "39cf6f22e10f90de57dbfd1310147bb9c786eda716e1b6902a9a70aadccd3f8a" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-017.glb", + "checksum": "6c4c8fe598558da8d85d85333d7d5a9a1898e0294c05debd15fe56b8ac5e5dd5" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-018.glb", + "checksum": "36cfcc9a8407db77ab58c587944c36d101e0c6557512e4af040932579b531a51" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-019.glb", + "checksum": "a2a73497fe96614c3e455850663c6eb782b5a9e38b519a331efa37cae7b26c36" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-020.glb", + "checksum": "a6c228a000ab562eb0119434ee46bd7df4e582a58fbd1fa6519f25629da2a28a" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-021.glb", + "checksum": "decf3d655e6e7bc063b890d3379a4838d7783a17442464446a775841b1d91f6e" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-022.glb", + "checksum": "33418b3b1af15a38115d5b8a58670fe7b85e4f7a37fe452d0c28ca44e3f52830" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-023.glb", + "checksum": "dfed306093680095cd9b6a38e61279130a9df3009853e5d4e9c425ab9a6c66ca" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-024.glb", + "checksum": "a4e3e233c49fc7676734e575a0a4f88f6888bcd2a639a230fe8e181d1ea315e2" + }, + { + "url": "/assets/game/manastorm/599-ulduar70/collision-025.glb", + "checksum": "1bcff3dda88d7507979d80b12a06670f04e9a15aa80b14b7fc01de8e0564972e" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/599-ulduar70/navigation.glb", + "checksum": "fd6366438476906a591e96c64f2d4c876fbac4b95d4e5516f6838203c6237ee0", + "size": 882640, + "triangleCount": 74793 + }, + "ramps": [ + { + "id": "stair-flight-3", + "start": [ + 634.7064575195312, + 207.69625854492188, + 1048.2894897460938 + ], + "end": [ + 651.1766577148437, + 202.13339233398438, + 1048.2894897460938 + ], + "width": 23.4515185546875 + }, + { + "id": "stair-fallback-3", + "start": [ + 709.4601017252604, + 194.94097900390625, + 1293.9694213867188 + ], + "end": [ + 714.6977335611979, + 196.5705108642578, + 1293.9694213867188 + ], + "width": 18.8438525390625 + } + ], + "candidateCount": 23, + "rejectedCount": 21 + }, + "draktharon-keep": { + "collision": [ + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-000.glb", + "checksum": "c6250d1e360a35bc714122b764f5b07dc840b41ce4fc69080e15a3176ba85b4f" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-001.glb", + "checksum": "7c754a3ae46549440149ac14007b09411a4424b2f1856510b8d483ccac2a8fb1" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-002.glb", + "checksum": "25df76551765a59d1893f1f120f5798a5c389fb7ac14d2dd2775f9949b9e753c" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-003.glb", + "checksum": "042dcd68e48c627eb565b15b014ab2406c55e3e59da7d47165a648039cb0864e" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-004.glb", + "checksum": "ba6d07f01038f15669f245fb9c534f58c1271ad24a6d6d8cee8f7fe54ece2ff7" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-005.glb", + "checksum": "8ad38de85df1a8611d3ccbcc607fa4a757afd20819d697b07f473262acc4625a" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-006.glb", + "checksum": "fa6f92a611ff44330ffa06938fe96be3bc758e9aca982dc9b8fb1ab0e74a5cd3" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-007.glb", + "checksum": "c6b6068412f46770a58030c3bb47e92babacd7e17ba73c676bb73f0bf2265ea3" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-008.glb", + "checksum": "6df9f37308c03e153b27d647b0c95cabe295eb2ed231262028becbb20d447944" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-009.glb", + "checksum": "2710b4b875aed819f8f1b4673b50ff97e2bfd982e868da6d069885945a9ccc22" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-010.glb", + "checksum": "750762de26e25ff833689a2e3f1b0ea55046d60320b7a636bd927498ff5989fe" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-011.glb", + "checksum": "5a20acb7d13b2f7cd4dfe8f4f55a5d5cf6616f58edee066e26f3e74f2687be71" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-012.glb", + "checksum": "f5d042513fb14ebf753d90734695ce3240a8c4ab6ce710e8f9cc009197df2fa8" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-013.glb", + "checksum": "b978d24d82c222c2a8501ec6f930f356172866b9f37689f9abe1f9f518ad7898" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-014.glb", + "checksum": "51b73d813acb3bc6fe484d86f1f8110ad1e464af227d537df2a2e7d46df97400" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-015.glb", + "checksum": "63c3692bcd810275e454568f766950d15862472655a255fcc0414cdcae683303" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-016.glb", + "checksum": "50ab4aaa85b657eed525173ee93f5a235b0f57f8b18c9ecb9b5d7c89066f085d" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-017.glb", + "checksum": "24f9bd6f478e592407c11b37d4bf9486ffcfaaf749ce327464989836e65986dc" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-018.glb", + "checksum": "a288d65c8b475f2a9a5a4ee4a112c3ef6b2c5a1788b00eec365b566c910722c0" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-019.glb", + "checksum": "c64ffa345381fe8b86f4331e73791984b81e974fda8d5598881fd583e515ac82" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-020.glb", + "checksum": "522f3de0441cfe6bc813a421b75828be6c5b277074d837f1952344fe0f2de924" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-021.glb", + "checksum": "ef3b5cb2d86ce9adfab263d38820c3b8fa4aae5abe51921c50078793c0728bef" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-022.glb", + "checksum": "10a16f42b69053620083db06d2e177e5e4a30a7edc139fd9e2c019b19686fcbf" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-023.glb", + "checksum": "cb0d7dd64437e9c936676b5e66c8eec5220d7733987f8b8baa1dee01b0eb2c33" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-024.glb", + "checksum": "3c4a274a2d47e76c8ae2b1aeaaee34e1ec0fedf5a7892180f6dd7891645b977a" + }, + { + "url": "/assets/game/manastorm/600-draktheronkeep/collision-025.glb", + "checksum": "4afcd91696b56f6e9d62547fbff018759ec576ae2dfe8878872c29f0fe5dad8c" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/600-draktheronkeep/navigation.glb", + "checksum": "3ae4c87f2dbe01a96e489e7f3590ead93b260e3a70fc4fdb59f397be977443d7", + "size": 4362736, + "triangleCount": 364577 + }, + "ramps": [], + "candidateCount": 8, + "rejectedCount": 8 + }, + "azjol-nerub": { + "collision": [ + { + "url": "/assets/game/manastorm/601-azjol-uppercity/collision-000.glb", + "checksum": "13b33f85227afecafdb7320a72f6e2a5d4a267f6bfadada6b6d7a3e30f871318" + }, + { + "url": "/assets/game/manastorm/601-azjol-uppercity/collision-001.glb", + "checksum": "e335be6512c809ddd4b0903c311769e1949c1ea02fb11a61c36931aec1941404" + }, + { + "url": "/assets/game/manastorm/601-azjol-uppercity/collision-002.glb", + "checksum": "a4a26779151b0677ac026287ac1114381321016bee4028eb48e099ea40fc562e" + }, + { + "url": "/assets/game/manastorm/601-azjol-uppercity/collision-003.glb", + "checksum": "5e5422ed8787474d1312330c12b11ce5ea2438f28ce1f5667ca2020cef0fd789" + }, + { + "url": "/assets/game/manastorm/601-azjol-uppercity/collision-004.glb", + "checksum": "abc95add7eedf7eeb9ae91718b84495b00486224e041af8d1a3a81a67714fb91" + }, + { + "url": "/assets/game/manastorm/601-azjol-uppercity/collision-005.glb", + "checksum": "eef0717a92f7281c14a26b90262158bc4f869cde17311b30fcf7720cb8a80144" + }, + { + "url": "/assets/game/manastorm/601-azjol-uppercity/collision-006.glb", + "checksum": "9563ed33dc2e5131ff0677b81d0566341d8413b9db434c87df100d93d275406f" + }, + { + "url": "/assets/game/manastorm/601-azjol-uppercity/collision-007.glb", + "checksum": "69d06c455fb483075faa49f90d332a2ab5eb8d0f7678192dc1fd22f17468bfe5" + }, + { + "url": "/assets/game/manastorm/601-azjol-uppercity/collision-008.glb", + "checksum": "137afb32344cce1fde27d1084a20557eb605fb9abf8119dc72fa4cbffb0da7ea" + }, + { + "url": "/assets/game/manastorm/601-azjol-uppercity/collision-009.glb", + "checksum": "00b9ae825b2ca462beac744bb10e482533751624e93f942ddada360d33036b7f" + }, + { + "url": "/assets/game/manastorm/601-azjol-uppercity/collision-010.glb", + "checksum": "0e6d551892b3409d20b78d54f9fd12e1a261a03b162e3bd32266743205eb71d6" + }, + { + "url": "/assets/game/manastorm/601-azjol-uppercity/collision-011.glb", + "checksum": "cf1f7d2b73dc26e85aa23392f1d764c45b4e396528b45588b8bd236b232a5a56" + }, + { + "url": "/assets/game/manastorm/601-azjol-uppercity/collision-012.glb", + "checksum": "e5f4104edda40975fd71d4a887bf38cd2f3d95ec9c12c363ac8d3d311537b807" + }, + { + "url": "/assets/game/manastorm/601-azjol-uppercity/collision-013.glb", + "checksum": "813911bf2bd55edb5cbe048b21a84ffaab16df245c47594aa7b8d25ed5a57fc3" + }, + { + "url": "/assets/game/manastorm/601-azjol-uppercity/collision-014.glb", + "checksum": "0bea9ee488eba9aa7e113da81a97810df6bd1d33c00842c7efe696ce4b4f9578" + }, + { + "url": "/assets/game/manastorm/601-azjol-uppercity/collision-015.glb", + "checksum": "69206949fc8ba8554312d65c85fbeefa5a7da8dd348acfc782b35a45ab7a1659" + }, + { + "url": "/assets/game/manastorm/601-azjol-uppercity/collision-016.glb", + "checksum": "9666cb782f3613fa94ec8177b7c7702bcc4853956c487e6689318da7168148ca" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/601-azjol-uppercity/navigation.glb", + "checksum": "a0e40e020a6de452e8e81cd9a056e737a4b5c45133cc28e421d05f24495ed4f5", + "size": 365248, + "triangleCount": 29405 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "halls-of-lightning": { + "collision": [ + { + "url": "/assets/game/manastorm/602-ulduar80/collision-000.glb", + "checksum": "04617866cdf4afcb5ad0d20e7cb2a69a1ce11acb8a5763e7d86d44feb102d2c5" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-001.glb", + "checksum": "52a0ba562d4254e7eaa9fc79040233bfb44509790063c09a67e4b86666c3cdc4" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-002.glb", + "checksum": "8a14f8f608681108c84aedf75a5641a459581bba347c1ee7931067a78d932266" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-003.glb", + "checksum": "c9c2652e490e6cf3c3f19a2c9819137bdef1189acdb3f835876f851f0bc363e0" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-004.glb", + "checksum": "0b523fe9e230ce41df0a58667107ec185c8d21084f433bb5cd02f8932b6feee0" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-005.glb", + "checksum": "52a8f5c916bc4ef817e6d9b6b2f9612a04bcca6650d3b3141dcc68c23da8e9dd" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-006.glb", + "checksum": "deb8f007deb1d26f0fa5a06312eafe7aa7c89ae1a70bff98b38221a37b761f47" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-007.glb", + "checksum": "eb00c7ff82c8ba506d5361c5635bfd441f44c37358c780e43f9bb82e5aba6c33" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-008.glb", + "checksum": "57cea36e68caec6b8c156f6ccd5e3c883cc7e0f83f4ea10e3f8d9bc712577180" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-009.glb", + "checksum": "5443483c56a737d3df1dc55669beaae55f6c0686ff1b5f036a071e8e3588b42a" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-010.glb", + "checksum": "cc0ed4c90e9189d79f8d61641f01b67f5bd1f3a75958d93965c8df822734797c" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-011.glb", + "checksum": "e3628bf833f67642da353d3da61fb520249b92779cae406adac264fa0b81eac8" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-012.glb", + "checksum": "d0788642d4a6dcae261b28d1d27490b25cf4c4f9874de3e06e223a521d22eb6a" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-013.glb", + "checksum": "443562d0fcfea2d6a1ed4c4b5d17b0d569a8597f424a2410fbe4fab042fcca17" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-014.glb", + "checksum": "07c63968b0017e66410de8086b9f363b0ece28d4ede43756a3d8b47568afdcc4" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-015.glb", + "checksum": "885bf34661d50d2f9cb9490d077c549b88b7b558f8dec86f4766b913983366f1" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-016.glb", + "checksum": "0f081bc563e0c0b3e3f2fb693fb4afe4f55b972e62bcb6a65f80f6bcaffb1837" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-017.glb", + "checksum": "249431347db7f6db00db83aba45f95894ec424a8304093201a2dd87a39ca4292" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-018.glb", + "checksum": "79b2e41832c61be86d435f07b6b64618e98e52447c78644763d56b94b9e885be" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-019.glb", + "checksum": "6797465424114beb0b29966448462dcc1f26a3612a33f6fef2f2df03217a5e70" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-020.glb", + "checksum": "dc74e61b0903dc3b8a5ce5a2c63f0af49ade2adc2f657c5d27204f2a2e56599b" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-021.glb", + "checksum": "4fa733e62c7e92862cea98dddc27b1971c2b3828efcfec94d545d22017ef7c8b" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-022.glb", + "checksum": "2f2215aefaee3fc5f2467b90e2246ad91c9322d3741dbc0c6554481698aa709f" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-023.glb", + "checksum": "897649dfe2806bac09a1b9b932aae48ea17e52e201ef43527293a242e5462fe4" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-024.glb", + "checksum": "677808ad003eee9fe934ecd51f3ad16a1a72676b4708439008515b73afe506af" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-025.glb", + "checksum": "5394117a68c0b7fe9d68612e89309a774cd4b1049d75f56d0944ed706663b33e" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-026.glb", + "checksum": "2672c0ca5d8e9c2a36042c968d833123d625e0ea2eed7444a326ddc6cdd27b8c" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-027.glb", + "checksum": "b2d54c1ab6a8de4dc1384571433a63bc241634038591d10a32fa13645879c1f9" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-028.glb", + "checksum": "b70364e240bb9f7e501b4bcdc7d06f63e5875d25a1a8cc77ea13a389d4d99bae" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-029.glb", + "checksum": "00d56a0e7dad84536f3873d680889ed35f9780dfce7bfc4b8c21474d541fb6cc" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-030.glb", + "checksum": "16dd6c77416f6267fb9d82f2c4eae47beced707b7c0e1919dc2d767820dfe84a" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-031.glb", + "checksum": "65b64c4d5614d3e82c0ed24efdbcbbf4d83f6de39e092b861961ea3e1d4da046" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-032.glb", + "checksum": "60a8c9e0138a6efbf3be15073ab101b5f9ab902b60dc661b7be1f9278ac8974f" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-033.glb", + "checksum": "01a7807aff8a66d34eadbe524eea9162afb8c785bca7817494a61f90fee0f57a" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-034.glb", + "checksum": "6edda3cb2a37abf24a155db4fa77ce26a73eaf43e706133982ed617006434ac4" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-035.glb", + "checksum": "0aa0f9a6b906e1b99da358b5fe0dffcd336f89a50e60d99fa95bc3076b298fcf" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-036.glb", + "checksum": "de29778af33b37be2478f56e376e448fa8ac942208476e99c0b73f32c65534fe" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-037.glb", + "checksum": "5b5e609fbe329f7c918387a37b6c9fa141cd662ff59adaedb4e671b2d415351a" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-038.glb", + "checksum": "edd19878de013aa3f79e1284c8f4bb1766a2f12334b03b7de0834ba9a6a9a435" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-039.glb", + "checksum": "642edc273856d009a2484478a6f14ca7ffa1948f006ab74183eccfb7de3a96a3" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-040.glb", + "checksum": "164474c15184717563134327e84a6bc1533bb35c752f8ebcec4ceb7936c26b8b" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-041.glb", + "checksum": "158efeccbdadcc6bd640a4948cafb211347513f5ea9ebdb6dabcc344e35e46dc" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-042.glb", + "checksum": "c4370d11ba7c7b5b0339421ffe1e1f8af4dde1bb6019bf1c395b3ab98f2a49f7" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-043.glb", + "checksum": "932a49730aa9de47deeab36ec27a6a473529761f5c4604c7e69a097713e00fd5" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-044.glb", + "checksum": "e5639c1a5c5200e06117b5cfdf4f2eaa44df6f85bf8f19a762822411d0a36ad3" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-045.glb", + "checksum": "eba028e348acc30f4fd89e12f28db80e4ea782a737921218d4822bd8c8f04bd1" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-046.glb", + "checksum": "24f4ab0ec9c1084e39eeb66ad9e32caa681276dfbaede1e39ba757d6e07fce26" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-047.glb", + "checksum": "0f6e4b7aded6a97b6d3db75eeb01abc731d044b5d7e8be9f7361c2ccd16c9ed2" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-048.glb", + "checksum": "64acf303daabb731ec6af0b266e6f13ce1397f2f630e7ef4663fc835186267a2" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-049.glb", + "checksum": "c96da58c83878ff95c2bb189fb3696a573d54203a95e6fe6dd4046e52cbb1511" + }, + { + "url": "/assets/game/manastorm/602-ulduar80/collision-050.glb", + "checksum": "892fdec93ea5843c44cfd3f7349ffea9cce9189580daf6e448aa17e4ab6efede" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/602-ulduar80/navigation.glb", + "checksum": "ab8ecd073d634823a3c37772cba6f7fddd20842f119c285fab385fd793773c39", + "size": 3402184, + "triangleCount": 292705 + }, + "ramps": [ + { + "id": "stair-fallback-21", + "start": [ + -947.5400333068646, + 138.35630798339844, + 648.819575639846 + ], + "end": [ + -938.2897489098698, + 132.80821228027344, + 650.9551720659772 + ], + "width": 15.72497584691725 + } + ], + "candidateCount": 252, + "rejectedCount": 251 + }, + "gundrak": { + "collision": [ + { + "url": "/assets/game/manastorm/604-gundrak/collision-000.glb", + "checksum": "ed1ce62f544c6fb47d649746f8d9ebf1c6ebe2c4a88e4c4ed3fd3b033edc49ff" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-001.glb", + "checksum": "0ddf22ba15a12cae7145f1613c910940d288f7d6d51a37b62cd5bab61c5070a3" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-002.glb", + "checksum": "929db3926fc1719e33a6c3bfc244fee24ba1373fc9885f746c9d5359c1d3905a" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-003.glb", + "checksum": "79d6197cf3e9df89bc0b53fef3aabeb893b5f87e7e5d3ac24144f933a21db921" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-004.glb", + "checksum": "3b89e2654df953f73230be4c6bb685ec9d4bbe4b38dd1ae63529dbd802e32bd4" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-005.glb", + "checksum": "1a1341d41849d571b27804b6f012173bd3ff3c4f3f5e47a183911386c45bfd80" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-006.glb", + "checksum": "070283ce42d0386357676f669564d2f994752b2fc6986e0f58b53693a04a3392" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-007.glb", + "checksum": "d194a6fda7531e8eed6c50c037b2b20917ca29106f000ffb593eb726cc3503d3" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-008.glb", + "checksum": "76610f0a4d450e1dc8039c050f6cb0cca91c24f1f58fa13ed727d26c3833ca18" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-009.glb", + "checksum": "555feebb9478a014242157e979408da0b8db5abaa410b23618d490e2018627ca" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-010.glb", + "checksum": "4ee44b8bb7de224ebfafbe99d3d0e1c242f358b5890278213edf2c39f0d96a05" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-011.glb", + "checksum": "243d56acce35178d465953175a2805f1f6f6c9375d40eb9eb6af17c03d5f6008" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-012.glb", + "checksum": "d8a288ccd02b368fb7c2f87bf0cc77db0b6b0e71b4948478f97b27c863020a1f" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-013.glb", + "checksum": "65f246f557c5182d83d539df31f78f3ee82162ffab2a8d091be333f35e149e97" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-014.glb", + "checksum": "20941037eb57813b451131b9b5d2f54f98bd733f6e154c472e1cbf6588056967" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-015.glb", + "checksum": "232afb1d61351f0dbbe83afdb0a8898f119aeb7351d04ac00f60b6d5fdb7c9e5" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-016.glb", + "checksum": "789fdad76b0f70b78ac1dfce59d674dad361acdd921dc649056dcd5863f65930" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-017.glb", + "checksum": "dfad0f72efb8d54a88325de58bb0a381cad6ef74b0edf3fe8ee678c555ca2ea0" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-018.glb", + "checksum": "d1d6b1515d955dea2acfdad08102fd0a0c00775174ed4c0a7844a9c40e52f013" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-019.glb", + "checksum": "be1581db72d4d2d39230ef7b9eea8a0f561797b0f428e82166fc6331fef58e8b" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-020.glb", + "checksum": "8da2adf5fd0294d20c90821bc1a42984cbeecb7bed5d78bb348055f1e4b82a5f" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-021.glb", + "checksum": "e107ab3fab4f3747a83600379c4020409839cb7c6f0f0eafca25fcf1120d5c3a" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-022.glb", + "checksum": "8c757134a76e90a61c07a82eb22145605cca79d120e5d6c0000141c341732883" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-023.glb", + "checksum": "d4d7e447b399f8ae9b250265c9be9189bab3df9aa14ada9c901541b483d2040c" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-024.glb", + "checksum": "083f8ec7c8b75a220eb6a4aa66e7b4302c99279efd467b3f81e3f931e3f6c860" + }, + { + "url": "/assets/game/manastorm/604-gundrak/collision-025.glb", + "checksum": "603ac2097b0a0d441bcbeada216ce469e11c3ba04ec6f53ba3d2740232b62855" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/604-gundrak/navigation.glb", + "checksum": "ace7eb32a564022c5ddb2a4a65532c62ad7b01773ca6bf3286d23344648801d2", + "size": 292128, + "triangleCount": 24827 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "violet-hold": { + "collision": [ + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-000.glb", + "checksum": "48e7defb1df8e3587a315cfa744832b4208dec376b6f4127ca18dca27ced1a47" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-001.glb", + "checksum": "8db9fc4b2b0d9f7bdf75198e00708e49fa8c28c49f82822c843c2e8a2f89e356" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-002.glb", + "checksum": "a5059d5b423bde1d35497d40f139c80e223d6d1203f508d405bbbe903356229e" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-003.glb", + "checksum": "79b78e82d87464d6ff7dcbd38d006791a00a3fed753700cb78b6634c973cbb43" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-004.glb", + "checksum": "ea2c07024f241ae5fdacf4d757313cb82e7f7b99178ea3d0576c66d4915f32b2" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-005.glb", + "checksum": "cc650413142a86b996a770a903f5319559a8563e0770563082eb4c6043455069" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-006.glb", + "checksum": "eabd58aedb41684495f467f1b1cbde6859750c498f3364c9e15e89207f43f08f" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-007.glb", + "checksum": "284e0990387f4da1f6e709e90b43fe5817e803affd2f3b4ccfd71cd4cc9b73c8" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-008.glb", + "checksum": "c94d0d34dc7b4e02ce0305f4288b33d4b235c0534cd12a77b5086969a1723b30" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-009.glb", + "checksum": "8c1e2807414015e55a29333609455115b2b0dcd14e712b0c9d0095b864b477d8" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-010.glb", + "checksum": "a4f07cd549a04f81a9a095ab4641d6420c235f255613efc37ee8064246b226c0" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-011.glb", + "checksum": "35e3cc6bf9a4058abbca8b3bde0082682fc7cbec0265306946ad1ea54b80bd63" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-012.glb", + "checksum": "271550b91091e1d24b09176b9736d8a6b275de6fea9cc1206cbca70ff4e94ffb" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-013.glb", + "checksum": "de15a6591eb04c22655736193a19424140fb6c79809618d5c6da6681d917fd3b" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-014.glb", + "checksum": "1fde91ee0fd90806bddb0fececd157416222e62f620b1dec76920585d169f50d" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-015.glb", + "checksum": "fbe39e901ddd9897f3410490806d676eb3d6cf8dc3060c67170078b86a8a8d49" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-016.glb", + "checksum": "c91af5156fc9cb2befa124a99edeab6b03b39526c2a031cf3fb8207558d65565" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-017.glb", + "checksum": "95726c31d085a206f772412a8a39b8f368fbbba8f23c82d7795be990162bd67c" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-018.glb", + "checksum": "8e150d1c25cf8909c6439b82ca006ede03fc479e1b13ff0ebce89d69cbf01069" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-019.glb", + "checksum": "401112e5abeb64f5a8497c14fe5d52694db6716858fe96b237c5f4c076a273b7" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-020.glb", + "checksum": "f8723f121fa48346f4ba323cb97ff4de83c5ed5c6b1b0bc83e4e3a8faaefc92d" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-021.glb", + "checksum": "5bec98379321457aad66dd666983048741c7ed6b1887c5f90f5f3c7106bb7a3b" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-022.glb", + "checksum": "23d20ae28498e2f0ab1f8f127d9537e5e9c42329f103a93dd8d8abc8768076f6" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-023.glb", + "checksum": "130c71a1719051b177999990b5320d5413df36abce2c48c4f65f22f81e2363fa" + }, + { + "url": "/assets/game/manastorm/608-dalaranprison/collision-024.glb", + "checksum": "c2467d164cc777f6771afdbfc8a43d1cb3b5a95cf4f0395bf1cbec049de68ef4" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/608-dalaranprison/navigation.glb", + "checksum": "fd17cfff5366b82fdc5aa0cbcf5462a21ce807453a3aadb6918067f5e9ac734b", + "size": 63304, + "triangleCount": 5795 + }, + "ramps": [], + "candidateCount": 89, + "rejectedCount": 89 + }, + "ahnkahet-old-kingdom": { + "collision": [ + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-000.glb", + "checksum": "cad2f02462b6e79e963dd93a56a47f5b7957d001a8b3b4cf6ff552879ebd799a" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-001.glb", + "checksum": "cdc17214d35fb1198ed7bfb8582d19600b1852f72a9f315cdfffdf396ca98a48" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-002.glb", + "checksum": "16da2fd6919e4fb389f9e6cafdd86936d6f4bb3b1ff2b5c170f3795fe12dfaa9" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-003.glb", + "checksum": "65f7888b95d61c4d5ec15dfe9a577f9699792db59bbbe78397102f9bb78fabe6" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-004.glb", + "checksum": "f553585ffbf03e78928ee7045133f56a9af0197dc166c2cef88e48968d308109" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-005.glb", + "checksum": "07abf9779e8bd88ff341758107f7ccd41c0ff9622418783a51270dce918356ae" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-006.glb", + "checksum": "c386d4c372845a2e4fac09531b08c826c7c0578e10c83f3241a936338dfbbc15" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-007.glb", + "checksum": "90563a406caee7f74728be93155251af79a450c4aa166449198b6925cce33d85" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-008.glb", + "checksum": "085c2100e32fc935c951b8dae1422247fea19bfb3d909389c031ccc026008a7e" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-009.glb", + "checksum": "3d67047f9d487a16a9269d7b13f84251e4e080be864087aea7aaf0afd85aee53" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-010.glb", + "checksum": "01dd32fc24f6708a88847d9451d5d69e64ed184339927c61420febe9bf75e116" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-011.glb", + "checksum": "bf88061be44744dbe8d2fe4749b45f52dbfed1e4b53dedb8a9c746034a06d42d" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-012.glb", + "checksum": "a0349e63dabcc8dfa23ba2ebba63fd7e7468a25b0e8ffcbe9b7d97ebf844485d" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-013.glb", + "checksum": "5a73b0098c8d69ff31a5e0c32e7834df1acda632605f15b0977bff6e51652938" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-014.glb", + "checksum": "1764f34fab350c79be8f7b47d08584d2664a23290547b4c88beb3cba102bd970" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-015.glb", + "checksum": "a52e529825fe98dfd012a21d59ed8c609cfc62071ee8eba7acf8fc2f1408d592" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-016.glb", + "checksum": "f9347b862cb6cfb0bc1e38879d509ae047fb68147ddd5048e6b85b90ddd1bb4a" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-017.glb", + "checksum": "30ee1f125d7f12968b6d17eaec3c5d3b25f7b3e76c517ce639ff1aa9f8204a1c" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-018.glb", + "checksum": "19c46c6c5ae76e51b7ce5bc8cf5331e7ef5022a1e02f9e61244fc8fa857b7aea" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-019.glb", + "checksum": "3d7dea1b78024c3f1e6e2ed2fc2bb9fe38b186bdeb0774eef2e2b526917409a9" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-020.glb", + "checksum": "ffd1c35bbb0b3a097e4703b070483b450140666dc94df574dea65b6b2227d6d5" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-021.glb", + "checksum": "0760e455e62c304665abec8175ffec9eb1a54da4783a7587d52487b7198978a9" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-022.glb", + "checksum": "6c1ee27984423671dc40c5ce9869df8c5b5538d0b76dfc05d147d18bfb76c815" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-023.glb", + "checksum": "61f000920754062f3262eff14231fc177374638a6858b7d8d2e8be1fbe607275" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-024.glb", + "checksum": "093c3e423c24b0b8f5e0e324769e324511a06a68c6d9c7356c876fde846c8d93" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-025.glb", + "checksum": "fcb7957c6ccc1b482af9cb8dc905d651f7101273e57983326d9e4b6674db12c8" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-026.glb", + "checksum": "f20b5ff4dd21c802a7ff1e898742db4ff4f5b0c1ab48783ed663002c9e2d9e8d" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-027.glb", + "checksum": "e81ff97a7494b153ce317aa5a09222602f24c86b6d2aad940c8a5a6edf6c8224" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-028.glb", + "checksum": "ee3fe76ca6ae51dd782ea3a3532d150c4d4a3f0c2a331a3ff342f3e0eab0663e" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-029.glb", + "checksum": "94352cdcc08a1a81cc815a029b98dc71d09e8a555b607fd95fc4b45c6635ba07" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-030.glb", + "checksum": "0d51a24606c96a10546c233cf7b8be3e26d2028b78988732567c04c7ab1fbd69" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-031.glb", + "checksum": "4a6243f6e7d37469e9f611623fe9d13378cc3373108f3494a311b00a7da74e55" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-032.glb", + "checksum": "db22498996b4c9f0d496d612d6eca4ed5a9a308ca9daa343e3b8c9a91a3edf58" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-033.glb", + "checksum": "71612522bf4b35d51eeaf083c6a53bd5e69332b24aad2bbfe21e3ac4ff802bab" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-034.glb", + "checksum": "98589dafba1d1b15986ecbb051659e91311a186365cbf57c9bba717f938a86fd" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-035.glb", + "checksum": "251e9616559d542955dcf5ee8524a777b74753d7a42e3cc5c62b074458ca8ec6" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-036.glb", + "checksum": "e7f5a0330f5361dc3af8f8b87e4447dd4d11eb7ce5fa0d86bf7876c03efe3bc3" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-037.glb", + "checksum": "fb42ac71edc188fe8deb0b78d97305fb2272bd908a047a2c02a01ab2bba69da9" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-038.glb", + "checksum": "a44a6fec1794f6d593d0ad621f89f572571c91c36fcf7c993ded7c1376a1c036" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-039.glb", + "checksum": "ee0a11bde371218dc58fa6a8578ed8fe628508f07c4c17b1e6583b94d156226f" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-040.glb", + "checksum": "79c6c4db4e7bfef7f5963c3c1c8fc49c5d924214277bf30a446fecb7409adf3f" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-041.glb", + "checksum": "4e65cb4b4cefa1f648826dd74319b995dd6637f808aa6d398d4528d86fa47f74" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-042.glb", + "checksum": "96ae8ac0b2d07b64b7eae37612258e615c3a22ec74bacfbbdb304e641ec5b1bb" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-043.glb", + "checksum": "72b1b7ef2bb40b7fa08ae80570e42fbb92251166aef5a7b6fc0a1c90c673bf35" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-044.glb", + "checksum": "8c3ddb1d536cd70cd6a563b921aa3dc03736af9d05a6ae7b3aaa84d20fd4aa3d" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-045.glb", + "checksum": "03e0ac761d68d55eaa34badd4c5231d06307ac664a8cb46dfe84cd33d0e14132" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-046.glb", + "checksum": "a1ac9e44717dbf2d60d06e2d7d5cda486c93048be6a321e9d3f1125ece28adc5" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-047.glb", + "checksum": "c925047df9f591e0c513d7a93622c98d1e805c506f02e59eb2ee2298ef93e8bc" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-048.glb", + "checksum": "0080687f0c0c212a3c9b8d0f4c0868e46a76657daa575975c48a2b886a457aba" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-049.glb", + "checksum": "2eadb7cab0c4ab8d49bd8d4182d0630d46c652c066f206598703172bc2892052" + }, + { + "url": "/assets/game/manastorm/619-azjol-lowercity/collision-050.glb", + "checksum": "852846ace7e5907c2c0643661ff48715824d4b197d1b1bc8d554be0b851030a3" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/619-azjol-lowercity/navigation.glb", + "checksum": "7b49990e78209eb0ccf3449403bb4ba17ce9482b0becbd8cf5b155d79e317153", + "size": 4747016, + "triangleCount": 402335 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "forge-of-souls": { + "collision": [ + { + "url": "/assets/game/manastorm/632-icecrowncitadel5man/collision.glb", + "checksum": "e6e50ffb3222fd8cf27456844bdd7d8c4e249454241c8003dede7c82218897ef" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/632-icecrowncitadel5man/navigation.glb", + "checksum": "93034b579c111f17356554bb4e37fa56dccbb89523c3a88a143797cc2fcc82de", + "size": 358564, + "triangleCount": 26452 + }, + "ramps": [], + "candidateCount": 13, + "rejectedCount": 13 + }, + "trial-of-the-champion": { + "collision": [ + { + "url": "/assets/game/manastorm/650-argenttournamentdungeon/collision-000.glb", + "checksum": "ed75a335eaea33054eb8860216aecbc18d91b5ad223c8b8ccde81062ad989b3e" + }, + { + "url": "/assets/game/manastorm/650-argenttournamentdungeon/collision-001.glb", + "checksum": "b9b074479057f0bf609001e3a4918d90dbe43d0a85c37ab892a6eb1e27504280" + }, + { + "url": "/assets/game/manastorm/650-argenttournamentdungeon/collision-002.glb", + "checksum": "4c2a0284a33930f41214713b9246f5b164f864c594bd545d62ca705f19e6f522" + }, + { + "url": "/assets/game/manastorm/650-argenttournamentdungeon/collision-003.glb", + "checksum": "653be0261418bec25c1a0d9ecff0d0a179dd2e32b4d4d811fa2267645932a0fe" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/650-argenttournamentdungeon/navigation.glb", + "checksum": "03d0ee92b05bc92d15a7e7c717bb97c90e7262a254a94b921f1e1c5ed6787439", + "size": 1610632, + "triangleCount": 128834 + }, + "ramps": [], + "candidateCount": 4, + "rejectedCount": 4 + }, + "pit-of-saron": { + "collision": [ + { + "url": "/assets/game/manastorm/658-quarryoftears/collision-000.glb", + "checksum": "99799000a867a14422630d305ed6bb7943b353774325a73b85727245e2e04e4b" + }, + { + "url": "/assets/game/manastorm/658-quarryoftears/collision-001.glb", + "checksum": "45392336c8a338ea436170665e5815d56b710c149575a3709929193a734002e9" + }, + { + "url": "/assets/game/manastorm/658-quarryoftears/collision-002.glb", + "checksum": "b9ffc463e825eecc879c2c8303f43ca5800c4a2969b5b367dac96c4f5b864371" + }, + { + "url": "/assets/game/manastorm/658-quarryoftears/collision-003.glb", + "checksum": "ce23f687d1af7e3e9714462ac78a1a7450324ad0faa083ba211aa13701667cf6" + }, + { + "url": "/assets/game/manastorm/658-quarryoftears/collision-004.glb", + "checksum": "db89e660bca5897af3c3905a873e6fab2cdec25b44c18533e21083809ed25a3b" + }, + { + "url": "/assets/game/manastorm/658-quarryoftears/collision-005.glb", + "checksum": "fde33d9adef84ecb479f0deda42459b1e259ae3d6baeaa8f7b9ed0dcd3a5ba30" + }, + { + "url": "/assets/game/manastorm/658-quarryoftears/collision-006.glb", + "checksum": "6bd6d0baa49e5176bb095e28e71f5c5fb51f2f5634359944facbbadcfe5478cf" + }, + { + "url": "/assets/game/manastorm/658-quarryoftears/collision-007.glb", + "checksum": "e42824d1e52da4f01425e64d63859ce3b3a1fd54a5e1532e7d72a3e62b29e035" + }, + { + "url": "/assets/game/manastorm/658-quarryoftears/collision-008.glb", + "checksum": "dab6cc30b32927eb49fdd9e68386bcbffb23b4413ef5824e79ac79e2e257d06e" + }, + { + "url": "/assets/game/manastorm/658-quarryoftears/collision-009.glb", + "checksum": "d317a5fb237d84ea089c9cb51210871b09f80f90be368bafc7999caee4b7d950" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/658-quarryoftears/navigation.glb", + "checksum": "4059475cc89b10d61729778113445c3e9835257d310bfa1f04be624a94b5d2eb", + "size": 3089960, + "triangleCount": 250921 + }, + "ramps": [ + { + "id": "stair-fallback-26", + "start": [ + 114.09618339957302, + 647.3031616210938, + 1337.4852181837 + ], + "end": [ + 113.61770464606124, + 649.3568115234375, + 1342.0376394282134 + ], + "width": 12.84281642875347 + } + ], + "candidateCount": 60, + "rejectedCount": 59 + }, + "halls-of-reflection": { + "collision": [ + { + "url": "/assets/game/manastorm/668-hallsofreflection/collision-000.glb", + "checksum": "f5147fe0f57d9a0aaf13977b379661eb13d30f07e501b25d951f632909403764" + }, + { + "url": "/assets/game/manastorm/668-hallsofreflection/collision-001.glb", + "checksum": "ba9bc990ead1ed7e924f2a06c6f3316d73ddab372383cc4bf88a439025b66231" + }, + { + "url": "/assets/game/manastorm/668-hallsofreflection/collision-002.glb", + "checksum": "50e13b85461bc87d8c24ed67015dc62aca044e29baca6ce49448f9546b24465e" + }, + { + "url": "/assets/game/manastorm/668-hallsofreflection/collision-003.glb", + "checksum": "a2355f0342be59b88b856a68b178299ab6d3ad46ef3e9dba0e6238b3deb99f89" + }, + { + "url": "/assets/game/manastorm/668-hallsofreflection/collision-004.glb", + "checksum": "0144681eb85a6cecaf6fb058c3e7a482d15e64f3ea57e809717d6db6d46028e1" + }, + { + "url": "/assets/game/manastorm/668-hallsofreflection/collision-005.glb", + "checksum": "49289aa60495200dbba85c04494b401348455c32a0af7cc3ad2ff6865c0fda2f" + }, + { + "url": "/assets/game/manastorm/668-hallsofreflection/collision-006.glb", + "checksum": "7266ca81445054d5b317df6ce6c02af322dfc0b976c184644a1b3fd0b81f0599" + }, + { + "url": "/assets/game/manastorm/668-hallsofreflection/collision-007.glb", + "checksum": "7d60aab6a971167973d57adb0f5baae36dbf59189f1ce06b939d164caf0197fe" + }, + { + "url": "/assets/game/manastorm/668-hallsofreflection/collision-008.glb", + "checksum": "94205540fcac6cc8f3fc844b261ba05a920a029a41ac940135df5fed05ed8bd6" + }, + { + "url": "/assets/game/manastorm/668-hallsofreflection/collision-009.glb", + "checksum": "5c9f5aff516fcf4df90f01922287c896c4174293992859a8aa9ac1e4fee3fc82" + }, + { + "url": "/assets/game/manastorm/668-hallsofreflection/collision-010.glb", + "checksum": "9d4d28d860c9f50f1defb5ed85a87f308f462c4e9a2d9b8d12b009cc12ecfa7b" + }, + { + "url": "/assets/game/manastorm/668-hallsofreflection/collision-011.glb", + "checksum": "30a1f8f153c05e51702c90e9b62d91e15b4d0a8d7ad7354758fed6630157f4b2" + }, + { + "url": "/assets/game/manastorm/668-hallsofreflection/collision-012.glb", + "checksum": "70de30385f5083c32a1414db03ba8f4f29518aae0c9222a9bac118956da28dbd" + }, + { + "url": "/assets/game/manastorm/668-hallsofreflection/collision-013.glb", + "checksum": "6f954d2bf7910173a3f85cf20987edec386934b1fcac0790cba13ab8e114cd48" + }, + { + "url": "/assets/game/manastorm/668-hallsofreflection/collision-014.glb", + "checksum": "96778438f975a33099055c8ee0345db6ababc7bcbd178815fd193323a4638810" + }, + { + "url": "/assets/game/manastorm/668-hallsofreflection/collision-015.glb", + "checksum": "6a3864059198cc03202efacb4c559111ca2b25840664cb0ca8cd918f4a98a144" + }, + { + "url": "/assets/game/manastorm/668-hallsofreflection/collision-016.glb", + "checksum": "cec357bfa6d3a6578c2efbfd2cfbba85ec1297f80c42680993c8904fa26420f1" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/668-hallsofreflection/navigation.glb", + "checksum": "920d9a4655d818ea0fdc56a76bc9d033113e6bc2ec10f543629c3a5db1412f7c", + "size": 5400024, + "triangleCount": 444731 + }, + "ramps": [ + { + "id": "stair-flight-10", + "start": [ + 2026.9702837136467, + 709.3038330078125, + 5282.1894489240885 + ], + "end": [ + 2024.1194822013497, + 707.7197265625, + 5284.11233882396 + ], + "width": 8.143138395199369 + }, + { + "id": "stair-flight-11", + "start": [ + 2032.5702422041265, + 709.3038330078125, + 5287.527374203206 + ], + "end": [ + 2031.29165222138, + 708.0852661132812, + 5289.741957015371 + ], + "width": 8.175948312280088 + }, + { + "id": "stair-flight-12", + "start": [ + 1374.3347473982462, + 195.19158935546875, + 4596.179974695523 + ], + "end": [ + 1374.3715999486815, + 196.58876037597656, + 4598.291255895957 + ], + "width": 4.407766649267177 + }, + { + "id": "stair-flight-15", + "start": [ + 1477.9715673302444, + 196.434326171875, + 4571.622822403348 + ], + "end": [ + 1477.8032630857629, + 195.69577026367188, + 4572.993550475704 + ], + "width": 4.402573801326771 + }, + { + "id": "stair-flight-17", + "start": [ + 1523.9417472343262, + 264.76019287109375, + 4688.512225278131 + ], + "end": [ + 1523.6584592485444, + 264.0216369628906, + 4689.844990465694 + ], + "width": 4.409123616091183 + }, + { + "id": "stair-flight-19", + "start": [ + 1477.8002243938831, + 195.7002716064453, + 4572.938772907716 + ], + "end": [ + 1477.6467306786262, + 194.78045654296875, + 4574.188878899284 + ], + "width": 4.40297719943921 + }, + { + "id": "stair-flight-20", + "start": [ + 1523.6672156832672, + 264.0261535644531, + 4689.805388418097 + ], + "end": [ + 1523.4059513790562, + 263.1063232421875, + 4691.034540330221 + ], + "width": 4.4093764685722086 + }, + { + "id": "stair-fallback-11", + "start": [ + 1981.0229461694182, + 708.6945190429688, + 5329.952236378906 + ], + "end": [ + 1980.3807951364795, + 709.3038330078125, + 5331.064474594088 + ], + "width": 8.34430095154541 + }, + { + "id": "stair-fallback-12", + "start": [ + 1982.341930296626, + 707.7197265625, + 5328.287217945267 + ], + "end": [ + 1981.2627689860428, + 708.6945190429688, + 5330.083243972616 + ], + "width": 8.453134263070314 + } + ], + "candidateCount": 35, + "rejectedCount": 26 + }, + "tinkertech-showdown": { + "collision": [ + { + "url": "/assets/game/manastorm/821-tinketechshowdown/collision.glb", + "checksum": "cb3159b1ffdf3137671838b00815741abae0a85f50ae82a5ba76d8733d500de6" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/821-tinketechshowdown/navigation.glb", + "checksum": "d11f2e5700c041c9580740eca39d85c026dab6f5689e4036d1b9d6ee0d73a1b9", + "size": 317488, + "triangleCount": 27408 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "frozen-reach": { + "collision": [ + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-000.glb", + "checksum": "6c6df953f0bb4618f963a6c769190df81e3b7d773915c17caca0c08f2d462a24" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-001.glb", + "checksum": "97c4ec4e71ae60afb022c0e64b17dc0b2025e13c5676f5e837d2f12f1e7ee885" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-002.glb", + "checksum": "7098f52a2c5a5ec630f336a7fc89b2d4895c1d642bfa18fda4da1c93fb68086f" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-003.glb", + "checksum": "08351edfa09791586fafcf978ed97c4931bb4880850fd92491e6ef5933c4de1e" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-004.glb", + "checksum": "e7a8a7769c3f658af45c909157fdb64ff27ef49fd75427253ba115042557c92e" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-005.glb", + "checksum": "edb5662d360f148bab0bab3aea5166350a4a15e1abc2b00d1cc0c3a092e52ccb" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-006.glb", + "checksum": "e9b68d3438deb67bfbac4fb4a0d13411acbe753b154ed0b603cf5f2e69482c71" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-007.glb", + "checksum": "9c4a1f37d5885a7bbc6993eae9ab2e5f276a7a10a8a3fcae3866c75c69e12c15" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-008.glb", + "checksum": "de90295a1d8a41fc6c7fa585bd2cc201dab6825cb9ee1f339dc640e9c2fd471e" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-009.glb", + "checksum": "0d14b55b1766e14ae8573641fbcc0df692f724ee313eeafefbf8b9549f5ecaac" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-010.glb", + "checksum": "0c1e79dc5f0c824da5057d9b58b9e129f145c73250890d7ff703955fcb3780e7" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-011.glb", + "checksum": "a8b76e2b0c14d12ee68ed8489265b99cc72268966944c109134b7d985ebb3a8f" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-012.glb", + "checksum": "90d8b162468ea1fe53ef6e8b4f0e9a17980d57ab0487e318d56b7ab67527bb9b" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-013.glb", + "checksum": "46e14fa81747bbf3a2d9d010d75836f7cd39deb70152ca9c72c2bcf84d9b7eb3" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-014.glb", + "checksum": "3baa396d57f9662b11b6b795bc91a87cb5c868a1b8aa5ec342b4751758efed20" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-015.glb", + "checksum": "a322d98a76eef9d5c00aafe26ea2c3ea6894b177280bc9421e4c5a4b170ae716" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-016.glb", + "checksum": "9276a227c84f434b6d2c19d746f5d537e03b9365f4cca4c297881615f5347702" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-017.glb", + "checksum": "28ba2af310c7d0d761e5e2c9837b4c04b4f1a7ec30e6b735215d3b984d44e523" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-018.glb", + "checksum": "d31935ec9fee94b64f2720ae45e3da204f87fbd9d00031277afb5c3c21cb4f9b" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-019.glb", + "checksum": "16219faccfedbf8630a5d295fa980c78c48a8966f1c2a86397b25b92663cd7f5" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-020.glb", + "checksum": "b81ac7d67c624be04fc74dbcd66a642712576d02708ee8ebe912ba0c14a39072" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-021.glb", + "checksum": "3922ec584999383783ad6ed768ad765d9aaa3d1d9ac56c91ee2924eb4e17b73a" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-022.glb", + "checksum": "ba03e6e3dc0bff7aaf425c530ffb0a9a23f6de3a9b6fa01b5353f279678b6336" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-023.glb", + "checksum": "f18f0981ad76de5c384979dedddd08e974965ea1959001215a9759a9edb586c5" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-024.glb", + "checksum": "e25127b0ef4170d78e78961999e7d0f7bfe9e0ce6b092cf906a29a3e16209d59" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-025.glb", + "checksum": "946d25b0301cef546d78502ed66e8b87c59095b6ab47b61b997c966be15676ca" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-026.glb", + "checksum": "699169b8ba6474315899fab7e5783cf47aa69f9604bc26447ad618239565df56" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-027.glb", + "checksum": "d927cd6de947b6443191058f2c3e6743706ef96f1633a5d1e43e1f6b43bd3f90" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-028.glb", + "checksum": "74ce217108c8f97c7914d5ca8493faa60520aca220d45ce1e7408224f02e79d3" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-029.glb", + "checksum": "60ed573cedddebf14e65977624ffac137485e9f1e5c249605139d8b6714206bf" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-030.glb", + "checksum": "1f177a3ba0ecea569ed0b53675dd0f2bdfd21db8eec3ce403464639571d37b46" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-031.glb", + "checksum": "bfc56d203e318054e2be2377aa7e477d33b28f3a82c28141160d7652439a26d9" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-032.glb", + "checksum": "5539aa6a8075e4f4c5bda24e3fb95cc166b08280f7858e3660ee90e9f45c481c" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-033.glb", + "checksum": "40a671315544bd76c04050a0561da862c4a848d15fc579883b3357756144165a" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-034.glb", + "checksum": "9ac7d7906e887619d176634c6b76ad6b80d3a5c6adc0b8fdea9d9ac11137cda6" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-035.glb", + "checksum": "ae8f69b5f23470421efb5b79f705027b7bea4eb1a49dcafc183854002a5621f6" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-036.glb", + "checksum": "46b1a3816f0122ae8abea28ace4a583e4920560b0439970721414186fc7065d1" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-037.glb", + "checksum": "faa6aae268ae734ebd1886b41d0e67033c98a08fbcb316690ace0de482e1ffdc" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-038.glb", + "checksum": "66e3e84ef2836da0f185e9183fdfe5acbad5a2f291f0ac3edf21bddb8d48b66a" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-039.glb", + "checksum": "2855c6984c27a86b49233cb8da87e858fe1a27e9787d0c82fec136847499f733" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-040.glb", + "checksum": "bac279ec1e55083beefec1d4a45552512ab184a23fa8765a4ee1184caba31e69" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-041.glb", + "checksum": "707b209cefb3a79ebec83a2d331645296e4e0c64c9996db0baf2d519d89a8f64" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-042.glb", + "checksum": "9b64e6c2fb06b41b5c14537b9b2cbc1180172c485cb18fe7d8bac23559574689" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-043.glb", + "checksum": "90f72dc3ecb087ad12b3794816860d3b6b4170bb327b302833174f28572bae3b" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-044.glb", + "checksum": "e22e54d31768ce384e41dc60cf64eb0bc269d7dd5823b59bc34f97b79ee9e975" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-045.glb", + "checksum": "bd3c3ca49c1e700e961526b51e4c8340ded8684df88a92fd425d85d07b1ee40d" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-046.glb", + "checksum": "f0203be353545b3b2bd0bcaf2c354122abb677411be578a30657f373a92583ff" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-047.glb", + "checksum": "f48e98bfb2c589f0155e84714bc18cbd30439c79a89466d041e6aca8bb7a8a8e" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-048.glb", + "checksum": "0df593ea787443641aa254ab844db3000b35d51ba7169e857e148aadc9a5a248" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-049.glb", + "checksum": "490673b92c9bf8b4a645fa6af8b73fbb0973149e08505c35f2784076c4ad9e1a" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-050.glb", + "checksum": "f22d17610e28a127f136da50efee350a107868cf74d60ca8a0af53ebf2538ea3" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-051.glb", + "checksum": "e22206d0fdadc6466025e0e46ab4b86674ad533aaa4705bca5061d83b123ea45" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-052.glb", + "checksum": "3e9c43282c1a98370ccdda331fbff6ae0f755fc92b7a1e20a80bdd7d4b4d3391" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-053.glb", + "checksum": "025459483d6e34fd2cfbf5b702aa2e64566d8836846e3a8bc8e38d3fc4e4e5d5" + }, + { + "url": "/assets/game/manastorm/908-alvaencounter/collision-054.glb", + "checksum": "1b67bf7d152acf8b41d21c8ba42c1da99d916be2c3682a1c5bca37a471ab5895" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/908-alvaencounter/navigation.glb", + "checksum": "53747d3e8715e855beacd87a07d7d9f765fc493708221dda6dba5b8c29bff9b3", + "size": 2873512, + "triangleCount": 243342 + }, + "ramps": [], + "candidateCount": 0, + "rejectedCount": 0 + }, + "forgotten-mine": { + "collision": [ + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-000.glb", + "checksum": "9cd229c8887628d6652ce776c3d8b69dd9b076aefe1f26c46bc736e0385540ca" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-001.glb", + "checksum": "81e8ceedadae432ad657b2b8a31a4bc4b6b01c48b28bc3920679d949807f203c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-002.glb", + "checksum": "7bd7d313372f4650a34f01e922a2aa675e1af7ac774ff1d9ea99afac1e639e64" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-003.glb", + "checksum": "5e3a4988c89402c7f7ba8b6c848abe748e27787c561c3e2db8e4541f1779c430" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-004.glb", + "checksum": "ee94acc527729af4602782754ef38e61d1e13f0c9f7b1bf9757cb5ae39ba2427" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-005.glb", + "checksum": "e2a9462a9d1f65fc9507ca235ba447a7b95d36a488c76b22a8210245ea427706" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-006.glb", + "checksum": "8deab6290ad0c8969ee8485b067f2d6df1808f7964d37c08823f805976034aa4" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-007.glb", + "checksum": "0046d75b502fb0b2c632f70152131d5c6580700e50bf048cbbbe20e04bcedc86" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-008.glb", + "checksum": "d3904f0c181eaee38b196248fe7c78360e029649b92f9917685a9206f7a6cff8" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-009.glb", + "checksum": "246117b036b280e4d1ddaf08b78649e099e55fa6f0831bd93d83ca8756f4e286" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-010.glb", + "checksum": "1f23fbb3f70b2bdb36155c920afe126d3299f971a688bf66a10313fed8008516" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-011.glb", + "checksum": "9e8047b2bb9df8c2e05669265e6a7303c301f86962c1386285dd29c2883d6ec7" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-012.glb", + "checksum": "6e728cc328bb667d0a41726fc1e3f440226f880be4ad8938b0f397cfe175da33" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-013.glb", + "checksum": "3d0ff70a898425e4c5a4e1606bab68f7ae0b964a39d779b97a0c823c9e453f20" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-014.glb", + "checksum": "81e8e21e8df712254c05361f143202590db80fc7760df8b1d0491ffa0b9fd9f1" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-015.glb", + "checksum": "e40178c75c02adad2c44f4e9ece8139a9c0e3bde36f936c95967d4340f33a567" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-016.glb", + "checksum": "a3040a1fc5084a80f8ea3e659c9168041140c291730b7066addd92cdb2edd80f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-017.glb", + "checksum": "6213883663eaf3985f4960d189cfc8e8edc37e3c69f2bc101edfa21daf8057f0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-018.glb", + "checksum": "7481a21f2157197cbb7019b120df6637ad2a6ab585a429c72c1b3a6bd62104c8" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-019.glb", + "checksum": "bd4d87ff0fc6966930b30678542e0fcbd5f2f4e028d9b3705b90fd8722aad298" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-020.glb", + "checksum": "a22f217d202ffdda6f1ba21ac6deaddae522bb579beaecd2fff2726206b45031" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-021.glb", + "checksum": "121114c4e28267b31d41aaed21a60cd8d3b4686916e8b2d76dbd3883c6e52664" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-022.glb", + "checksum": "038caf1c4d5fdd20450668a716b80ff9b112a873f1c04c678124ad692e554dbd" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-023.glb", + "checksum": "0fc24aadb753fd87aa288843b334e29044eba7991d29a2e2c169a5ef2ba333d1" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-024.glb", + "checksum": "6af35d6e0e9c9c381d349128512d5e8595883083b8d0c2ff59a647760a79449f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-025.glb", + "checksum": "76237cd0d8c62804d9243e9a0238ef29a8ce625ce623486de1c17c3ee5feed31" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-026.glb", + "checksum": "0e24f2ed9555c4d5376994bea7f46e400e689aaacc1cee83a346645fadf3414b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-027.glb", + "checksum": "db2330d391f392531151b10cd89a5240690d8a8cd184166fb82deab077b4c68c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-028.glb", + "checksum": "e02b0fb8d4176e1dcfee76d9a677bbb89a591da64e369808e90b94b99d1000d9" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-029.glb", + "checksum": "1eb94c699c3ef5af74d7a2ad7ae1d1ff55f1a0a2e3bc5c4f026b38b6b7506851" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-030.glb", + "checksum": "1246025b514d4e518b9e5af8821f48ee888d605071068895e92d275602e662a9" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-031.glb", + "checksum": "2aaba158e06c2c8abe42f389536360daed792a97f05174a5561e8fc821ce1579" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-032.glb", + "checksum": "9fba04022a650de69e32d26ea9ee2594c7776e4767432be77d5b9a360b4229ae" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-033.glb", + "checksum": "d622e7b077b82675ac387eae1753abf2fed971315bb04259c2e30fd38f4151aa" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-034.glb", + "checksum": "480a7eb95a415311579d21e6f4f00533dcdd6d9daa557d85a6b17605c126008e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-035.glb", + "checksum": "bb57e0e8fd20368f11dc42555b041625b4f59c8266fea6cdf14dc4d26c00ae37" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-036.glb", + "checksum": "e9caba1d8ef4fbed7ce1a4e4624c9fabf53faac3a876f453b67209eacd9732aa" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-037.glb", + "checksum": "901bdb462412a0986fda103db531f9891d3e5f283d5f4367c17770854014a2c9" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-038.glb", + "checksum": "0da7c57037b8da96d3efdc48190290ec83e6e1de90a6a795d4bff7b21213bfb6" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-039.glb", + "checksum": "569e2212a340026905ca61cf290e78196a34d5efe962482ace21508d3d10d473" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-040.glb", + "checksum": "6f9765da7daacae0e8cd73697f958c70f12fb2e4308f9263f13c9a5648018e43" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-041.glb", + "checksum": "e934e2bffb09029c0e80d09359c93579b77cf46e2eda60d849abfa5f1fe97b26" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-042.glb", + "checksum": "b86cb3bdf447eb01d92b1d690aa79b69f8d0c21733625c9e88bb17ee35e90ad5" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-043.glb", + "checksum": "bfa3ac43200cb6a7394daaac8a75e90262674d186eb3f710a722ce3081fc7580" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-044.glb", + "checksum": "7aaea1d42d2059a49b6e3b4b6199d9ecb1e145b81899764706fc8262c1c91dbe" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-045.glb", + "checksum": "ea4ad076dbcb6320b4230c7218c669ed2599ed23e52831b735009951de3b9ec9" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-046.glb", + "checksum": "5ae0316a15a40a3a73c1f866ecff958949845e354ad95b02ba6fbe739812fb8d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-047.glb", + "checksum": "5c1c86fa8e8603ad442c167e053f11a87df982be6014a04a56f946b6ee2d57a1" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-048.glb", + "checksum": "8c861e34674d93da4856a57286353bc12eb964ecad0de31483c97f2317fa5a18" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-049.glb", + "checksum": "a0abb2a40d35e6b782bf03859049af786a2465afbc81172ce1e7a612e11b8f64" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-050.glb", + "checksum": "ece9e6db7a25a722119338a721006e700295f5e2fb0f666ec31448c69f7ed6c0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-051.glb", + "checksum": "a6e136f320087a89731853d8e60e41ef763be17a3f839a5208025a021d805845" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-052.glb", + "checksum": "b3068797232d66850407d4d84fc8f6b10a30274e922f32ca82f2b3fd919bc7ef" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-053.glb", + "checksum": "80ae5e9f1862fae7d64c9947d8e461039bf922a1d9a52e692376f4c0ddd0e667" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-054.glb", + "checksum": "009f33c961f87f4ef57764e13865f003bc9fc9c473d6c58c3f5c0c25768ff32e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-055.glb", + "checksum": "3b85b4e327f89bc3baf398cddec289baf43d20e94ed36bc03aba297038447286" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-056.glb", + "checksum": "d17ceeea0026d3c0ebac66bf80528b7e96d7c5d21f6ca5cb8fb007f34d79e1ed" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-057.glb", + "checksum": "029f62d0de0bcbf2065f4c3fc17919988a07780ae1e8054f98ef4769c1864c58" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-058.glb", + "checksum": "e6d8154863f7e6b6580545bfaace542b33852d579e7f676bec5655f8d7042ffa" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-059.glb", + "checksum": "0f9fd26d19af27611a7cc4b37b2d08ccd2da6b45b2d7423e20fc96467e3d6e26" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-060.glb", + "checksum": "c91509a6fea8da434304153f5540ea21606c25572baa373c7a7e60b12cce8358" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-061.glb", + "checksum": "7cbfc9011d4d0210775af70a6f6eb9a93061bbda8fd8ef1400ecb17bb58bd812" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-062.glb", + "checksum": "eca547bd7ba1c8f7c25e79738483e4dc4192efacbe701c60e8159a3e45f002f2" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-063.glb", + "checksum": "a39f858bd65540306b8a4f416a5abc301c0cb02e56cb4d91d51a29fafba76edb" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-064.glb", + "checksum": "3cb2b5ffa94aa94ae1d458ff60f3cdbcbd79f51c950b7bb1dc6ec17bf902ec24" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-065.glb", + "checksum": "22ec4844a77442e49be79730c151eeb21d6a43b88ded566a28bfa0c2c444e854" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-066.glb", + "checksum": "b5be35385ed756f7189d37c6eea89f265422762d49e5b5dcbbd89c915401cfc0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-067.glb", + "checksum": "a78d9429a80ec3dc10e13ba8ca10a028a10a93157d12b33a7581466a399d34f9" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-068.glb", + "checksum": "6958846ef95b308b5907b9e7f278e86fd9ab9d8fe288b8dadf8bbf4e03dc1294" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-069.glb", + "checksum": "cd48dc551abc0a87d07d8ffed55f3310346e2d4e42e1a77e08c44dcdc2d901ce" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-070.glb", + "checksum": "8173004ca447eb85edf653c5a724e3a90a9927af66c2889125114afe0b3ec899" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-071.glb", + "checksum": "afa5ab6e77baab2dc9c57b0f5012d70e215072bd3548a15141d46af31fe9a6c9" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-072.glb", + "checksum": "3a128f20720e5a7ffc6cf3a4d30b3672975b40f7dc63b967d3e673dca725190b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-073.glb", + "checksum": "81445baffeddb9aae6a45ee214bb41ec36a8eca829cd7f45f9b15070f22a967d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-074.glb", + "checksum": "c58545e86f9609599a797d3d97c81b5a0b0202b33a2b8d35fa4acf22fececf91" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-075.glb", + "checksum": "f93f3b4660e17082da85aeb84d2bed51398000c079212f981c6c59e16adfb60b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-076.glb", + "checksum": "bb8de2ec71fed15a8ad271e4d41344348fd57c0980ad53d175d8f063d7fe484c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-077.glb", + "checksum": "abd3df25c12698f64de44f121ed715f74a07437903e914a29d5020e395e60448" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-078.glb", + "checksum": "db4ec0a06cafe51256fe99dda375d3243d78c4b16c6046c80812687634f8d328" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-079.glb", + "checksum": "9d29aadfc906be490a5154382136a78ef5e8e0d9017e59ebe481ee61e98f9e94" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-080.glb", + "checksum": "3c81dfa4fe812c0386e31402eeef3b8e334f72691ecc9491618d753d36603311" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-081.glb", + "checksum": "226f50e8429dd879a417528795daa978b4981862bb3c16c80b9e36bc2403f011" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-082.glb", + "checksum": "0151b7f895ac1007f369c90327ebe6d9e6f6d122ff81b15eab2c400002707868" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-083.glb", + "checksum": "6092f5b8ba70957f3106f57df1d50888779076fde19ed69b409c786dd2a30ae6" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-084.glb", + "checksum": "1b89682007e7c4310beef74e545b314d5f4099fc0cc84476ff0c74c22e579c30" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-085.glb", + "checksum": "398bead92d98cfa45d39d1069e29aae1a6f3bbe1522b244dbf5be4be80f6afe2" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-086.glb", + "checksum": "3994808dd08dc44eebf26685ccaa8ad8644ae4596689cb3979a9581b72ed92c7" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-087.glb", + "checksum": "1be66b925ecdecc3ade23fa52f8de8cd5e597f35bd85537ee56b5b070175efdd" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-088.glb", + "checksum": "1c050dc4426259333a2afca25d23906065d9b9dd9e85681fd8d3bb2fae4b8265" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-089.glb", + "checksum": "3fad0136e23c78eaa56f7ffc10418adcdec3ded1aa487d023fcea323e35b9397" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-090.glb", + "checksum": "58ef38b3fdf6e4572a0d45125215d4af7c895c98bfee8b2fcf114095c29f113c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-091.glb", + "checksum": "1c0b9282d2850a0adea7a792ed8ad3f32a38912c04d62244f0273713ca092554" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-092.glb", + "checksum": "056889e7c3b1b645e22742549bc59f86eba1e404b15d964afcfabea035e191e0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-093.glb", + "checksum": "0c292e993a939b299f5dcd7482e3faaf0d4bcccccd2842e442625c93947c9651" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-094.glb", + "checksum": "34c395195d8580144d9d2c5f64a0700e86c63109bac25f34371f12754a5a784b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-095.glb", + "checksum": "01dc7d729e363c1fcb9d134187d3dc45db66f7dc664f42f572aaa29995729cc1" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-096.glb", + "checksum": "ab21833cfd17d899168bac35ab6d51f31ad35d8bb002526056b1e48c162c2549" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-097.glb", + "checksum": "b98e3c576d3e31e1eacb76701e5c0e66023935dd7942a685072f4f7a5d3e56ee" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-098.glb", + "checksum": "9ea8f1cbd2316c707d8d65fd51750a40e911551146dceb54beebfef83fb51d12" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-099.glb", + "checksum": "89d8d24100957e2125ea7390432e1518f1c67dd54c9317802ae7bdd7dc074d21" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-100.glb", + "checksum": "73435d9e361ecdbb170c69759704c7c021a6de70a41c2d5ec7a166a3a73aaf99" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-101.glb", + "checksum": "f5bfe72da12579a0f4a19f28a2639bb3c201451204cf4eebde0e1266fb5c80ab" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-102.glb", + "checksum": "c4e3e1f088f2c767ea193bca939e95f3918b86d3f8060689f5a6b0ff1d7ddbc5" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-103.glb", + "checksum": "ba9f579238d8fd71d1d238b5761cb1aa22f4657b3d8cae4dc35cbb755dc6eb18" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-104.glb", + "checksum": "5bd4f39880f58021e0031c7cdf7a861ca484e026a515e44a310a17a2868f026a" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-105.glb", + "checksum": "73761c8de431f6e13c442945503becdcc71ba93d4e5c1ecf23dd6cad896baa75" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-106.glb", + "checksum": "8af5c307edd94b8a28c4ef6ae8dc3f4ffe683c5111e0a172b72350ad33e2ece6" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-107.glb", + "checksum": "cc98206cd4994ef172227477800b9280ec4fa994b27fad38b9226f11e0b0c354" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-108.glb", + "checksum": "891c797fd5e7c6910921a644411cd650717ad37f55c8ea315f9186bd90913510" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-109.glb", + "checksum": "3129384b62d6ff49c4d1bdaa6b4072f1390747b844b4a5e44d09c685b4436173" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-110.glb", + "checksum": "d22a3abd2ba8c077f6b38ea91cc41537fd9b1053ef797a54945ca131a8ec181a" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-111.glb", + "checksum": "fbcd82a2dba23cc74ff905f0bd07e8f99b347c86609c769636c3dd64b62344ff" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-112.glb", + "checksum": "395aa673aebe944f91ea5f6960f588d83a8a46d90428c060e629b76951dcba86" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-113.glb", + "checksum": "f975bd7c195c6b337415cc7dec819b79c09fec1462905cc3154a71680d3576f3" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-114.glb", + "checksum": "de72ddac53d07c59903266dee7545f0fcf7d2c15876eef53e60b12a1803e1ecb" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-115.glb", + "checksum": "a8a5a2f0f51fd085410a9b03fac576c8bcaf5eb8b0a909a1d49d5f9a3e15968f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-116.glb", + "checksum": "86f768f4acb2c61c1ec4f6087379066bd655128a049855e22f93474b62893dd9" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-117.glb", + "checksum": "fb86571fa9d4144b9c7ff4b5247ed7ef655672c23329fcbc26e31181f16582fb" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-118.glb", + "checksum": "b65ca81a86b1ebeca5a78f6aeaa4325f99b20084c6c862484604bcbead13b3b7" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-119.glb", + "checksum": "39748d31edb59e22bde58a9e9133635ca359a7f74a38cf110059a36537d1e992" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-120.glb", + "checksum": "88dfaf493af2dd5cd5a67d26d129e03b5041e07d3ceb093aaab68b9ea7defc18" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-121.glb", + "checksum": "72cffda52df9c6d6fe8edc0b218c05a00b869841ffd713967dda64597341bbd4" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-122.glb", + "checksum": "c058ad28143b060d52e1c6309bced98a560e141277c1035629b72df69ef4de68" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-123.glb", + "checksum": "a783a955a40e8bf1cd59f8ca04e388cac9998b0050dac1b9196abb6a84527e76" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-124.glb", + "checksum": "6b581982a3aded38f3606cde7e6e5d6eaff81a9ecbc96cc8a6d5f7b49b5eab22" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-125.glb", + "checksum": "3dc184f32b3344a9773828a2de1cdbd861a6088881097ba9492ec226408f6e6f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-126.glb", + "checksum": "fbad4245eb3c739cebb09725963bb9c23913296048d2033a5bacaae9441313ec" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-127.glb", + "checksum": "f047f1ac2b02bc0fb2bc718d391eeea01efcc9e489fa26e733f716de4c82f087" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-128.glb", + "checksum": "6f7b75a180d4795b1654e0b7d006749008e81b545467a09ce12859ab691781ec" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-129.glb", + "checksum": "48c1900456d71f058f1045396e75ded01c02ff37efdccd59c2bbcc7a0ab5d4d6" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-130.glb", + "checksum": "28ca760e3379aa7e8c5ff8bc9ab401829905f9079e164f3cb25b64b7ca1bf919" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-131.glb", + "checksum": "2d4e4981dfd15c9a5369e39f7b811f9efcb0f9c081e7304d7b5c66b4873ff74d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-132.glb", + "checksum": "c010be5462edb29bf891a4072b4666f2e17ed6de2bed4227dad5d99840bbd40c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-133.glb", + "checksum": "dc499df3b84950c0af2283eb985d4e7e1b247d2e9209b5b906e10252070aa5b7" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-134.glb", + "checksum": "a49757576328497d39da46548f854e0a6a9c0fe1079063f9263a23ee140d69ba" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-135.glb", + "checksum": "98731e26671b82838989ffbc6ddb8ca301207eff51e57e995a8240ff9bebb627" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-136.glb", + "checksum": "380371300631ce4ebba2371c4cddaad3032eee2085fc1cc40107145d337fa229" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-137.glb", + "checksum": "ebde3fd093f8741c5b4ef977a456cf18d22f724aae30bfe7e01206438b9418a5" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-138.glb", + "checksum": "b4e64b9bc81312f9ec7711112159e0c1d9bdb898041d3ab187ff96d3bab15be9" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-139.glb", + "checksum": "88c086deeaf4f3114db278d29d23c6299ff6ca0c6449d2481237f33acb11e04c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-140.glb", + "checksum": "0c6d4adfce03e9af846a6542640a78ea220b8a80cfe79054ad42cce20b7a5617" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-141.glb", + "checksum": "30986a77f26c59034f991a9f98b0846d89ffbe6164cc00e49a86aa9d7d486ba2" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-142.glb", + "checksum": "b9b4dd01a2776cde3ec1a06b982650bdec1a55b251129bd49b47aa23f432690c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-143.glb", + "checksum": "7682d39cdd84be9ad06141dae2fa5c01e557e036c9752930a886a5a8c9653a88" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-144.glb", + "checksum": "7d0a31f616d7725eb78e6ef4525b1f5257af02551ef598a770818ca775275b1a" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-145.glb", + "checksum": "4f905d7ebeb716d7757c10a6df582430c2e01649a04f9f6f83d89c43f0f5753f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-146.glb", + "checksum": "4e6df24025559f8b1dd52a8216e7430fdfffca0f5e951fd8416ebc2c8d018070" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-147.glb", + "checksum": "f4b8292604f8af220812534116341438c8a67cb4127e879887588b84fe23e465" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-148.glb", + "checksum": "5ce210dbb63666b97b63d096a5591e61d27a77bc1a2c96adf5686f0d0eb7012c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-149.glb", + "checksum": "28f5b15b7ae6eb1bfc40371a8cdd86210b0f322217979321a8eb74a44c6b038d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-150.glb", + "checksum": "fcf563d450b88f5d1eceb562bb16bd6a7bc752029c0502718446c7635251df43" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-151.glb", + "checksum": "4bd74987d0d63360600dbccc2848b5c57eaf993d87c3a7f3254e5442fe0955a5" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-152.glb", + "checksum": "a6bb6bad2705197c3ec1deb0970bda5f83096437cd63897a800c4a4a0a15f007" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-153.glb", + "checksum": "281710bb2b0053cd1c374aec03bff63624b9f6fc34039376c062c555551d4a51" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-154.glb", + "checksum": "0473fb3999a0e894393a9085c897fc3b6a2c2fa61a6dab55110c05c3867ca7ee" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-155.glb", + "checksum": "f7f3b1f234d221453884a269bc6a288da8ba39ccda1ba5ec858c705bc5fa5561" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-156.glb", + "checksum": "ce1dd0e735969aaea866c961c7c2993ef59b241d5289d4e1091d7a3267ba1fca" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-157.glb", + "checksum": "90b6790ae8d4ae803465512c812f61f0ea80cc79fd8f5156870132a8872d7fa3" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-158.glb", + "checksum": "eb31abbfb8786f230a902d2610b58275f2f12f39d26e8ff6b7ce865b18732f15" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-159.glb", + "checksum": "6e8348c03eb1c712fee08870f75fa701499d343f67ce79acd94dc0b6d01c2fcb" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-160.glb", + "checksum": "faebf74897f9f4e03e6af8153469e2f0d9ad014a46fc67074433678eebda7d21" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-161.glb", + "checksum": "748c2862a072a5f75710b95b1d118062c3e63b9bad589f0326ee23577271280c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-162.glb", + "checksum": "1fc17f2c40b00f99f0442153cc711e84d3042346915991eca560aeec36a9cac2" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-163.glb", + "checksum": "9d617d17cb10ed7489563a5626711f4d21ac4a4e7210fb0957b6b2f38ae3cfbe" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-164.glb", + "checksum": "9ac75d59999433f3572015fc551c0bc2395a12800d4aac84628c2d2fde2c27c6" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-165.glb", + "checksum": "9d51b3db3f8f201cef8433c39fdf601fd8e5f14b48ea34b48e6fd0574758c481" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-166.glb", + "checksum": "1a58c8878a8dd30660ee6c9094d6c2dd40bfd92fed968c73c0baf9a35d1355fb" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-167.glb", + "checksum": "20c7c6912638afb6e66a22c818758f22fe86fb8bd69b1829a38b09662e6db2f7" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-168.glb", + "checksum": "407398ee35a4dbe5158c07571109529519ee7e963b0a2e6e482c5a50ddebe263" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-169.glb", + "checksum": "97ee34bc43de7d5a3e2b7e5fc6a7f1fe534f4e2a29b4ffdfa62ab357b28b336c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-170.glb", + "checksum": "285a5c9bf3d45c369b284dd8623afd0d0570c3730a05d977b042527c43085aa8" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-171.glb", + "checksum": "5dde99c13e673d7ba1172d6c770fdbc0a1929e7ab6fbc5a45a1118df2aca03d5" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-172.glb", + "checksum": "61b507df7a4024f72f8cfa8e6a7d28f371e7cf63c6cf66c76c57079a1bb3eb62" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-173.glb", + "checksum": "c033d9705fffb77d7d543cd8c6ab90f6d7e04ea3d604601484b38b2781493893" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-174.glb", + "checksum": "3e0ce597fb41c586f0c808cbeadba6306410211a67311fd4f658f0c6687ed856" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-175.glb", + "checksum": "1f6db28869552b7ca10a20e23eb0c34d0eca2d4e176d8afff44b98c55f2f4252" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-176.glb", + "checksum": "6054f14a1c5db02da6bded19a62958ce67a532699e41abc30d82d5ad5fba1dfa" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-177.glb", + "checksum": "8cba5254f7f8145a6950a923c006ce34b3d76642677dec1b4ce85f614ec599b1" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-178.glb", + "checksum": "6b54cdaf40782775e41eb88e80b1c7410c5225f5f9b9bd3c27fc9217c4290144" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-179.glb", + "checksum": "c072220c78db67e9546ad62a2dfa469532b61c20cab6f8a653f2b2d21bd08d08" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-180.glb", + "checksum": "a7c3dcff359b37078ec25181fd239dcc9fd0be507f102298b81766af88e7c33c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-181.glb", + "checksum": "4397e447757358a9d89187eef62bc0b0af24c1037d52cf5b8436b4e4b3c86492" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-182.glb", + "checksum": "ef06b0558cca185fd1cf12189100102df05f2783076089d3f65aa4f1c66aa98f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-183.glb", + "checksum": "9878d54506481c9b4805c310c75a1a5d09028eb1c707945f0bc6fcbcb24b63bd" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-184.glb", + "checksum": "d3d5b3fcba563ced57aff328b02f4c1350256cd02c9cfd440d336331c91b9431" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-185.glb", + "checksum": "08dc69a5e8b9e9d0af21cf058dd6ef4bd87fc6005a76ba54e4b441eb72af23a8" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-186.glb", + "checksum": "493fffa2691b3e731ea7dd2509b3df739e0c8c66a54aef1e5f72438482e589d9" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-187.glb", + "checksum": "c84c71ee8c6a7834b6642db6fad91c04fec0f2cdd7513c4109109e545085e15c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-188.glb", + "checksum": "6bfc51742faf3b53565be9a485fe592d9fd2babbc7c060a78060434f004b9a90" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-189.glb", + "checksum": "6872dd0e65dcd527cc6a21661d7f4e051b8899dad9a10a163248e562e64c7258" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-190.glb", + "checksum": "7f0441086288c1190c276b395b93e2bf0ba33b10e3c7fa8f67615845d5b791c0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-191.glb", + "checksum": "844516cce0c071eb5284d9d1df07fc11ca593364c12b54e91dd1bf748a0a8934" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-192.glb", + "checksum": "a0d58087fd18df8b9107f71897cd720cca74eb6c508149de19b5900141c7bcef" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-193.glb", + "checksum": "11ecee417cd578c93cad49ad0eeb178a271ca6f55fcb6a207c01b99536597535" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-194.glb", + "checksum": "f411ab8471ddf8108a04682870f1f5db8be89390527584b052e9458c2ba1fc96" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-195.glb", + "checksum": "874f25f920a9e463a284b1f61b4b604c50adb212410e0cfe9e323c3c28772fae" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-196.glb", + "checksum": "7b7de906cf327065035bed35e924e31dbba674311a88d8f4ca91f7f81c25b2cf" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-197.glb", + "checksum": "d6bbcbeb4dae038045f043c369547d62994df91528b25da18cc8d8b01b9b2202" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-198.glb", + "checksum": "feb3da8468bfdf7d5bd668e96c07b6ebd0dc57596de62a6d7d66fed970a78d13" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-199.glb", + "checksum": "8957001fa832c6a33123d1259144d61a39cebb88b11d180055dd0ef24d481c31" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-200.glb", + "checksum": "f706e7c62658c839ce3fb78f6bc400f9eff046428624393415275fa08ae1b528" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-201.glb", + "checksum": "d3e93b75162e783c8ecb4b32f5f37137a6f75a41bb10275b3162af5bfad65f03" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-202.glb", + "checksum": "020e5e015ff16ec03304fa1bc7c50c8bfd82f6cfc9afa7b3f34e255ae57506df" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-203.glb", + "checksum": "7f2ae8b27e02e1e15648fb832f6d29d51d25f9d9d5335ded25ad60ce588c5f41" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-204.glb", + "checksum": "4da57dc9bafbc60571388f1879ffcad4709833ad4ff0d0c8787d5e417a56e0b0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-205.glb", + "checksum": "0079f988c758a26a45274f82183e2b8a1397d3af0e4a1a0df061183ea4ccee5d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-206.glb", + "checksum": "4bb2b1a5d54428a4d9e592a3e5c98b73c4389fe82f67bb4e0aab9b849d73c702" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-207.glb", + "checksum": "fdcbe8936d227f3bef4013769ff8e5954287572e9da4b8ef443e5406a9e3583e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-208.glb", + "checksum": "825e79ceb3616862cba7c200b0960496035b9337323c6cfdc53ee28901201b0f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-209.glb", + "checksum": "ada470eb24e2bf63410954539ee8dbfa7520ee2516b95b757b0f22bd8ec157d7" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-210.glb", + "checksum": "ec7b1a4474865f0468b44015f29bb1d89550f014e0029dcd1d6dd20e3212fba9" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-211.glb", + "checksum": "12e5014a34c8d0e2dbc9a00e1870c11f71c64e1152ab0df3086b2f118237320f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-212.glb", + "checksum": "ff8498e4fb628d202e587e35e1e735e901776946ead6747e526baa0aa811de43" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-213.glb", + "checksum": "72ece23874d1d7a105582513ebf2eb244dc3e5de299cdcf81dfcd5a938ddf071" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-214.glb", + "checksum": "158b487072b2b980e871ebdfd145b390be5cf9353f561f9a0a83cfd6bf40378d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-215.glb", + "checksum": "057da5113b6944e6851aa3525603e24fa43a78fb7f157f28352bc01ae2c70904" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-216.glb", + "checksum": "6042641fd3e214295d9eb62b2054112925e199f8755407bb76348073af175b84" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-217.glb", + "checksum": "96b6bb20adb930067f85aa38a67753009f54783399e04cc05df7dc91fa24fced" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-218.glb", + "checksum": "066f6f48b044e30e1e0948983143ab7c0021d65194cb796c49e2bc0bbfec3297" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-219.glb", + "checksum": "0bc2495d5929e2a3fd4660d10951e199f94f5341b24bbc9ee999e57ab07d3e25" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-220.glb", + "checksum": "f3b28e3a92fb9069b3f6ceaef467c4a778bd141169d593e1f19bc9e535076690" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-221.glb", + "checksum": "0b70bf4f99181f3bba0a1eb3391d5b1ac42f9cd28f120134ff79874da8e48da3" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-222.glb", + "checksum": "ee7ac580d93440a06270c14869e4682d502a00fa3cfba192d1851057c4d293dd" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-223.glb", + "checksum": "dee6f35d883ca5524dfd7e3d7cf20efff7a3c66d139a0252a0576a6b130b2879" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-224.glb", + "checksum": "c3f624c2c21db6380bd04f33a7bf26fc6c2d64a3bfba4dc0d38ca6a9af06dd40" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-225.glb", + "checksum": "5ab16959ace65e246162954da9281c491c5eb1ae98a1340035abf7a17d351fc5" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-226.glb", + "checksum": "a0a1a96c311530da70d74bba8cae21a80dccb3298c0c0a3510114cecd95fb481" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-227.glb", + "checksum": "c71dc3833cde1a4110ddad3e522e35a8443cf40c92bc3109af3e957856206da8" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-228.glb", + "checksum": "02fc7c08fd6df98a0715a38cf6087a1dff1beefa94062e3ea2c4e36446a5fd36" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-229.glb", + "checksum": "2b614d0437e8f018dfac24e5d73e502e76875705c1584255d17e1c200f375d87" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-230.glb", + "checksum": "d798134d4c7715e23a03fa4acfb3b52782b4ae1d36612fec71e61dfad42fd85a" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-231.glb", + "checksum": "cc2c8fde1447d69d1ea6f97fb6f0316fd15da6166257cae40dd3100243635675" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-232.glb", + "checksum": "de4617e7e75e5a04042fb6671583bcdcbe105c24316b451d265a79e4d73b7e0e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-233.glb", + "checksum": "4a2fcbbc5897f63aa96a2699f303e6205c56be012e2c70a5819e56acc3b30403" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-234.glb", + "checksum": "c051f9626e826f701934c4a4afc9d2d8540e23c5e1a40807f1b69cbed7d6a130" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-235.glb", + "checksum": "799e5f85dba842477379ffa4fc96bece7bea492dd8d168598052894ae5907804" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-236.glb", + "checksum": "af7e00614396cf74755ec7879535e9193a4ebd91d0e2c58c8c5c90214c791528" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-237.glb", + "checksum": "7a211164e0390e7fb45784ce6c718aa89fadcbec6eaafb2c4bf4397f2b1fd349" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-238.glb", + "checksum": "32c1c0222af4a39b864b0181c119b3cb1d4531d07a89a679b7c41acec7783485" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-239.glb", + "checksum": "57cf9f57ee514489aefe8d4153bb33166fd12829a9a5e70a09d9205a5f653b78" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-240.glb", + "checksum": "3fc704b2b24dc968cad02604d73fac428fe36c92a662413d30d81ca31b4ea6be" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-241.glb", + "checksum": "100fcbce51199a709a45d157d703ce45a6d88020b60ada83873ecadee12019f0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-242.glb", + "checksum": "053ed2ff6f36087ad5055d5a59e5b0e1d2b795f7f16bf0a6880be699a934da30" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-243.glb", + "checksum": "07de16e9b00455c29c899f510e53fcedb819f2aacd3a8bbc1a424b8441bc10d8" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-244.glb", + "checksum": "29921d02b10f706bf2e8bc0b9a332a7bf387dd7b382551dd9e911cc17291628d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-245.glb", + "checksum": "7a2c3144ec886694c8075159148452f3c4edabe7b10a671afaa56aaa1f73f8b2" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-246.glb", + "checksum": "ab163512bb74505aa64aa63d1f9af0e7633ceea70e6486fb444f1c9fa99084d3" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-247.glb", + "checksum": "a46fe0a53934238231c6690e4e685cdb9ea55dfe7f6e9b169a247fbeb6004704" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-248.glb", + "checksum": "1fbd82ecd6793bbb8a82acd9153d5c942ec4f1c450039dd7c4d55a4987c7909c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-249.glb", + "checksum": "cd1b509faa3f9a624e5475f9ca4e141399c40be34c8fe07a9d750de319f38974" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-250.glb", + "checksum": "15e96fa9b5a3c082d5de5b2309366984d2560e3b8b8c3fe11293ce1d20618e89" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-251.glb", + "checksum": "8f3238c3c1c5ff59c77517726aaf4b7463e1575b1b9a465638235f03f03b60a9" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-252.glb", + "checksum": "0663fa32755b7849c16aadbbfc0f125e13ba1dfc4022e8fc4b1cf443a4698a90" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-253.glb", + "checksum": "76093402fc5228ec1968d56a42045956481eb73d935fa655744a46f36da4a1fe" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-254.glb", + "checksum": "2ea1fba86ab99aa0873284366190f1feae8e765934c16b53afb14fc915484d3e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-255.glb", + "checksum": "d68744e6a57f4fbeb778f3827fd9cfd507a17b6da6aacd1153ea193b614baa32" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-256.glb", + "checksum": "9401cdc5652758b2ad51c1773ec3ac0bc512f317ab82a503cd420b9d25aa613b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-257.glb", + "checksum": "2c9f2e197519cb354eedde0bb89c82f52e8e2cfcdb1d7d841a9cf1319c6e0964" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-258.glb", + "checksum": "cd1a6b9010aa652ca112a7862e9f12ccd056776594dc9ede43bfbf1be09e4dee" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-259.glb", + "checksum": "3e5da1be189860e604cf2f640d4ce0117086a21600e7650db043a8dc3847ebc6" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-260.glb", + "checksum": "d278670a02255f8f572ee376046865b56178555b4bf8741db4951c5b7da893ee" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-261.glb", + "checksum": "a4b71da65932beacb42d277c1d707b986a7a1f5a8485e49abf4df52814282e61" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-262.glb", + "checksum": "e08493508c6f684ce27cff3e97d1068dedbcb870153a90b2c66459d9603881ee" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-263.glb", + "checksum": "738f6c8235aa83fa234ebe52381bf9410083f5825a2ea254e0e6ed20035f2c4e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-264.glb", + "checksum": "ea80ca80764326fe5941683ad4bce91335bd25fa156a735394df0d2751c6c98f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-265.glb", + "checksum": "583faa21f089d853ce2798f2b8845d95f02cb4a56f511b58028e83375893aaae" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-266.glb", + "checksum": "8f75d6547e8171748dc26a2a8affea72d6d6eb7f2cdac1eb38dd7ac809d7dd86" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-267.glb", + "checksum": "4ffeefd1dc7f611af453decde4261ee58df8d301b440ccbac51360f19139e798" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-268.glb", + "checksum": "22e7c9803ccf0b487eef280adbb510e2bc9232259ebe34f8fa86ba1215fce57e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-269.glb", + "checksum": "540337252ecf03b1a27e2e71056c5e04f76a62d28e5c082afe09e80dd50106dc" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-270.glb", + "checksum": "99cb3b98164655d6436dd6cbd135874b98fd0f9c8e996981871fd9ea4cd23113" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-271.glb", + "checksum": "4f026d91754f76eb3a759f15ac4c008a92b23b467156f48eb4de085ead67d559" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-272.glb", + "checksum": "58d483fbbab65920df93b2da17d97c65bed33f426eac0a87aec209e7ee6c90d7" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-273.glb", + "checksum": "f3669cbb9b362f35d6d4676085d9f876d360aed8d672055a44b98dda37ec9f47" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-274.glb", + "checksum": "f1b83130b4158e6df1aa4196e21d95924371c7a19f1ffeb54cc272ed57cd1c57" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-275.glb", + "checksum": "9f157067a5e4968077041676322698610b0c89da558ebca8ca9584000bf14b57" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-276.glb", + "checksum": "5835ae02d91e9ca584c91ce1fd45cab93d8afbe1d208b5f1c568feda81f54f8b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-277.glb", + "checksum": "a85c1dea4edc9d7f96acaf15052f6b4e53f37450056095e0f165142897e87462" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-278.glb", + "checksum": "9915069cb6e1b0d4963247c81669c67050c8c31322b2ecd1b6f73d9ffe2cccab" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-279.glb", + "checksum": "ad8caa12560ce2a5ffa4846ab58e746d0a115a1df5e218ff9889e104d89a750b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-280.glb", + "checksum": "65a3bb4403899544e76602733d79df3efbae24752cb79f808f0c4e7e9e60dc49" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-281.glb", + "checksum": "d2be0af75b19f661bfad58b43580f504fef0e82d63963e2d659d5479e343cfb5" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-282.glb", + "checksum": "aa6d1b37bbb3c64d180721b28eb25a7a732f4b1a33bb1fc87605d7099d7e1eba" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-283.glb", + "checksum": "ba153e6bfa3b5241d781c4157eb973bea979d9db803a9fe6dae10ffecdb5b4ed" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-284.glb", + "checksum": "54ecf70b2932e1d2a25843d935ecf6d2e6b207067b88a0ed92841b6393464305" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-285.glb", + "checksum": "141a82c7f4b3f9454accef6f978a80ac912b4a4cd29c86635f875cfbb3fc4ec2" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-286.glb", + "checksum": "751ff40ea0a50dbfc6293071281494db4a9c660819a47b7243a9c7d26d7cfda5" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-287.glb", + "checksum": "b0974248547388374884fe62d6f699eb03480c7221b6b3e2f8ff609fffcdcee2" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-288.glb", + "checksum": "a2cf3187bd3b50dd104dd4a56adc899b984f3d7997e67571708233253d2edf5f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-289.glb", + "checksum": "d3d6adb39da459950c0e02e35d6e94584cdcbb61e0120c8c8178d64fd5ca4e27" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-290.glb", + "checksum": "865e48c6777799308c4f49b5f61fb133e25fb53a49c11973c4bd9368d6e58dad" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-291.glb", + "checksum": "0069234bc074a3c528fa9ada68e1ba2ff716ec6e9c984002ac8703c875e26260" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-292.glb", + "checksum": "373cee4040bb9b9853a0117268169c144e2b20cc12fcd6078f613049e3278eaf" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-293.glb", + "checksum": "c856854b0120aa569fc9640de620322e9db2e0b7254887482fd92b9e920b6d02" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-294.glb", + "checksum": "bdb4a45a7577fdfec0afcd0eae8bb50943ca96e7a927fc5da1c086487539583f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-295.glb", + "checksum": "0d3348ba8d88fd8d4d6be4e0a94d4ab836b7252f4f020173e2ef804f072ef48e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-296.glb", + "checksum": "e735edb4927e9647f2745f3ac4ede395615e065a84c7e74a333f9c108285f7b1" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-297.glb", + "checksum": "18c628a2b920db04d9c04b660e4578661bc7e95d942793d732e13134aab7b1f0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-298.glb", + "checksum": "7ba1ab867c1f063bf4d50629eb4c9973fd675c62ed93a15dffedf515f805a8c3" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-299.glb", + "checksum": "462b5997faeb2accf41268fad7dac41178d99bf60e0af522430d7bae41004a05" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-300.glb", + "checksum": "3f442a7315bae3dddb2b253e7efb5d8fae9ff4aff2ff513e396223471fa2e6f4" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-301.glb", + "checksum": "a814bb46d34fd2964168f8e2680abc07144f6065b1d79e4fcbe47f4372793625" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-302.glb", + "checksum": "8411d359ccaba2b4c15d7410c838a27af379696996233542df655cc5e8e1e13f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-303.glb", + "checksum": "efa10ebc7594d75683cbd3a5cf6c4bf912272be74c58f157b0bb42d48cbb4f98" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-304.glb", + "checksum": "36c83151d29b97a613f3439641a8fb5f0328e71d21eb4282e6e439333ac98627" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-305.glb", + "checksum": "2dbb038fd3fb845628e820dee05a16bafb0082a43d43103cedf4d1d57d11c6c7" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-306.glb", + "checksum": "83fc5d40c14717e6c89c7ed93c67e7681162279a27dfb5cbb8ca71edecee38ae" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-307.glb", + "checksum": "b8026309873a252273bbd3200b41d00fdd418a082d24c6b272ce90264daee192" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-308.glb", + "checksum": "74396b6c74893a59a53eee28d4b57078c8c1b349059194694c32c58f9efecf64" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-309.glb", + "checksum": "88ce301df8924fcada6c6897a4361b637467319cf09ea1161fb1414f539af101" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-310.glb", + "checksum": "0101bd2e5106fd0dec12c645b057ab61d2592c85b87a41aab29148d1a85cd992" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-311.glb", + "checksum": "964249fe847e4ce9fd591978112a30ce03f3754daa4fc056281457aacdc701ba" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-312.glb", + "checksum": "e2c47e0fda71f3a8f17e6b3a3feaafdd6a2b9ffe9ccb289861c92a58f4fdd436" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-313.glb", + "checksum": "66fe27a92432146abe893dc4ff92bf01a241f88cdfa78c137f1ee493e985619d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-314.glb", + "checksum": "7d43ff56cdbc8a4d9d476dd71de68a99f17d098979238ae28bd2967a8d0a2dce" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-315.glb", + "checksum": "ba237c4b88cccff4f7cfae1ea818505ead1019f1bbc2fd3041b0b96683d604a7" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-316.glb", + "checksum": "ec02653871cc19668a92324f7bc937febb4940b49dd326d003b058461ed71050" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-317.glb", + "checksum": "6b16a11754f69a6539c98c2c95ee9efc9929b798de7eb2c6e36bbea6269f3700" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-318.glb", + "checksum": "eff805ae8d07e564fcf9d477f34779aed3d8723ceac312ba59aa8e8bdf5dc00b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-319.glb", + "checksum": "d8e2bf0e6a181cffe1fc7a85c7a409ef4f865f9b2a71faf4f04a852cef01ea62" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-320.glb", + "checksum": "0cfce715ff411051a5df2a479c4b54ed77bd4d265190ee30c588ab963bbba4da" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-321.glb", + "checksum": "92f172b772be93d6d94e38af8f9ebf1cce185571a13b111bd7ad51be80598219" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-322.glb", + "checksum": "ec3d124291e1bde19d6b95d9f727fea23aea5598e43cd5529d2474ed01ba5234" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-323.glb", + "checksum": "debe322c05f7edc5dff0ad76f4612cb426dfb6cded96a34c438aa642bda52c07" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-324.glb", + "checksum": "4b2b3f24a33ed50c15701969c630b9e290923eaff7d9237503b4562aea57a42a" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-325.glb", + "checksum": "71bc5c2b6afa6ac46ed2b6ec16904792c1ca3541555bc326aa96b5d77dd8d2c8" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-326.glb", + "checksum": "bba57ca347e0d15bcdd449d71913d7074667600ab99d741c24bad873337b4dcb" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-327.glb", + "checksum": "e1019bc716df7945b32e2cbd05ac1b6d696079a8c2fd11d6a07847a4a98960f0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-328.glb", + "checksum": "382e01b0f200fae1905517ba01cb91c9cd2600c41ce18c15033092cd5bd99113" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-329.glb", + "checksum": "21d61cf0f6fb480fd0a5e2f57e4b3cbb9a68d9a96a69dcff15d4d5ee18a79344" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-330.glb", + "checksum": "ba0a60f3b59aef1eab9ea9b4e9659eb085854bde5f779dd612f1f5b220068b27" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-331.glb", + "checksum": "ea819020c9c80fbde26de9f572ba76ee134b7d48198ed560f8d17e9cc9cf8f13" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-332.glb", + "checksum": "730bde1d31f6135e09bbf613580e6f333cd071676fef6c6418bc51f26670a0e1" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-333.glb", + "checksum": "8e0953f19995469366e737b91b1d268d528d4b75ddac03047410b60f8011ab39" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-334.glb", + "checksum": "12a97e54c7e1bd237df068186aa0f27ff6bfbd40e0ac81d9e45caf74d28c87ee" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-335.glb", + "checksum": "ee404d8644265ea6c84c9aea3ff428d9d598b6f9a79f07ed08a282418f7a2774" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-336.glb", + "checksum": "00c82e8472afac2c951b3eeb263ea54df5d7adc472cf9d86376b91ae333ddd19" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-337.glb", + "checksum": "4cb853b5b139b142052f4135031a409f6eca9c82c0db7bae0c7b09a4ff181edd" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-338.glb", + "checksum": "e0ddd74e7eb3aa4857fdc013860618d4c423123d0287f20f91e2d79e699497d9" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-339.glb", + "checksum": "1b1254bbbea20598363690c4a6b868ad32c0851bad2e3e6014d280cc43eb7d18" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-340.glb", + "checksum": "4c7af0d692096bf994d457d9d1b3f93936ec822318e7d90f1c8cf3de360a1c3c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-341.glb", + "checksum": "ea1fc4576b9a75d53ede59bca70fd75637f5ba946f3b61550c5cecf7b1f3ec60" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-342.glb", + "checksum": "805a31aef8c7e52c412cf7e397e2588fddd7ba7cdaea191d1af59e8e4006a590" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-343.glb", + "checksum": "766e800425cc07eeb645ea63b992315aa90b3d8408c4d8b885467c66e522fdbd" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-344.glb", + "checksum": "9141db18bb1de097abd8c1b8812327cf01960991c4b9cb81e75d6fdb8a450e68" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-345.glb", + "checksum": "50d391352ce6cfec9463ac93990ebd4bcc976ce6a2d51a2c2f4bda0548649588" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-346.glb", + "checksum": "c98196af62b284bbb37ce980f25f92fad0eadd7338b9a20c2eac346874f4579a" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-347.glb", + "checksum": "f65513a62829e239cd182473b036b2463d0ce6fcba3816298abb419eb758cb46" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-348.glb", + "checksum": "79262cdff765253b50d6fb4cefeec1a90a39555de376a7b45bea06ed2fc92d0e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-349.glb", + "checksum": "4aac9ef6ed578e627284361cee486ec4d9b3a77af7814cc24516f38876396793" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-350.glb", + "checksum": "f44bfed82178e5551842b4f17e9cb7410c19c831938db47cf6911ece415d9294" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-351.glb", + "checksum": "70cf02fa313f8d5a12aae968a71231eff6b274b8d528f96cf027c217698a0a8d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-352.glb", + "checksum": "87bfcdfc036e7068e594ea12c1f27fe082159620c650bd6333632ed18abeabd4" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-353.glb", + "checksum": "049f15d159886bbebeb4340a4bc4a58f7ebd18892341b91e1986083513e206f1" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-354.glb", + "checksum": "c6df78f49f67bbfae677472bc674bbc1e412d35596c3f3c86daa5b87ad1b22e8" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-355.glb", + "checksum": "2390d1efc3ba3df9fbf1b4fc32043318357c0fcb864c57c5491291dfd9a8b19c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-356.glb", + "checksum": "5f19155f1459a1589fab1b11e2461e2e631b9729a5be538659a0c02340269f92" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-357.glb", + "checksum": "5b15258cde0639d7918a1e1afa40b492a2391bc8eabc27208d94df3a05600923" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-358.glb", + "checksum": "d98b4c60af860339961abb2c214df3146ee6512ec48391b9c4f11c9d7e624b59" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-359.glb", + "checksum": "9780eda8927302d54956a96aee20055005e20f840c86f6dea9c1c582bfca5fc7" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-360.glb", + "checksum": "b871672129404b814cd1a1c9a59f383a51781b6a8695cd0fa811da1f82d5c5d0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-361.glb", + "checksum": "604ed854ef126f4f6a0dbd34583c4563b1a519d9af458180fc5f53f6dda50b7d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-362.glb", + "checksum": "f372b3c25734cdb9a84b5d0a8260b0f2b90f71328d1c3d3c753cf147aed37712" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-363.glb", + "checksum": "9b92df0ee08453a0ef6c60b423119b3d820af1b8e1ad58c099f2e7dd36349058" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-364.glb", + "checksum": "0de5524230c01c35b90adcb30e563e3b744691b3af0085a29d17e7e4b2142dd0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-365.glb", + "checksum": "04bcb26fabb470071bb1a8955f96eb9452e2895cc254dd73de23b95802b75596" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-366.glb", + "checksum": "0d1af9d863284e2cbf787979f5338ef33ad4e9fb944a848fd262221d905c2a80" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-367.glb", + "checksum": "1857e133c78040e75f0e3ddfe0a9df3d7e9727cf69871bad2b7e7a7c2b1b0476" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-368.glb", + "checksum": "be163bd03fd6598f685c2356a481d9f059bddfec1b043d1f896176d658fc244b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-369.glb", + "checksum": "cd043c0b3f4afcd37a2eee7564b25485a1ff2af4b9d7a8cd36c8622064e77612" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-370.glb", + "checksum": "f2f78f1d111ac18c8cdf8c4623386d5807c1a1ad2c46547f8f847d2f440a2015" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-371.glb", + "checksum": "1ac47125d7c733cc8106e0bff140c7a8567a5c030ef56539074cfc38b92e1b40" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-372.glb", + "checksum": "7531ee8096e1d4c759286e1a085d4fba934e5682816f8d9cd9668856b4054e48" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-373.glb", + "checksum": "572430c882b5bce2a9b141d947f7b08ff37579d058e5aaad2564c55518b19e46" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-374.glb", + "checksum": "312b6a20d15a3dbaded4e3181d7209bb37e936f76d36f8ce06078aa5579fbc9b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-375.glb", + "checksum": "07a1efb775d3a6f2109326a0f55687721f844c7b43e0d06bff953d77444fbbd2" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-376.glb", + "checksum": "c869e1d86a828673f130ea77b4d544c2569587335ff9b0c73b5f294397c9942d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-377.glb", + "checksum": "df46e3dd2d958f3c6781ebd3f66da6077e2430dea2d3bc7c8aa1706a4ed03b09" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-378.glb", + "checksum": "31e6b184415f233a6ea9ae60049198c0ff31249585f35adaac1fd453139327eb" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-379.glb", + "checksum": "caf3c5f76091ce296bd7dce8f89565608b6ab4d8d8b6b4d460be9af9c4dacf47" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-380.glb", + "checksum": "730d8568ea6ec9da71738fac0b2d7126c7c6983ee583056cfcac3434d3a4e389" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-381.glb", + "checksum": "5b39ea1d93f86a97e23ae10c268744fb83c421252716ea8febeff5971eeb7da4" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-382.glb", + "checksum": "ae22bc055e0ed83773d4438c844e7a0a4289d56b6dc3b329b5ecb04393277e4c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-383.glb", + "checksum": "6fb0c881acca00a5076311346966f689716ddc0f9a5ea3c342003329d006da30" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-384.glb", + "checksum": "e4ecc7446293c5e18e02c1dc878c86e7096f07ae541617bd4c9e8d3c2b20eda1" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-385.glb", + "checksum": "54bbc60224eb3ec37cef43f0d9190a61fe24c67e9e06e375b10c37235a8e32e0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-386.glb", + "checksum": "f9045c88715b9748b8bd03a886b53f23e0c9ad14a64652ebc9a0a718738d095a" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-387.glb", + "checksum": "96d380758f0af6daa46556f7e4b23264f6b3d3501495e978f876aef18e29f990" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-388.glb", + "checksum": "40c420b5328c751f347309b3ae1ee9af7f2ff17fcb7800626d6ea02dc2115c34" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-389.glb", + "checksum": "88e3a3ba1d19052fd9549ac72b01d86156b6450d281511afdf95f9d4c5d8ed3c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-390.glb", + "checksum": "1985f71611645d960716ea5302b19144058ec8831589298903cbcc2950ceacb6" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-391.glb", + "checksum": "6f51f47a5602cbd13d67fa5d91f4fccbdc0e73704702698169270890e590cacc" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-392.glb", + "checksum": "022404a25bcb642444f903cec6b0247f6b70aa9609e7eae33d3437fd635ae349" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-393.glb", + "checksum": "d72841aaa5216d399b1c976ef0bb6ca947c74e80a1b1cc9a023053e59a7472b0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-394.glb", + "checksum": "7b028c1ed9f4b1c0867a1ee89972a641b8356b141128d7015109155d40f4747e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-395.glb", + "checksum": "913bcc7340a821603045f752f19efc8bf50e299e566d5866f0c1c5261bdeb5f2" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-396.glb", + "checksum": "26c1d5c99600512adbce38937ef69cb3bbc02f41606c0798572d2d19e5ad0ecb" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-397.glb", + "checksum": "c296fd370d88966a81e019d8ef86c5ee4032d88bb3039e40bbef3a5d3c116a4c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-398.glb", + "checksum": "a0e8aea92aaa57ad2fadffdcbf52a70b789c0b3a2b182d8771efc8bbb8b922d3" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-399.glb", + "checksum": "a66e3635359f398bd0e645604cc3f6ec9ba031faabc5e88c61b4cc65fa75394f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-400.glb", + "checksum": "e265367e47415b70e3130a3df1be5c46fa0f4e1ca49f956e88e38c01cf41b7c7" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-401.glb", + "checksum": "e9f52b994d1749e36bde5b4bc162b31ab0df3e686305eb75cc68674fbd1ce4ea" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-402.glb", + "checksum": "147f7868df74fcf33189399b5674cb7f978afd42a63f821acb9cc56218e6aeaf" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-403.glb", + "checksum": "f02e6df156ae3e72d43e00f5dd063cf72b8187b363ad6161519937e812ab533f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-404.glb", + "checksum": "de1d9b18c3e851e42aacd3a95a36b03eefefffca23623e28101b883fa4f0aa3d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-405.glb", + "checksum": "f63680ad6d4e29b906f8b6a20f1fdac00d1791692942956a70704044a6faea7f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-406.glb", + "checksum": "ce7188c577a745b19b9500148362d773a339f3160b8600e5301e83832e2b8187" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-407.glb", + "checksum": "5db4df9af79a995bcfa304b51aff2acb76172cb425303cf1dbdd147f2073457d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-408.glb", + "checksum": "a4c26f90a77d26e1797a34528950b32667d4d3b6c4f2e46d36aff7ca4707ca67" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-409.glb", + "checksum": "cba0ed65a27d0f17cb0e626fbcb26cd7dd921eb3dbb6e212e931cb09a2dd791e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-410.glb", + "checksum": "08e55de760f231ecf8c382265a202f3d2f6bf989b5d90017370d088d89d8d301" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-411.glb", + "checksum": "966474cb1cbecbf4a5c2235ab58fa044d7d766e3f23811d755e699efc718131a" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-412.glb", + "checksum": "21a5066f7979a44aaed7d843a7e7c6c5812f4770b9e88bfb11ad540f4627e3d1" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-413.glb", + "checksum": "a1b6d64e60f7cd1ea123a03d8cd4d530e4d717c4b507f15d5a1584d045bb7f23" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-414.glb", + "checksum": "b82fca77127fdbec877ba843a8ba8948f192261af89ca1ac79c6a5502cc827ea" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-415.glb", + "checksum": "99bcc2e581f2966e2d040428d0dcb8700192da059d8cdd81025624eba340162b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-416.glb", + "checksum": "694ce78d52f9721663e2398847a3fb4c30d89485b7af70162bfa1939f096dffb" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-417.glb", + "checksum": "350b1cd71fc3e980bbbf8dff7ff83ffcc09a4834b0eec4d952b83aea5578310f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-418.glb", + "checksum": "aa7fdad38be264cc5ed08c51247e7e44dc85b113d9cda65265faac027ca5a106" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-419.glb", + "checksum": "a0b4177e73d8b7c3da4a8fc2e36ecf3ab3c33da47e79a034fdb31c7b9d881255" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-420.glb", + "checksum": "38876960077620398871d30fa0ba3ece4784b15d5ba4dea0f96fde96439cdf6c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-421.glb", + "checksum": "69924012cbb454262278b78c7dc53bb718b327415dc0917ae1165bf27c2ff621" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-422.glb", + "checksum": "7d80045d73ab44bf4f69fa4bd7951d581778e35da31ac2d0c362eb76efeb1980" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-423.glb", + "checksum": "fad4377a3f82b156bcf2c9b4dcfb2cefa04a5cd5cc8cb6772df50ce940d10d4f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-424.glb", + "checksum": "6195017bfd6837c4402e18af5957707b55ff2482002f0fc1c3612430c7f55d06" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-425.glb", + "checksum": "7e2f90a72df2e7a0195c5b82c2336a7a18dfab493a6e49102ff03c0a9fca7ae0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-426.glb", + "checksum": "ff25f21ada6e8fc0c60a6d5cc345b0f7891bb2be3db1a1fc83ec13415e13001c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-427.glb", + "checksum": "4a3e1c113747b4fe5739025b050de3a7a3210cf26c0bd4c36b91535f3aaa1a9d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-428.glb", + "checksum": "734f49d9d741e54aaff49b43006f51347af42b10c89ffb840fc31a79a6a5d093" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-429.glb", + "checksum": "750d1bc74167dda9ebb2981ab596c32b6be3fb37f3d5876f175bf9f7c0541cff" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-430.glb", + "checksum": "f0915582ad281ef10c1abf638ee547036e60f37ced01f11a3deb60b81cc98d38" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-431.glb", + "checksum": "846d3b3c12b821af18cb599894fe5ea29d35479b18646372f57f3dd5b0b7e168" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-432.glb", + "checksum": "90830e7290b76456b0c4d4d79fb5645f78f09078cf03a32db6ea96fdf6ad2d7b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-433.glb", + "checksum": "1ce068b986992974a23a16f1ad37edac2c9f21b665c33eb78300163806c464e5" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-434.glb", + "checksum": "96dcbcb41f723138cedaa2066376775ab61b7a5b40fc42a9a23ee92c6cc8576b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-435.glb", + "checksum": "8556920274348626e1ef2ccf50fdf72358719f11acd679e3058b8301871872ff" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-436.glb", + "checksum": "7cc17c169257a1aea10ed899f9950f053f5ddd62d58db0cf6055b93d58de8208" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-437.glb", + "checksum": "529c8763e2956ede02a2cff8fee4680a4a79dc518afa10642d0b6bdf241bc7c1" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-438.glb", + "checksum": "6834bd104937f841793bd7344b61641cc871bf83ae6393b7501acd1045ac25f5" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-439.glb", + "checksum": "55c4ffca87e978b13bd9cf9d6fe643cf978d84f196a699c42bf69b1975dc5446" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-440.glb", + "checksum": "d58c01cb5cbbd7502fc5fc1c8aedd81ae15dd0ea770b3fdc2c507400fdf3d933" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-441.glb", + "checksum": "93b216a9d9b15cd5c29e1a30847e9f0804dd5bd08bcd9dd3d474d62b1ed654ba" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-442.glb", + "checksum": "24b593e0dce8b505b5bedd855816ef6df6bb036ec1ea9530fcbe7abe8f2dc1b8" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-443.glb", + "checksum": "d212debf456e708da0420a929d8a879dc309824a747bcaca5fed5a69b0a79fa1" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-444.glb", + "checksum": "dae9881ea5cf3d496ff4f4d8605b10c55ddaf34e2a529c61e9a398482ded642b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-445.glb", + "checksum": "d99ffc732758106a9e06cbe868d4ba40120ff8b26da3f74814428002862607e4" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-446.glb", + "checksum": "ac41f7a3dffb055ab299f6397dfb33d8c23815d0b431f426f8efbda42125992b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-447.glb", + "checksum": "23dba50af74ff87d92ad442e575bfc7626ba3836030ce09877f82100b14f0de1" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-448.glb", + "checksum": "6637331e36c8ed82b97c20e670e35f853ca83929487bd38ef60c53a4f27d3b2a" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-449.glb", + "checksum": "fc37632f8475d4a79b67a368b467e64b72b99abd22129322dd4e03095e24ed23" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-450.glb", + "checksum": "17145b51f4b1709e0b9bbb05c3e9d4dc8077734cfe19502eaeb602de94d0a153" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-451.glb", + "checksum": "08478b4f74f9cc883dc95e7bcd26d42be09bbe3e15869b60a41acf8453248a1d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-452.glb", + "checksum": "2cdadb38ca8c1fa21d31e931ccba88ad0e0a914a1573e6d4bfb1826e412301fc" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-453.glb", + "checksum": "d948eb2ac12b92ce3c65cd80c5f56f42ab27817743c66a218ea668d02555368d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-454.glb", + "checksum": "8f95bd042da280e3aab2f595d658e6e8686f5eeb169e2608da2dfc486c26af48" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-455.glb", + "checksum": "e8a77b64b2f6cb427d503ce110d0399cc6c30b6a80666d42691d910c0ea37e25" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-456.glb", + "checksum": "d2d35d64d2c541ffeb53d96e883c716fe06c18c6565d08c59236e39e5b4193d4" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-457.glb", + "checksum": "f4760fcaa266ad7e90505000b47da748f5d0c8ff5285b85b1375544ecf14a7c8" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-458.glb", + "checksum": "7a4959236114e64a6fe8c14f842c109a34587c998da71c88a0ff43ab64852fec" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-459.glb", + "checksum": "0e0c2fafb9c6823ea48617bb74a69dbeee621906e55f56295eedc1dc28e6cf24" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-460.glb", + "checksum": "07cf9bbe0d2f84b171ee8224dcc5d9171bf9c2e2537230f2cac51d83104542dd" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-461.glb", + "checksum": "89f5e3a2fba572e04e9a36ff2c2759330ea5c1e9a19bf5d1ef2ce9f40b11cba2" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-462.glb", + "checksum": "fdfd7204fc64d81ef7ea96286dda833485b2df55875b4960ba1f8f1579786dee" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-463.glb", + "checksum": "66ed2ad6036326f8a42b2a81fbc5f976dde341d04a92f632d9a40dcf6e14a9d5" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-464.glb", + "checksum": "6bcb42265e9b5a5582d69e4bb917c50c6c8733d31fc408a34d4ee8c05cffb85e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-465.glb", + "checksum": "f66fd9f2b4b4a3397d6ac41701d7f90b7fe83a0346c0d2e0632224daf5f8be2e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-466.glb", + "checksum": "e88aa77da0a365620e362d638d14a79ff83dae3fd3718ee502bdebc5ed65fe97" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-467.glb", + "checksum": "cdfc291a44f5260fafac01135ce0ae2037878860d5587d806a55235371243622" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-468.glb", + "checksum": "ef06811e683f607dea089c0e6e3f2bcb2cabbfa56b5e50b9f941ae155c7ae54e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-469.glb", + "checksum": "55b7a428313341bb57eed975fd59087a512cacc9f1e9aa700a16c34f526ecd27" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-470.glb", + "checksum": "6123d5f3ec1c3be100a0f944d173c4b60429637b802758d2bd3aae0fda2138df" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-471.glb", + "checksum": "2a06dd3080e887eee6705bfa4065b56f87d5a608eb74897fa78824ef9e8c1e8d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-472.glb", + "checksum": "7c873fbddc2f659268c338bf2027fe988c211fa90c4e0172939a640222c55c67" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-473.glb", + "checksum": "4368d5f2047e1fe04af585fba60905849e0869aa992c4be0fa534a197cbd49d4" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-474.glb", + "checksum": "051948efa718f1d48c9b137b92c6af72b24b218cc9a5a0b7fedce9cc45b6b576" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-475.glb", + "checksum": "887ddb91453168157fe93c273e9d5eb170582227b1c26723ea8743b2767bcbaa" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-476.glb", + "checksum": "0acb69096d41b9e6b042e1f734004613b7625f62dd69a1f300d6cdcc256f1eeb" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-477.glb", + "checksum": "9bbc8404d680bf5fedacf8f002cffd3ceb3fc4b436b2d9ec6c293a68e4e3cc5f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-478.glb", + "checksum": "187a7214dd26ed64b3ef856f256a147c6d881572a4a94e24cd0ec8dac7ba0c06" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-479.glb", + "checksum": "e3626b2988a2f6dbe592e9b4a7a64e0f6f0e85bce5d0cca4d102dc6b7c081bf0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-480.glb", + "checksum": "a13da12fd80f4d8b31f449820b57c2533e746172271a81e0b124615dd73c7941" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-481.glb", + "checksum": "21280f451b418667ba66df78d8fd1d4e59cb7962bf5a086feb83c8e0a96819f4" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-482.glb", + "checksum": "b8af7d681df0147f1a511331dbbfd12d63aadfd0c4702acd79a08c804fb2774f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-483.glb", + "checksum": "83d8736e95e064574d36364475f50c25bb5fa649c32bc1867cb47a78458af291" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-484.glb", + "checksum": "dfa59f275d701e37582139681f163cf6eea30511890ce367ed3abb90b6fafee3" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-485.glb", + "checksum": "49c474d2f108ef5f12b394a362b1fbc68fd3995133ef46e16b7e0be08de4fc60" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-486.glb", + "checksum": "4179fbe6a5bfb9586d64a40246a93c2bd70aae1efaeed18caa9c1e5e56ccfe2f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-487.glb", + "checksum": "7a2ca6e71187a4220a195c52003a009fe29dcac37ead3273722e5c207b31dab8" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-488.glb", + "checksum": "6b9e13823a90d0f04b7d00897df07ef1b18ebc1ff818f8f76912f25de7f7addc" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-489.glb", + "checksum": "790b7256ec2e0ac1506c13852d7149c8afb6ad5f329724ac35eec38782a43dc6" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-490.glb", + "checksum": "8508413a73a597352846d3ea115d54da7f091b9525a7c55511ade70a4af7cc2d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-491.glb", + "checksum": "be0c01c362a6e805fa51497f2942581461443dbc888e76397443cd9a1633d30a" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-492.glb", + "checksum": "9ff82bd854ef523a54cf9bec25c92283d3f79efc5b0485a7ed67dd3b0ebfda40" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-493.glb", + "checksum": "e0c3230b2ff449e8e020446bcf4f016991553d69468d48586d54925fc44d848b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-494.glb", + "checksum": "a246cb99c63dee45ee4789083dbcd7b3f716882f48154edc68f58eb5150ddacc" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-495.glb", + "checksum": "d1ea99dcc62ad4a388c38c0283fc82a43d9384c3bb6ebcf2e829c8094c5be270" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-496.glb", + "checksum": "24349fac7db58f482e9438a3fdee70f4690c54376786071f6fd04793d6919dbb" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-497.glb", + "checksum": "36b3ab1550e93c930aeadc5789f361d38f89d05859c0383a053d40773b97c28b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-498.glb", + "checksum": "bf9e1cf750aef5d5aef68398efd8a4a158f0bfeccbb339fa6f233901866fd6ec" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-499.glb", + "checksum": "114de55fdd713ef824036e1c16b11836c50636b3777ff9f91d59f1041a557323" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-500.glb", + "checksum": "6d0016c9194e4dff83224f05b32989b03228d27c17b35deb92a8e63c5823dabc" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-501.glb", + "checksum": "8654302e53c1729b8d45ab06abb79bc4386c59664e471af13c13f32e2a858ac3" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-502.glb", + "checksum": "70abc34381ae40e9f1cca08d0acdbe205585da3f1551117ecf3f7f4f65526551" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-503.glb", + "checksum": "f16b960a74264181b0536ae9c363de7398e16a8e7ea8e774d53589c15fd46a31" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-504.glb", + "checksum": "c49b48570aeaf793490578959f5e25dccb47d36aff3191e059173139d3243688" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-505.glb", + "checksum": "1940a4d1fe1e8b079c11add0258d5ade1fc4e3782cfc7a828177ed72d2c5cb63" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-506.glb", + "checksum": "b4571cab06b2e497328f78b30bb16ed4d3fa9fa23d2cf9740a4cb8dab812648d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-507.glb", + "checksum": "1c8d84aeeb2f99e25d26a5369dd513e4df4196b1fc03549763c8a3beb879214c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-508.glb", + "checksum": "c80ff19cb4dfb249f3881bba725a808ecff7e1e59d693aee24393f1999c0aabd" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-509.glb", + "checksum": "408089fb2fdd68df10779e7576a1f18cec5d9b56cc0b41328783ab8284c54a34" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-510.glb", + "checksum": "e8d086a689e2578a64f35bc5195fd8e07750b7d4c4f94bd339c0a76698315626" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-511.glb", + "checksum": "f2a00c6a4a8833c34ba2543107536e76a64b5df363acd4f7e50eb5993d2cc61a" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-512.glb", + "checksum": "6804ac2e85c24875931e4113a92af2dcde70516faf34511e6e86dde3f4723477" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-513.glb", + "checksum": "bea5e1d2e06d9201d5e18e1f997f536aba4f7e925eb55331916dd9ab6470e5f7" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-514.glb", + "checksum": "a7f2ed0039c9b1c015f2b979328df29a8b3d7feca4ec53f71b0314870d4fbb3c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-515.glb", + "checksum": "4ad902f5b757fae054a1dea688ba63b9f8e5f8167e9c2224732cce6a0aca1df5" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-516.glb", + "checksum": "45b6a5814f81b922e6cf912c6353846c6e1938a986c09e5226044bda80095031" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-517.glb", + "checksum": "9205857b106f11749f258cc3e38c103c3dade0d3146385e645809a7ed4d6f3e2" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-518.glb", + "checksum": "4f87532fac1663567828182d01b08b5e1f19eb3159f590896b8e323876c7da2f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-519.glb", + "checksum": "188a50392f485458a3ddddb2099068526a8b1da3e5deb7ebcf8a110bd4715319" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-520.glb", + "checksum": "249a72251723a5296b4276dc5681ca4e664e1ce6ade3d46c4c0c54d172209f67" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-521.glb", + "checksum": "2fd2cb2b75c2c82bc0eaf4d00f7a141fc2deb3a85d88218f1535ad667ae4aa39" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-522.glb", + "checksum": "fb8d90988da2462f39863cbebc2e5c0d7d0fa81a9a891343bce3d1df25867b0f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-523.glb", + "checksum": "989385c66d444d42ec1768d14c304077e627366ae26d2be478f820bc65e73257" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-524.glb", + "checksum": "ca5e2e204fea17435fda36fec64e4f5e5a85019065c4665abeb3f5f824515c31" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-525.glb", + "checksum": "8784a778c19188bb169fd6653cffa80fb34763cafa51a4b288e52076666584e8" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-526.glb", + "checksum": "4ccb510859f89bf1fc134577841d28d85406c66075b0550459297b530040f371" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-527.glb", + "checksum": "1bd6f34b2d206aec6e6e6ad84b88009f2b36a9836fc5995c4df828631af64350" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-528.glb", + "checksum": "b59f4d24f7a81f12117b67ec48c03f10dadb8bbffd406e7b27516c7fb2d87db9" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-529.glb", + "checksum": "754530842044e590ff15042fcbdd4e8f31fe604a3f47bc27b807a87bbb51590f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-530.glb", + "checksum": "29ae3e341093340e0be42fd382ecab837f059b0c002331ea931746744d9619ee" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-531.glb", + "checksum": "8d2d014e99fae2daffea58c5eeb62f53a7f92d17935db0f627d8a9d36cf5a13d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-532.glb", + "checksum": "99bfee371645a2d4c44544eb443138fe59d7e09b48793aeae3bdb754381bbe6c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-533.glb", + "checksum": "fabbf36e4416466b8f222c62141faf2f75a9a7b5c5ae0bcf7823e88603157e53" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-534.glb", + "checksum": "7b71f60d740161120e829dde660510e60f86afafdea0686e6c3645230183fb4a" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-535.glb", + "checksum": "be4239e3579739111b94a24dd28529606de5b61b9a18fe2f8d2358ea84ddc999" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-536.glb", + "checksum": "aac3f9c6d8b233ec65e83b2ebd52e2b8c6ef1cc65449396e6051b4f27604266b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-537.glb", + "checksum": "07580c20da472ac7379ad6acb8005fc52999e6c0278966eb3726dc53c26f0007" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-538.glb", + "checksum": "efda3dbcee84c6fb1dd4e8e0dfe31b73b494372221c183c319e6416d2d2521bd" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-539.glb", + "checksum": "4793575c1e38f5cf7f54af7cb919bdf166facaeedbb12a890a7298d763c45115" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-540.glb", + "checksum": "f0614aab36972a9914d23cdf98ffcd057d0bf43b913e3128f422f99061f15172" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-541.glb", + "checksum": "2027b53bf966f3f4941326712eeee3508ae9ffd67e67af47bd9891d59d60b972" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-542.glb", + "checksum": "5f7c5b20b4a7dcb1f53d3d106cba9e4b432df068f86da51f14809d6beb550165" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-543.glb", + "checksum": "6afcbf88e20dc0ba43981bfb625a0deafe6b3a9efc5f90f8e3d70410383cfce9" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-544.glb", + "checksum": "868e741485d0f901b7293f730643bce44faa5178092396c242d37a751d350386" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-545.glb", + "checksum": "7e453be8f87cdce5ae72c49cf9b0f18e0a04b39177ec1c3c589ca06652bbbfcf" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-546.glb", + "checksum": "b3ec9691e05ec83c296ef1679a86ab4a73ef022a3e86fa6a523a9d44d78dd22f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-547.glb", + "checksum": "303d651e1b4847990a395050da862753a5f0b6c880fd35445ed199f2703dcd66" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-548.glb", + "checksum": "dcafe3a9c11ca0b6b436a6bd0e257a060d8876b778e852038ee7a98d45f812d9" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-549.glb", + "checksum": "ee3f7e577aa186b2aa631fdceee187e60ebba13217575eb1164f34944acddf8f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-550.glb", + "checksum": "41800b59a22ef8a7abdc77d4357b0c764a9f308ccf5d0ed1892010846f510ff3" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-551.glb", + "checksum": "fcb7a8223456d16cc7e6564faaf28541ab510ebe3a58f877a69683a5a1799f32" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-552.glb", + "checksum": "1345e9186184af35e7cf68b8c837ba53fb96127a817fcfba398aef524eb0fa1d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-553.glb", + "checksum": "e6e7ed65aa527df6c44edf2b52b17bcaba95c9c44f112f7d303f6b514d842d5e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-554.glb", + "checksum": "f6d54340f5c97afd73b0a7f4887be463142fd1cd3780ed578643aa97a984fa60" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-555.glb", + "checksum": "8874613e5d9b0ed03361c95d3e3ebe12d450ab21ff11328c12a587aa04347801" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-556.glb", + "checksum": "d38606fc4e007b8793dcda7191d92e9c566eaf7a13e30057ab34db39a3314fb7" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-557.glb", + "checksum": "aa232b6ece5e1819b76f9ef26b5aa47a7b4c4ecd5e460169597ed90f1c54f163" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-558.glb", + "checksum": "e200404370d434b78e049784fef95157243004f958f6df220e2359e022d000c4" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-559.glb", + "checksum": "eb6e224ff62227c844eec147e6f7b2f4e305a6d7b08983916b113b6d1e755878" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-560.glb", + "checksum": "6568dfcdecde751a693e01c91de91603dce47d578cba4e228bb89ade6c5d5b56" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-561.glb", + "checksum": "93e1613b48ec5acc26accf015d821b626dd63b8e37f2ceb6f4b458359126f958" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-562.glb", + "checksum": "3315c3b8f372c35edb42b2aed7d178f22aecce659c67e382f08c36dc91b70661" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-563.glb", + "checksum": "50bd863c0932c4df7bdd702ea908f61065c534f235860906de2aa9cbf60dec59" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-564.glb", + "checksum": "1d32543fd46df0cfe6069e02aaf06c400f5f5f85887a98a9aeeadece91b47b41" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-565.glb", + "checksum": "0666a92915f48d9b78f1072c811f3f2e49fba8d0c43568f52be5d44218664a2d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-566.glb", + "checksum": "7394b31e5c552fd87a1a0a62c0fcc932f211466d9be94427e75b354f429a377e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-567.glb", + "checksum": "6733261e43093938680cc78ba34c5c76dd929756585232798cae833d51a9e120" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-568.glb", + "checksum": "493fa1afd575650e6237e1a85611d26bed4b09b98c9d840014566e0869d4397a" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-569.glb", + "checksum": "95477e36d0cdcbedfe51032408ead12af4c2340b61b5aa41fe8fe0cdcd02ceed" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-570.glb", + "checksum": "c76ffde4b0a93a8888ba831a647ab2fedd268e711f993b27ef5b9673f9f2914c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-571.glb", + "checksum": "c1b3125fec2fc234c9f3d0e6d75f7afc551d8356ffeec27125f87540f5a793ab" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-572.glb", + "checksum": "dfd9dab0c4a7c8a54251e8c2c17dc25629f9f6e13d5a558703c19ac6a634a8b0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-573.glb", + "checksum": "e55a52c1a6d2a4f2b2a9edef967110d441ba8e2f07faf220f971a5ef23e1eea3" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-574.glb", + "checksum": "54332c9b95241493e533281040d8dcd008e95bfaff29b88bcf29fa79e53769a3" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-575.glb", + "checksum": "4bca721672d764d85af2d04f8e5946c3c5dff13fb61500c36225a25fd9388d46" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-576.glb", + "checksum": "b91ee021967fa1370f3a56b5593aec992b5b8b67b4c932a11b39770ad5d7495a" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-577.glb", + "checksum": "bd589d8b57d5f68d3f0cd8ce63fcd6278306d656bc6bfda1890162ca58237c33" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-578.glb", + "checksum": "713ca9dbef2ec628011447e87e9e527c38af3262c4954b5937b9a3f77b89405b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-579.glb", + "checksum": "442c4d2b9049d168839867efc1b0bf052c6a6027f1f2da068463489fbb83e901" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-580.glb", + "checksum": "524f7f07d36fc0eb72d2f5aafe6cc7e037226998bf729a1fa9cf6e49880ba0a3" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-581.glb", + "checksum": "19a7bbab0072e02ff9ad63667fa7ed7296d3def9c5ea6338cb21e739b6590369" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-582.glb", + "checksum": "52cc1e2eeab91c6ba20a5d464a58114edf477be0f5cc9a8376af7e64acb3d599" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-583.glb", + "checksum": "ac4ae65759ea4ddaff2dab303d3dc337d6cc588a2dca14a44bfcf6dba0d5b374" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-584.glb", + "checksum": "e19e85a9475ccc3233aa83118143fdf0f1ae84dab046ec97eb59eeaf6ef3690e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-585.glb", + "checksum": "0563a8c2d779d221e7edd4f24bf0e390c90ece737418fee2dc68c5f43fda4a9d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-586.glb", + "checksum": "a4e395841f037278497e204005b609219325c28ff5123a83afc0303a1589a482" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-587.glb", + "checksum": "321b13c8ae5ae229e32eb2536c0da6255350bb86a226a2555ff293911995f9ba" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-588.glb", + "checksum": "c2bf3ce500696180af57584209669e646932b744b0c15c86b1099e1b0da09349" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-589.glb", + "checksum": "344f5e2c53cd3ffabb6f9e3af90e786e5fa3795e3197a7df6c3c9c0530c161a0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-590.glb", + "checksum": "25ba7ddeabb6e06c4bfae54a0b39955e15973870635fa5483495d053fadbf8c0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-591.glb", + "checksum": "113dd338a6d50bd5fdeab704cb4dfc0ffd60f362b54b4ac168b1e969a3e1d814" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-592.glb", + "checksum": "60c7036cda1bf597972b484f4e6cf3991a22b66418d7d11430c929940f72b71b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-593.glb", + "checksum": "bc47c53b9c01fde5b368af440a6d094c892e7cd374977ad9486eb3041fb0ea52" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-594.glb", + "checksum": "c9c3718f03eeac19b56065d82021ce9dca3a83fbb5b941adc24e174fdb325960" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-595.glb", + "checksum": "e73039a84834ec4432f9e2c3d68fe06bc8ed15f7749b60990efef4f9d1d99994" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-596.glb", + "checksum": "a6b0efe91e15e9e669717223575396b8342fb0003078c186ca1222b94adbffcd" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-597.glb", + "checksum": "238d69876651080a6c86d5853aa5904724841269df5fa6fb51eec6deace0b53c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-598.glb", + "checksum": "a85ca1100ee960735c36c155a63896b32e57c852b03765c70e20347a8f0fbc6b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-599.glb", + "checksum": "040b5564416dd10b42a00dea8796246d492896e77ca3962c6231f844330c726b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-600.glb", + "checksum": "2908e21ab96aa5f59326377c9c3275e15ebce7db9a05345c44adc6106768fd5d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-601.glb", + "checksum": "0120b5e8e4ac02b3d19a06496a055ee410c5a0f69342890f4f3ad4f039d94a7d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-602.glb", + "checksum": "5e5cfae2fbaa50b9adbd126fc4cc8217662216246e88462a8cf52f197c9d99a1" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-603.glb", + "checksum": "7565917bef093929acac21bac6d3d1f33d0a0bfb9be60870d538d5488564402f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-604.glb", + "checksum": "6f7900bbd5e1f94bde83a31a9f214464dffced941502a4fc0cd82842de6a5f62" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-605.glb", + "checksum": "85fdf8467eff2a0d57ea3349b584f2d153a5f592b906c76f19c8d17ecdf9ea53" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-606.glb", + "checksum": "dccc39520bd2b6317fd0f16fdd5269fe3517bee08552188aa082a3d9b06c8ad8" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-607.glb", + "checksum": "14c7adba753733801587d3f9d5ca837abbb811dcb38be277ffaf982c2fdc9f35" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-608.glb", + "checksum": "6113ea60297145e954b1d0c8e7b445c086a4f88688bf8293dbebdebd40fd3a7c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-609.glb", + "checksum": "e42a19651658fc3c63d4ac1e3a48c88cfddd8403cc5679ed5a479edc90f73073" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-610.glb", + "checksum": "038ac0cd1da150fdd8215595e1a6f775e408a5c73510537ca6866baf0f6c0d86" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-611.glb", + "checksum": "672e9305dd74bcdad746b9d0f83f197816b1f9bc048cab4c5940812fe3f62d0a" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-612.glb", + "checksum": "c3a991be300a0079eeda57c9e3e79aca5fe015b5a94c39a1d08f263d6a34cd97" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-613.glb", + "checksum": "d007f826057d75f4e0eb10ddaa66c86afab78cbbf97609f69e212c922ecc8116" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-614.glb", + "checksum": "12a5a7bf5f1dc409a64299eef0cec2c9a3704b05670e2e06cd1afc31b12a3332" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-615.glb", + "checksum": "5fed406a35c9e86678532678728e59e0b5b2d5b5b9fc62681b87a975f85dd66c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-616.glb", + "checksum": "0906253f7855badb9ccb507e30444db49c1e3bbc7d597f7b248b5471823ca7a1" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-617.glb", + "checksum": "d20b59a684b3db2a466ac5251bdd72f46e035e7cd5ea1ff7331747c54cf464b9" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-618.glb", + "checksum": "9c7bcda74276c0538d37aa39f7142faf36c71a3d794f5722891ff285317cb3b7" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-619.glb", + "checksum": "98fb8cfb22366d5b85f9ee1a7dcfda33505ec85d1a37ea7abc8a9733ef8c3230" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-620.glb", + "checksum": "4ee0b32f0510306286f1017e1785f67906de6a4b9f3166f6f89bb4a9d2394801" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-621.glb", + "checksum": "02455e47b3e3f764c36b2ac165d06cb1e991214329c454ba88576cb02f27e538" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-622.glb", + "checksum": "e8c15c0ee120005691890b7cfad0fb6479f7458eaaaf58d4da3f891fb65fc3d0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-623.glb", + "checksum": "8435f709917c07e0e8236035b022b41cd470e9a74044f7eaa67e007c5699dbd1" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-624.glb", + "checksum": "d368e63a870b871d2f69f4bd57fe864e9c903c4d8412fdce4e5c031ed670d32a" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-625.glb", + "checksum": "455f6eee3c824c45ded29494f1ea9108adbf8ba7eab06dae6063d33c006b498e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-626.glb", + "checksum": "dfb78ce5c6efe01ff1ff98290fb6ee1f83fe287e1dd2e1be72dc38a734eb0389" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-627.glb", + "checksum": "1493ff60e68abebf5425f3be3501e74b1787f465a5a2099ec62a90237905c3f5" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-628.glb", + "checksum": "35bc67edfd9e5b1ed58d42e6cc16776558e75e0e26d43a5a5fc7f6313fd0e0aa" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-629.glb", + "checksum": "ac3121921097d3bea4644e47f80a99cb49fda1c68166ec815a4b803913fb51b0" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-630.glb", + "checksum": "7aa726344cda59fc6f6584638eece630fcf4cf68af73961776c21893b2e99974" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-631.glb", + "checksum": "e007dc46e65ca45034d23998d059679be5ff00d9ed9ca1017bd77cefa1583a8d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-632.glb", + "checksum": "7a36c880bda19eba5c80492c1bc23e07bbb51698c5f9a19f27515e20b4ea7280" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-633.glb", + "checksum": "6c6bed0dd52a9e0b2256be6cc1b18a51ee0f172cbc202957410539e221d65674" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-634.glb", + "checksum": "ce93d8256b2ad03c781166be36f8fc29d07e2797b7d6e081bd1ade8de4e52f2d" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-635.glb", + "checksum": "7e02a8311cbd37909a5eca172875f69587244444e5d224540f9ff8e9b3d0fb5b" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-636.glb", + "checksum": "d09834286ccd790eaf49a44dd534301525d120ea93fe696053a57cc7e7e61ef5" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-637.glb", + "checksum": "2ebb87becb9e1996736512f46ff2a08c019772bc4530e6c1a35e3c4a2165e542" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-638.glb", + "checksum": "96c2c8f5e55f18f9284f85eb5beac98ae499c528e08dc48db8dff2577892d25a" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-639.glb", + "checksum": "eac693d9651b818b3257189ef39bc4ed6a83500751ff3c79620760af1dfac47e" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-640.glb", + "checksum": "d4ef2920d6faf84e5b2eb4860884e99deb157bd75c12234c2ba1f16140650624" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-641.glb", + "checksum": "a4201c076f6506f4ffde04d5724ca4529e081da76a6d575b4d9ea298a95e4a94" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-642.glb", + "checksum": "2d9a9959e6cbc39b47ed2a640189762b45f383d3442fcb9d438022fe8fc23a09" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-643.glb", + "checksum": "0586189ca6cb867fbf21c8fa5f664c3e7a0ee38f6550eb2608f589021ac56e42" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-644.glb", + "checksum": "1cf650bec1b052b32d3431734ec150bb05851dd76dfb8a9f888cdad9b6453697" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-645.glb", + "checksum": "f9c72ac7a0487d222ecd59ab4d339be01e8246576492600040cf9d4ed3cc0faf" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-646.glb", + "checksum": "1f0ebaf1850e62a71dd0f3c2b52b248cfa269457b4411c9f30b1c7cb0e991611" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-647.glb", + "checksum": "8b57d2759e8030e2760fd71183f7ba8c0f9e12cdc0a7b9f1c4cf3428998fc7b5" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-648.glb", + "checksum": "1e581eaecf353c2b915ad00a726cb4263ba569ef5523cb678296b0b89ad53aaa" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-649.glb", + "checksum": "19ddab059325eb2f6b67c8943a0eaccef3aa8ca48af02c9bbf9d06167bbb782f" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-650.glb", + "checksum": "2fb952ab86d0601d9bdab6e6307da72964779a6a6dc26ee0741d124b255b373c" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-651.glb", + "checksum": "72103747d522d0b85027130956305d9f829b05be549247aacdb65475314d51b8" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-652.glb", + "checksum": "d19142cb939e3b1493ec1c43b4e7b00a7e4e8075d769971a7af8b77b0736cd61" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-653.glb", + "checksum": "26f6ad333dc2d43899e30bcc86207f76acdd55752e1640901b4f1c228b6adbd6" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-654.glb", + "checksum": "ef5f0c188b9379203f8411d72dc6e3a1634cab8fa0695742a6f87d213ceda164" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-655.glb", + "checksum": "82a455b03cf87e5561fe28d78fb4b6b5f44ad960adf6752d8bcc17873f9f9f76" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-656.glb", + "checksum": "2f2f1caeaee27ed90d1c40b3bd33ed0210c2dbf9afb010c10679ccbffc580ef8" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-657.glb", + "checksum": "29c54579caf64b3c6b18db357735d36e401dd01fc1b6a871949bad13bfbf4aa1" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-658.glb", + "checksum": "cc9dde875197c719915ad8443b9279984b2a8377717e5b433a1f838333b944c5" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-659.glb", + "checksum": "933892c7f2f933fecb915b22aa45591b5d166f4970e820dd7750f74e2822a324" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-660.glb", + "checksum": "399b01bc91876cf61b32f1f639003429c705e6d1b4a59ff24087ba72bd574f40" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-661.glb", + "checksum": "44064d1c7dc6177182f0fbc1cbc6c85fcce6a8129327ae8381eb286dbb7552bf" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-662.glb", + "checksum": "14050cf26baba5071fe1d38c537a71070fef41b85aefd768b0a6b3897c44cc78" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-663.glb", + "checksum": "2eb2faa7de60c96df2bd9dcc50039c236ee4e9d1cc4d135f5663299426161cca" + }, + { + "url": "/assets/game/manastorm/1781-forgottenmine/collision-664.glb", + "checksum": "c7669ad7750424665a5f23248c75cbfa0cac4cab802bbd300dfeb2528ac83641" + } + ], + "navigation": { + "id": "navigation", + "fileName": "navigation.glb", + "url": "/assets/game/manastorm/1781-forgottenmine/navigation.glb", + "checksum": "aeb76952fab6a4494febff8fdeccab8d1d56fd01f5a51c4cc72b4b1a42501874", + "size": 5360496, + "triangleCount": 440319 + }, + "ramps": [], + "candidateCount": 4, + "rejectedCount": 4 + } + } +} diff --git a/src/game/partyNavigationAssetCache.ts b/src/game/partyNavigationAssetCache.ts index fa2e6321..3a6319c3 100644 --- a/src/game/partyNavigationAssetCache.ts +++ b/src/game/partyNavigationAssetCache.ts @@ -1,6 +1,6 @@ import { BufferGeometry, Float32BufferAttribute, type Group, type Mesh } from "three"; import type { DungeonNavigationLink, DungeonTraversalRamp } from "./dungeonTypes"; -import { bridgeDungeonStairNavigation, smoothDungeonStairGeometry } from "./dungeonTraversalRamps"; +import { bridgeDungeonStairNavigation, dungeonRampNavigationBridges, smoothDungeonStairGeometry } from "./dungeonTraversalRamps"; import { buildPartyNavigationGraph, type PartyNavigationGraph, @@ -77,7 +77,7 @@ export function preparePartyNavigationGraph( smoothDungeonStairGeometry(geometry, ramps, 0.05); bridgeDungeonStairNavigation(geometry, ramps, 0.05); } - const bridgedLinks = new Set(ramps.map((ramp) => ramp.navigationBridge.linkId)); + const bridgedLinks = new Set(ramps.flatMap((ramp) => dungeonRampNavigationBridges(ramp).map((bridge) => bridge.linkId))); graph = buildPartyNavigationGraph(geometry, { maxSourceTriangles: MAX_SYNCHRONOUS_PARTY_NAVIGATION_TRIANGLES, offMeshLinks: offMeshLinks.filter((link) => !bridgedLinks.has(link.id)), diff --git a/src/game/romDungeonHealth.test.ts b/src/game/romDungeonHealth.test.ts new file mode 100644 index 00000000..e5feeb47 --- /dev/null +++ b/src/game/romDungeonHealth.test.ts @@ -0,0 +1,151 @@ +import { afterAll, describe, expect, it } from "vitest"; +import { mkdirSync, writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { useCombatStore } from "./combatStore"; +import { usePartyStore } from "./partyStore"; +import { dungeonEnemyHealthBaseline, scaledDungeonEnemyLevel } from "./dungeonLevelScaling"; +import { BARREN_CAVES_ENTITIES } from "./generated/barrenCavesPopulation.generated"; +import { clearMobRuntimeRegistry, setMobPosition } from "./mobRuntimeRegistry"; +import { advanceDungeonPartyCombat, partyCombatRangeBand, resetPartyRuntimeClock } from "./partyRuntime"; +import { clearPartyRuntimeRegistry, registerPartyRuntimePosition, updatePartyRuntimeTargetVisibility } from "./partyRuntimeRegistry"; +import { subscribeCombatEvents } from "./combatEvents"; +import { abilitiesForClass } from "./abilityCatalog"; + +const report: unknown[] = []; +const trash = BARREN_CAVES_ENTITIES["rw-barren-caves-101456"]; +const boss = BARREN_CAVES_ENTITIES["rw-barren-caves-101462"]; + +afterAll(() => { + if (process.env.ROM_DUNGEON_HEALTH_REPORT !== "1") return; + const directory = resolve("artifacts/rom-dungeon-health"); + mkdirSync(directory, { recursive: true }); + writeFileSync(resolve(directory, "report.json"), `${JSON.stringify({ results: report }, null, 2)}\n`); +}); + +function initializePull(level: number, seed: string, maxHealth: number, isBoss = false): void { + clearMobRuntimeRegistry(); + clearPartyRuntimeRegistry(); + resetPartyRuntimeClock(); + useCombatStore.getState().initializeCharacter({ classId: "rom-priest", raceId: "rom-human", level }); + useCombatStore.getState().setPlayerPosition([-18, 0, 0]); + usePartyStore.getState().initializeParty(seed, level, useCombatStore.getState().health, "healer", 5, "rom"); + useCombatStore.getState().synchronizeActors(); + const definition = isBoss ? boss : trash; + useCombatStore.getState().registerMob("pull", { + name: definition.name, + boss: isBoss, + level: scaledDungeonEnemyLevel(level, isBoss), + maxHealth, + healthMultiplier: definition.combat.healthMultiplier, + attacks: definition.combat.attacks, + xpReward: 0, + }); + setMobPosition("pull", [0, 0, 0]); + for (const member of usePartyStore.getState().members) { + registerPartyRuntimePosition(member.id, [-partyCombatRangeBand(member).preferred, 0, 0]); + updatePartyRuntimeTargetVisibility(member.id, "pull", true); + } + useCombatStore.getState().damageMob("pull", 1, 100); +} + +function measurePull(level: number, seed: string, maxHealth: number, isBoss = false) { + initializePull(level, seed, maxHealth, isBoss); + let firstDamageAt: number | null = null; + let diedAt: number | null = null; + let openingHealth = maxHealth; + const stop = subscribeCombatEvents((event) => { + if (event.targetActorId !== "pull") return; + if (event.kind === "damage" && event.effectiveAmount > 0) firstDamageAt ??= event.occurredAt; + }); + try { + for (let now = 1_000; now <= 121_000; now += 100) { + useCombatStore.getState().tick(0.1, now); + advanceDungeonPartyCombat(now); + const mob = useCombatStore.getState().mobs.pull; + if (firstDamageAt !== null && now <= firstDamageAt + 1_000) openingHealth = mob.health; + if (mob.dead) { + diedAt = mob.diedAt ?? now; + break; + } + } + return { maxHealth, seconds: diedAt === null || firstDamageAt === null ? null : (diedAt - firstDamageAt) / 1_000, + openingHealth, classes: usePartyStore.getState().members.map((member) => member.classId) }; + } finally { + stop(); + } +} + +describe("RoM dungeon enemy durability", () => { + for (const level of [1, 2, 4, 8, 9, 20, 40, 70, 80]) { + it(`Barren Caves trash survives a full RoM party's opening volley at level ${level}`, () => { + for (let seed = 0; seed < 12; seed += 1) { + const partySeed = `rom-health-${seed}`; + const before = measurePull(level, partySeed, 72 + level * 22); + const after = measurePull(level, partySeed, dungeonEnemyHealthBaseline(level, "runewaker")!); + report.push({ level, seed: partySeed, before, after }); + expect(after.openingHealth, JSON.stringify({ level, before, after })).toBeGreaterThan(0); + expect(after.seconds, JSON.stringify({ level, before, after })).not.toBeNull(); + expect(after.seconds!, JSON.stringify({ level, before, after })).toBeGreaterThanOrEqual(3); + expect(after.seconds!, JSON.stringify({ level, before, after })).toBeLessThanOrEqual(30); + } + }); + } + + it("retains Barren Caves' authored boss health multiplier", () => { + const level = 8; + const maxHealth = dungeonEnemyHealthBaseline(scaledDungeonEnemyLevel(level, true), "runewaker")! + * boss.combat.healthMultiplier; + const result = measurePull(level, "rom-health-0", maxHealth, true); + report.push({ level, boss: boss.name, ...result }); + expect(result.seconds).not.toBeNull(); + expect(result.seconds!).toBeGreaterThanOrEqual(20); + expect(result.seconds!).toBeLessThanOrEqual(120); + }); + + it("allows enemy attacks and Priest healing before an opening pull ends", () => { + const level = 1; + initializePull(level, "rom-health-0", dungeonEnemyHealthBaseline(level, "runewaker")!); + const heal = abilitiesForClass("rom-priest").find((ability) => ability.name === "Urgent Heal")!; + let incomingDamage = 0; + let healing = 0; + const stop = subscribeCombatEvents((event) => { + if (event.sourceActorId === "pull" && event.kind === "damage") incomingDamage += event.effectiveAmount; + if (event.kind === "healing") healing += event.effectiveAmount; + }); + try { + for (let now = 1_000; now <= 31_000; now += 100) { + useCombatStore.getState().tick(0.1, now); + advanceDungeonPartyCombat(now); + useCombatStore.getState().advanceMobCombat(now); + const tank = usePartyStore.getState().members.find((member) => member.role === "tank")!; + if (tank.health < tank.maxHealth && !useCombatStore.getState().activeCast) { + usePartyStore.getState().selectMember(tank.id); + useCombatStore.getState().castAbility(heal.id, undefined, now); + } + if (useCombatStore.getState().mobs.pull.dead) break; + } + expect(useCombatStore.getState().mobs.pull.dead).toBe(true); + expect(incomingDamage).toBeGreaterThan(0); + expect(healing).toBeGreaterThan(0); + expect(usePartyStore.getState().members.every((member) => member.health > 0)).toBe(true); + } finally { + stop(); + } + }); + + it("updates loaded enemies on level-up while retaining wounds and fallen enemies", () => { + const initial = dungeonEnemyHealthBaseline(1, "runewaker")!; + const scaled = dungeonEnemyHealthBaseline(8, "runewaker")!; + initializePull(1, "rom-health-level-up", initial); + useCombatStore.getState().damageMob("pull", initial / 2 - 1, 1_000); + useCombatStore.getState().registerMob("pull", { name: trash.name, level: 8, maxHealth: scaled }); + expect(useCombatStore.getState().mobs.pull).toMatchObject({ level: 8, maxHealth: scaled, health: scaled / 2, + attackDamage: 20 }); + useCombatStore.getState().registerMob("pull", { name: trash.name, level: 8, maxHealth: scaled }); + expect(useCombatStore.getState().mobs.pull.health).toBe(scaled / 2); + useCombatStore.getState().damageMob("pull", scaled, 2_000); + useCombatStore.getState().registerMob("pull", { name: trash.name, level: 20, + maxHealth: dungeonEnemyHealthBaseline(20, "runewaker") }); + expect(useCombatStore.getState().mobs.pull).toMatchObject({ dead: true, health: 0 }); + }); +}); diff --git a/src/game/runewakerCompletion.ts b/src/game/runewakerCompletion.ts new file mode 100644 index 00000000..6a0dcf91 --- /dev/null +++ b/src/game/runewakerCompletion.ts @@ -0,0 +1,38 @@ +import type { DungeonDefinition } from "./dungeonTypes"; +import type { DungeonEncounterDefinition } from "./dungeonEncounterTypes"; + +/** Authored completion bookkeeping, independent of reviewed RoM encounter scripts. */ +export function withRunewakerCompletion(definition: DungeonDefinition): DungeonDefinition { + if (definition.expansion !== "runewaker" || definition.bosses.length === 0) return definition; + const objectives = [...new Map([...definition.objectiveOrder, ...definition.bosses] + .map((objective) => [objective.id, objective] as const)).values()]; + const encounter: DungeonEncounterDefinition = { + id: `${definition.id}:completion-baseline`, + dungeonId: definition.id, + initialPhase: "running", + initialState: { flags: { "dungeon-complete": false } }, + source: { + fidelity: "healer-man-authored", scope: "instance-orchestration", + files: ["src/game/runewakerCompletion.ts"], + reviewedBehavior: "Records defeated dungeon bosses and completes the required objectives.", + adaptations: ["Completion bookkeeping uses runtime spawn ids and persists across checkpoints."], + remainingGaps: [], + }, + rules: [ + ...objectives.filter((objective) => !objective.eventOnly).map((objective) => ({ + id: `defeated:${objective.id}`, + trigger: { kind: "entities-dead" as const, entityIds: [objective.id] }, + actions: [{ id: `record:${objective.id}`, kind: "complete-objective" as const, + objectiveId: objective.id, persistence: "run" as const }], + })), + { + id: "complete", trigger: { kind: "tick" }, + guards: objectives.filter((objective) => !objective.optional).map((objective) => ({ + kind: "objective-complete", objectiveId: objective.id, equals: true, + })), + actions: [{ id: "complete", kind: "set-flag", flagId: "dungeon-complete", value: true, persistence: "run" }], + }, + ], + }; + return { ...definition, encounters: [...definition.encounters ?? [], encounter] }; +} diff --git a/src/game/store.ts b/src/game/store.ts index 226fdf08..cc020ba4 100644 --- a/src/game/store.ts +++ b/src/game/store.ts @@ -8,7 +8,7 @@ import type { GmReviewTarget } from "./gmContentReview"; export type AssetStatus = "checking" | "loading" | "ready" | "proxy" | "error"; export type InputMode = "keyboard" | "gamepad"; export type GameMode = "dungeon" | "manastorm"; -export type GameOverlay = "pause" | "report-issue" | "party" | "inventory" | "map" | "boss-guide" | "spellbook" | "talents" | "bindings" | "options" | "ui-layout" | "keybinds" | "gm" | null; +export type GameOverlay = "pause" | "report-issue" | "party" | "inventory" | "map" | "boss-guide" | "spellbook" | "talents" | "bindings" | "options" | "ui-layout" | "keybinds" | "gm" | "dungeon-results" | null; interface PlayerSnapshot { position: Vector3Tuple; diff --git a/src/game/wowDungeonEnvironment.test.ts b/src/game/wowDungeonEnvironment.test.ts new file mode 100644 index 00000000..657fb3c4 --- /dev/null +++ b/src/game/wowDungeonEnvironment.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it } from "vitest"; +import { DUNGEON_DEFINITIONS, requireDungeonDefinition } from "./dungeonRegistry"; +import { FORSAKEN_ABBEY_DEFINITION } from "./dungeons/forsakenAbbey"; +import { WOW_DUNGEON_LIGHTING, withWowDungeonEnvironment, wowDungeonLightingProfile } from "./wowDungeonEnvironment"; +import stairs from "./generated/wowDungeonStairs.generated.json"; +import stockadesOverride from "../../dungeon-pipeline/runtime-overrides/stormwind-stockade.json"; + +describe("fleet-wide WoW dungeon environment policy", () => { + const wowDungeons = DUNGEON_DEFINITIONS.filter((dungeon) => dungeon.expansion !== "runewaker"); + + it("gives every WoW dungeon readable ambient and hemisphere fill without changing fog draw distance or material roles", () => { + expect(wowDungeons).toHaveLength(54); + for (const dungeon of wowDungeons) { + const profile = WOW_DUNGEON_LIGHTING[wowDungeonLightingProfile(dungeon.id)]; + expect(dungeon.presentation.ambientLight).toEqual(profile.ambientLight); + expect(dungeon.presentation.hemisphereLight).toEqual(profile.hemisphereLight); + expect(dungeon.presentation.fog.far).toBeGreaterThan(dungeon.presentation.fog.near); + const updated = withWowDungeonEnvironment(dungeon); + expect(updated.presentation.materials).toBe(dungeon.presentation.materials); + expect(updated.assets).toBe(dungeon.assets); + expect(updated.validation).toBe(dungeon.validation); + } + }); + + it("keeps Stockades' reviewed lighting, ramps and navigation links intact", () => { + const stockades = requireDungeonDefinition("stormwind-stockade"); + // The existing campaign compiler stores package vectors at four decimals. + const compiled = (value: unknown) => JSON.parse(JSON.stringify(value, (_key, item) => typeof item === "number" ? Number(item.toFixed(4)) : item)); + expect(stockades.traversalRamps).toEqual(compiled(stockadesOverride.traversalRamps)); + expect(stockades.navigationLinks).toEqual(compiled(stockadesOverride.navigationLinks)); + expect(stockades.presentation.directionalLight).toEqual(stockadesOverride.presentation.directionalLight); + }); + + it("does not apply WoW changes to RuneWaker environments", () => { + expect(withWowDungeonEnvironment(FORSAKEN_ABBEY_DEFINITION)).toBe(FORSAKEN_ABBEY_DEFINITION); + }); + + it("records a scan for every shipped WoW dungeon and installs only package-matched proxies", () => { + expect(Object.keys(stairs.dungeons).sort()).toEqual(wowDungeons.map((dungeon) => dungeon.id).sort()); + for (const dungeon of wowDungeons) { + const record = (stairs.dungeons as Record)[dungeon.id]; + if (record.ramps.length) expect(dungeon.traversalRamps).toEqual(record.ramps); + } + }); + + it("rejects stale ramp coordinates when collision content is replaced", () => { + const dungeon = wowDungeons.find((entry) => entry.id !== "stormwind-stockade" && entry.traversalRamps?.length)!; + expect(dungeon).toBeDefined(); + const replaced = { ...dungeon, traversalRamps: undefined, assets: { ...dungeon.assets, collision: dungeon.assets.collision.map((asset) => ({ ...asset, checksum: "replacement" })) } }; + expect(withWowDungeonEnvironment(replaced).traversalRamps).toBeUndefined(); + }); +}); diff --git a/src/game/wowDungeonEnvironment.ts b/src/game/wowDungeonEnvironment.ts new file mode 100644 index 00000000..b498eef4 --- /dev/null +++ b/src/game/wowDungeonEnvironment.ts @@ -0,0 +1,85 @@ +import type { DungeonDefinition, DungeonPresentation, DungeonTraversalRamp } from "./dungeonTypes"; +import stairCatalog from "./generated/wowDungeonStairs.generated.json"; + +type LightingProfile = Pick & { + readonly fog: Pick; +}; + +// Imported WoW environments do not carry the original client's baked lighting. +// Stockades' reviewed fill levels are the baseline; color remains environment-specific. +export const WOW_DUNGEON_LIGHTING: Readonly> = { + stone: { + background: "#171815", fog: { color: "#1b1c18", near: 44 }, + ambientLight: { color: "#d8c9aa", intensity: 0.58 }, + hemisphereLight: { skyColor: "#cfc3a5", groundColor: "#292820", intensity: 0.86 }, + directionalLight: { color: "#ffe0a8", intensity: 1.1, offset: [10, 18, -5] }, + }, + cavern: { + background: "#121914", fog: { color: "#172019", near: 44 }, + ambientLight: { color: "#bcccaf", intensity: 0.58 }, + hemisphereLight: { skyColor: "#b2c6b5", groundColor: "#242b22", intensity: 0.86 }, + directionalLight: { color: "#dce5bc", intensity: 1.1, offset: [10, 18, -5] }, + }, + fire: { + background: "#211612", fog: { color: "#291c17", near: 44 }, + ambientLight: { color: "#d3bca9", intensity: 0.58 }, + hemisphereLight: { skyColor: "#d1bca6", groundColor: "#30251f", intensity: 0.86 }, + directionalLight: { color: "#ffd3a1", intensity: 1.1, offset: [10, 18, -5] }, + }, + arcane: { + background: "#161827", fog: { color: "#1c2030", near: 44 }, + ambientLight: { color: "#c4c8de", intensity: 0.58 }, + hemisphereLight: { skyColor: "#bdc9e6", groundColor: "#25283a", intensity: 0.86 }, + directionalLight: { color: "#e0e9ff", intensity: 1.1, offset: [10, 18, -5] }, + }, + crypt: { + background: "#171721", fog: { color: "#1d1d28", near: 44 }, + ambientLight: { color: "#c8c3d1", intensity: 0.58 }, + hemisphereLight: { skyColor: "#c0c2d3", groundColor: "#282530", intensity: 0.86 }, + directionalLight: { color: "#e7ddcf", intensity: 1.1, offset: [10, 18, -5] }, + }, + outdoor: { + background: "#293330", fog: { color: "#35423e", near: 80 }, + ambientLight: { color: "#d4dccb", intensity: 0.58 }, + hemisphereLight: { skyColor: "#c9d9e5", groundColor: "#30382b", intensity: 0.86 }, + directionalLight: { color: "#fff0cd", intensity: 1.1, offset: [10, 18, -5] }, + }, +}; + +const PROFILE_DUNGEONS: Readonly> = { + cavern: ["wailing-caverns", "razorfen-kraul", "razorfen-downs", "blackfathom-deeps", "maraudon", "underbog", "slave-pens", "steamvault"], + fire: ["ragefire-chasm", "blackrock-depths", "blackrock-spire", "hellfire-ramparts", "blood-furnace", "shattered-halls"], + arcane: ["gnomeregan", "arcatraz", "botanica", "mechanar", "magisters-terrace", "the-nexus", "the-oculus", "violet-hold", "tinkertech-showdown"], + crypt: ["shadowfang-keep", "scholomance", "stratholme", "sunken-temple", "auchenai-crypts", "mana-tombs", "sethekk-halls", "shadow-labyrinth", "azjol-nerub", "ahnkahet-old-kingdom", "forge-of-souls", "halls-of-reflection"], + outdoor: ["zulfarrak", "black-morass", "escape-from-durnholde", "culling-of-stratholme", "trial-of-the-champion", "pit-of-saron", "frozen-reach"], +}; + +export function wowDungeonLightingProfile(id: string): string { + return Object.entries(PROFILE_DUNGEONS).find(([, ids]) => ids.includes(id))?.[0] ?? "stone"; +} + +/** Shared adapter also covers authored Wailing Caverns and survives campaign regeneration. */ +export function withWowDungeonEnvironment(definition: DungeonDefinition): DungeonDefinition { + if (definition.expansion === "runewaker") return definition; + const profile = WOW_DUNGEON_LIGHTING[wowDungeonLightingProfile(definition.id)]; + const stairs = (stairCatalog.dungeons as unknown as Readonly>)[definition.id]; + // Never apply package-space coordinates to a newly replaced environment. + const matchingAssets = stairs && definition.assets.collision.length === stairs.collision.length + && definition.assets.collision.every((asset, index) => asset.url === stairs.collision[index].url + && asset.checksum === stairs.collision[index].checksum); + const matchingNavigation = !stairs?.ramps.some((ramp) => ramp.navigationBridges?.length) + || (definition.assets.navigation?.url === stairs?.navigation?.url + && definition.assets.navigation?.checksum === stairs?.navigation?.checksum); + return { + ...definition, + presentation: definition.id === "stormwind-stockade" ? definition.presentation : { + ...definition.presentation, ...profile, + fog: { ...definition.presentation.fog, ...profile.fog }, + }, + ...(matchingAssets && matchingNavigation && stairs.ramps.length ? { traversalRamps: stairs.ramps } : {}), + }; +} diff --git a/src/game/wowDungeonStairAssets.test.ts b/src/game/wowDungeonStairAssets.test.ts new file mode 100644 index 00000000..bc03bb59 --- /dev/null +++ b/src/game/wowDungeonStairAssets.test.ts @@ -0,0 +1,81 @@ +import path from "node:path"; +import RAPIER from "@dimforge/rapier3d-compat"; +import { BufferGeometry, Group, Mesh } from "three"; +import { beforeAll, describe, expect, it } from "vitest"; +import type { DungeonTraversalRamp } from "./dungeonTypes"; +import stairs from "./generated/wowDungeonStairs.generated.json"; +import { requireDungeonDefinition } from "./dungeonRegistry"; +import { bridgeDungeonStairNavigation, smoothDungeonStairGeometry } from "./dungeonTraversalRamps"; +import { collisionTrimeshes, prepareCollision } from "../scene/productionDungeonCollision"; +import { DUNGEON_PHYSICAL_COLLISION_GROUPS } from "../scene/dungeonLineOfSight"; +import { createPartyDirectSegmentValidator } from "../scene/partyTraversal"; + +const pipelineModule = "../../scripts/dungeon-pipeline/wow-stair-geometry.ts"; +const { readDungeonGeometry } = await import(pipelineModule) as { + readDungeonGeometry(file: string): Promise; +}; +const packages = Object.entries(stairs.dungeons).filter(([, entry]) => entry.ramps.length); + +beforeAll(async () => { await RAPIER.init(); }); + +describe("shipping WoW dungeon stair proxies", () => { + it.each(packages)("%s supports live NPC capsule sweeps uphill/downhill in three lanes", async (id) => { + const definition = requireDungeonDefinition(id); + const ramps = definition.traversalRamps!; + const source = new Group(); + for (const asset of definition.assets.collision) { + for (const geometry of await readDungeonGeometry(path.join(process.cwd(), "public", asset.url!))) { + source.add(new Mesh(geometry)); + } + } + const prepared = prepareCollision(source, ramps); + const world = new RAPIER.World({ x: 0, y: -18, z: 0 }); + try { + for (const mesh of collisionTrimeshes(prepared)) { + world.createCollider(RAPIER.ColliderDesc.trimesh(Float32Array.from(mesh.vertices), Uint32Array.from(mesh.indices)) + .setCollisionGroups(DUNGEON_PHYSICAL_COLLISION_GROUPS)); + } + world.step(); + const allowed = createPartyDirectSegmentValidator(RAPIER, world); + for (const ramp of ramps) { + const dx = ramp.end[0] - ramp.start[0]; const dz = ramp.end[2] - ramp.start[2]; + const length = Math.hypot(dx, dz); + const count = Math.max(3, Math.ceil(length / 0.35) + 1); + const lateral = Math.max(0, Math.min(ramp.width / 4, ramp.width / 2 - 0.45)); + for (const lane of [0, -lateral, lateral]) { + const points = Array.from({ length: count }, (_, index): [number, number, number] => { + const t = index / (count - 1); + return [ramp.start[0] + dx * t - dz / length * lane, + ramp.start[1] + (ramp.end[1] - ramp.start[1]) * t, + ramp.start[2] + dz * t + dx / length * lane]; + }); + for (let index = 1; index < points.length; index += 1) { + const detail = `${id}/${ramp.id}, lane ${lane}, sample ${index}`; + expect(allowed(points[index - 1], points[index]), detail).toBe(true); + expect(allowed(points[index], points[index - 1]), detail).toBe(true); + } + } + } + } finally { + world.free(); + for (const group of [source, prepared]) group.traverse((node) => { if ((node as Mesh).isMesh) (node as Mesh).geometry.dispose(); }); + } + }, 120000); + + it("every generated seam matches actual shipping navigation vertices", async () => { + for (const [id] of packages) { + const definition = requireDungeonDefinition(id); + const ramps = definition.traversalRamps as readonly DungeonTraversalRamp[]; + const bridges = ramps.reduce((count, ramp) => count + (ramp.navigationBridges?.length ?? 0), 0); + if (!bridges) continue; + const geometries = await readDungeonGeometry(path.join(process.cwd(), "public", definition.assets.navigation!.url!)); + const geometry = geometries.sort((a, b) => b.getAttribute("position").count - a.getAttribute("position").count)[0]; + try { + const originalCount = geometry.index!.count; + smoothDungeonStairGeometry(geometry, ramps, 0.05); + expect(() => bridgeDungeonStairNavigation(geometry, ramps, 0.05)).not.toThrow(); + expect(geometry.index!.count).toBe(originalCount + bridges * 6); + } finally { geometries.forEach((entry) => entry.dispose()); } + } + }, 120000); +}); diff --git a/src/game/wowStairGeometry.test.ts b/src/game/wowStairGeometry.test.ts new file mode 100644 index 00000000..01f1b81c --- /dev/null +++ b/src/game/wowStairGeometry.test.ts @@ -0,0 +1,59 @@ +import { BufferGeometry, Float32BufferAttribute } from "three"; +import { describe, expect, it } from "vitest"; +import { bridgeDungeonStairNavigation, smoothDungeonStairGeometry } from "./dungeonTraversalRamps"; + +// The offline Node 24 pipeline uses native .ts specifiers; load it at runtime, +// without making those tool-only modules part of the browser TypeScript build. +const pipelineModule = "../../scripts/dungeon-pipeline/wow-stair-geometry.ts"; +const { detectDungeonStairRamps, dungeonStairNavigationBridges, dungeonStairRisers } = await import(pipelineModule); + +function staircase(steps: number, rotated = false) { + const vertices: number[] = []; + for (let step = 0; step < steps; step += 1) { + const x = step; + const y = step * 0.5; + const points = [[x, y, -2], [x, y + 0.5, -2], [x, y + 0.5, 2], [x, y, -2], [x, y + 0.5, 2], [x, y, 2]]; + for (const point of points) vertices.push(...(rotated ? [-point[2], point[1], point[0]] : point)); + } + const geometry = new BufferGeometry(); + geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3)); + return geometry; +} + +describe("WoW shipped stair detection", () => { + it.each([false, true])("detects repeated risers, including rotated stairs (%s)", (rotated) => { + const geometry = staircase(7, rotated); + const ramps = detectDungeonStairRamps(dungeonStairRisers(geometry)); + expect(ramps).toHaveLength(1); + expect(ramps[0].width).toBeCloseTo(4.02); + expect(ramps[0].end[1] - ramps[0].start[1]).toBeCloseTo(3.5); + geometry.dispose(); + }); + + it("does not interpret a single ledge or tall wall as a staircase", () => { + const ledge = staircase(1); + expect(detectDungeonStairRamps(dungeonStairRisers(ledge))).toEqual([]); + const wall = staircase(4); + const p = wall.getAttribute("position"); + for (let index = 0; index < p.count; index += 1) p.setY(index, p.getY(index) * 10); + expect(detectDungeonStairRamps(dungeonStairRisers(wall))).toEqual([]); + ledge.dispose(); wall.dispose(); + }); + + it("fills separate stair portals using existing source vertices, without bridging an unrelated floor", () => { + const geometry = new BufferGeometry(); + geometry.setAttribute("position", new Float32BufferAttribute([ + -1, 0, 0, 1, 0, 0, -1, 0.5, 2, 1, 0.5, 2, + -1, 0.75, 3, 1, 0.75, 3, -1, 1.5, 6, 1, 1.5, 6, + -1, 8, 3, 1, 8, 3, -1, 8, 4, + ], 3)); + geometry.setIndex([0, 1, 3, 0, 3, 2, 4, 5, 7, 4, 7, 6, 8, 9, 10]); + const ramps = dungeonStairNavigationBridges(geometry, [{ id: "flight", start: [0, 0, -0.01], end: [0, 1.5, 6.01], width: 3 }]); + expect(ramps[0].navigationBridges).toHaveLength(1); + smoothDungeonStairGeometry(geometry, ramps, 0.05); + expect(() => bridgeDungeonStairNavigation(geometry, ramps, 0.05)).not.toThrow(); + expect(geometry.index!.count).toBe(21); + expect(geometry.getAttribute("position").getY(8)).toBe(8); + geometry.dispose(); + }); +}); diff --git a/src/scene/MobPopulation.tsx b/src/scene/MobPopulation.tsx index c433abce..88a2370d 100644 --- a/src/scene/MobPopulation.tsx +++ b/src/scene/MobPopulation.tsx @@ -45,7 +45,8 @@ import { } from "../game/mobRuntimeRegistry"; import { combatHasLineOfSight } from "../game/combatSpatial"; import { registerDungeonRuntimeReset } from "../game/dungeonRuntimeReset"; -import { scaledDungeonEnemyLevel } from "../game/dungeonLevelScaling"; +import { dungeonEnemyHealthBaseline, scaledDungeonEnemyLevel } from "../game/dungeonLevelScaling"; +import { requireDungeonDefinition } from "../game/dungeonRegistry"; import { stepFearedMobAway } from "../game/mobMotion"; import { clusterStaticMobSpawns, @@ -884,6 +885,7 @@ function ProxyMob({ const selected = useCombatStore((state) => state.selectedTargetId === instanceId); const showAggroRanges = useCombatStore((state) => state.settings.showMobAggroRanges); const gameMode = useGameStore((state) => state.gameMode); + const dungeonExpansion = useGameStore((state) => requireDungeonDefinition(state.activeDungeonId).expansion); const characterLevel = useCombatStore((state) => state.level); const manastormLevel = useManastormStore((state) => state.level); const manastormPartySize = useManastormStore((state) => state.partySize); @@ -923,16 +925,22 @@ function ProxyMob({ useEffect(() => { const baseHealthMultiplier = definition.combat?.healthMultiplier ?? 1; const baseDamageMultiplier = definition.combat?.damageMultiplier ?? 1; + const level = gameMode === "manastorm" + ? definition.combat?.level + : scaledDungeonEnemyLevel(characterLevel, boss); + const healthMultiplier = baseHealthMultiplier * (manastormModifiers?.enemyStatMultiplier ?? 1); + const healthBaseline = gameMode === "dungeon" + ? dungeonEnemyHealthBaseline(level ?? characterLevel, dungeonExpansion) + : undefined; useCombatStore.getState().registerMob(instanceId, { name: definition.name, serverEntry: definition.combat?.serverEntry, boss, - level: gameMode === "manastorm" - ? definition.combat?.level - : scaledDungeonEnemyLevel(characterLevel, boss), + level, + maxHealth: healthBaseline === undefined ? undefined : healthBaseline * healthMultiplier, hasLoot: Boolean((boss && bossHasLoot(definition.id)) || definition.hasLoot), lootSourceId: boss ? definition.id : undefined, - healthMultiplier: baseHealthMultiplier * (manastormModifiers?.enemyStatMultiplier ?? 1), + healthMultiplier, damageMultiplier: baseDamageMultiplier * (manastormModifiers?.enemyStatMultiplier ?? 1), bonusLootChance: manastormModifiers?.bonusLootChance, moveSpeed: definition.combat?.moveSpeed, @@ -959,6 +967,7 @@ function ProxyMob({ definition.combat, instanceId, gameMode, + dungeonExpansion, manastormLevel, manastormMapRewardMultiplier, manastormModeId, diff --git a/src/ui/DungeonLeaderboard.tsx b/src/ui/DungeonLeaderboard.tsx new file mode 100644 index 00000000..ba134c08 --- /dev/null +++ b/src/ui/DungeonLeaderboard.tsx @@ -0,0 +1,70 @@ +import { DUNGEON_LEADERBOARD_PAGE_SIZE, type LeaderboardScope } from "../app/dungeonLeaderboardClient"; +import { formatDungeonDuration, type DungeonClearFilter, type DungeonLeaderboard as LeaderboardData } from "../game/dungeonRunTypes"; +import { ControllerButton } from "./ControllerButton"; +import { DungeonRunDetails } from "./DungeonRunDetails"; +import "./dungeonRuns.css"; + +export function DungeonLeaderboard({ dungeonTitle, data, scope, loading, filter, onFilter, + expanded, onExpand, offset, onPage, onRefresh, difficultyLabel, onDifficulty, + selectedId, select }: { + readonly dungeonTitle: string; + readonly data: LeaderboardData; + readonly scope: LeaderboardScope; + readonly loading: boolean; + readonly filter: DungeonClearFilter; + readonly onFilter: (filter: DungeonClearFilter) => void; + readonly expanded: ReadonlySet; + readonly onExpand: (id: string) => void; + readonly offset: number; + readonly onPage: (offset: number) => void; + readonly onRefresh: () => void; + readonly difficultyLabel: string; + readonly onDifficulty?: () => void; + readonly selectedId: string; + readonly select: (id: string) => void; +}) { + const controls = { selectedId, select }; + return
+
+

{scope === "world" ? "World leaderboard" : "This device · offline leaderboard"}

+

Fastest expeditions

+
+ onFilter("final")}>Final Boss clears All entries + onFilter("full")}>Full dungeon clears All bosses +
+
+ {onDifficulty ? {difficultyLabel} ↻ + : {difficultyLabel}} + {data.total} {data.total === 1 ? "run" : "runs"} + Refresh +
+
+
+ {loading ?

Loading run times…

+ : data.entries.length === 0 ?

No {filter === "full" ? "full dungeon" : "final boss"} clears yet. Complete a run to set the first time.

+ :
    + {data.entries.map((entry) =>
  1. + onExpand(entry.id)}> + #{entry.rank}{entry.party.find((member) => !member.npc)?.name ?? entry.party[0]?.name ?? "Party"} + {entry.fullClear ? "All bosses" : `${entry.killedBossIds.length} bosses`} · {new Date(entry.finishedAt).toLocaleDateString()} + + + {expanded.has(entry.id) ?
    : null} +
  2. )} +
} +
+ {data.total > DUNGEON_LEADERBOARD_PAGE_SIZE ? : null} +
; +} diff --git a/src/ui/DungeonRunDetails.tsx b/src/ui/DungeonRunDetails.tsx new file mode 100644 index 00000000..c45a244d --- /dev/null +++ b/src/ui/DungeonRunDetails.tsx @@ -0,0 +1,32 @@ +import catalog from "../game/generated/dungeonLeaderboardCatalog.generated.json"; +import type { DungeonRunMember } from "../game/dungeonRunTypes"; + +const className = (id: string) => catalog.classes.find((entry) => entry.id === id)?.name ?? id; + +export function DungeonRunDetails({ dungeonId, difficultyId, party, killedBossIds }: { + readonly dungeonId: string; + readonly difficultyId: string; + readonly party: readonly DungeonRunMember[]; + readonly killedBossIds: readonly string[]; +}) { + const dungeon = catalog.dungeons.find((entry) => entry.id === dungeonId); + const ids = dungeon?.difficulties.find((entry) => entry.id === difficultyId)?.bossIds ?? []; + const bosses = dungeon?.bosses.filter((boss) => ids.includes(boss.id)) ?? []; + return
+

Party

+
    + {party.map((member, index) =>
  • + {member.name}{member.npc ? NPC : null} + Level {member.level} · {className(member.classId)}{member.secondaryClassId ? ` / ${className(member.secondaryClassId)}` : ""} + {member.role === "damage" ? "Damage" : member.role === "healer" ? "Healer" : "Tank"} +
  • )} +
+

Bosses defeated · {killedBossIds.length}/{bosses.length}

+
    + {bosses.map((boss) =>
  • + + {boss.name}{killedBossIds.includes(boss.id) ? "Defeated" : "Not defeated"} +
  • )} +
+
; +} diff --git a/src/ui/DungeonRunResults.tsx b/src/ui/DungeonRunResults.tsx new file mode 100644 index 00000000..279ccc3c --- /dev/null +++ b/src/ui/DungeonRunResults.tsx @@ -0,0 +1,103 @@ +import { useEffect, useMemo, useRef, useState } from "react"; +import { useShellStore } from "../app/shellStore"; +import { awaitSharedDungeonRunPlacement, localDungeonRunPlacement, saveLocalDungeonRun, submitDungeonRun, type LeaderboardScope } from "../app/dungeonLeaderboardClient"; +import { useOnlineSessionStore } from "../app/onlineSessionStore"; +import { completedDungeonRun, useDungeonRunStore } from "../game/dungeonRunStore"; +import { formatDungeonDuration, type DungeonRunPlacement, type DungeonRunProgress } from "../game/dungeonRunTypes"; +import catalog from "../game/generated/dungeonLeaderboardCatalog.generated.json"; +import { useGameStore } from "../game/store"; +import { useMenuController } from "../input/useMenuController"; +import { ControllerButton } from "./ControllerButton"; +import { DungeonRunDetails } from "./DungeonRunDetails"; +import "./dungeonRuns.css"; + +export interface RunPlacementState { + readonly scope: LeaderboardScope; + readonly placement: DungeonRunPlacement | null; + readonly loading: boolean; +} + +export function DungeonRunResultsDialog({ progress, ranking, onFinish, onLoot, onRetry }: { + readonly progress: DungeonRunProgress; + readonly ranking: RunPlacementState; + readonly onFinish: () => void; + readonly onLoot: () => void; + readonly onRetry: () => void; +}) { + const definition = catalog.dungeons.find((dungeon) => dungeon.id === progress.dungeonId); + const fullClear = definition?.difficulties.find((entry) => entry.id === progress.difficultyId)?.bossIds + .every((id) => progress.killedBossIds.includes(id)) ?? false; + const controller = useMenuController([ + { id: "run-loot", run: onLoot }, { id: "run-finish", run: onFinish }, + ...(ranking.scope === "device" ? [{ id: "run-retry", run: onRetry }] : []), + ], { initialId: "run-loot", onBack: onLoot }); + const controls = { selectedId: controller.selectedId, select: controller.select }; + const panel = useRef(null); + useEffect(() => { + const previous = document.activeElement; + panel.current?.focus(); + return () => { if (previous instanceof HTMLElement && previous.isConnected) previous.focus(); }; + }, []); + return
+
{ + if (event.key !== "Tab") return; + const buttons = Array.from(panel.current?.querySelectorAll("button:not(:disabled)") ?? []); + const first = buttons[0]; const last = buttons.at(-1); + if (event.shiftKey && (document.activeElement === first || document.activeElement === panel.current)) { + event.preventDefault(); last?.focus(); + } else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first?.focus(); } + }}> +

{fullClear ? "Full dungeon clear" : "Final boss clear"}

+

{definition?.title ?? progress.dungeonId} complete

+

The final boss is defeated. Your expedition is complete.

+
+
+
Time to final boss{progress.timed && progress.durationMs !== null + ? formatDungeonDuration(progress.durationMs) : "Untimed"}
+
{ranking.scope === "world" ? "World leaderboard" : "This device leaderboard"} + {ranking.loading ? "Recording…" : ranking.placement ? `#${ranking.placement.finalRank}` : "Unranked"} + {ranking.placement ? of {ranking.placement.finalTotal} final boss clears : null} +
+
+ {ranking.placement?.fullRank ?

Full dungeon clears: #{ranking.placement.fullRank} of {ranking.placement.fullTotal}

: null} + {!progress.timed ?

This resumed run has no saved timer. Start a new expedition to record a leaderboard time.

: null} + {ranking.scope === "device" ?

Your time is saved on this device. Reconnect to submit it to the world leaderboard.

: null} +
+
+ Stay and loot + Return to main menu + {ranking.scope === "device" ? Retry submission : null} +
+
+
; +} + +export function DungeonRunResults() { + const progress = useDungeonRunStore((state) => state.progress); + const overlay = useGameStore((state) => state.overlay); + const record = useMemo(() => progress ? completedDungeonRun(progress) : null, [progress]); + const [retry, setRetry] = useState(0); + const [ranking, setRanking] = useState({ scope: "world", placement: null, loading: false }); + useEffect(() => { + if (!record) { setRanking({ scope: "world", placement: null, loading: false }); return; } + let live = true; + saveLocalDungeonRun(record); + setRanking({ scope: "world", placement: null, loading: true }); + const online = useOnlineSessionStore.getState().snapshot; + const submission = online?.activity.id === record.id && !online.authority + ? awaitSharedDungeonRunPlacement(record.id) : submitDungeonRun(record, useShellStore.getState().session); + void submission + .then((placement) => { if (live) setRanking({ scope: "world", placement, loading: false }); }) + .catch(() => { if (live) setRanking({ scope: "device", placement: localDungeonRunPlacement(record), loading: false }); }); + return () => { live = false; }; + }, [record, retry]); + if (overlay !== "dungeon-results" || !progress || progress.finishedAt === null) return null; + return setRetry((value) => value + 1)} + onLoot={() => useGameStore.getState().closeOverlay()} onFinish={() => { + useShellStore.getState().saveActiveDungeonRunCheckpoint(null); + useGameStore.getState().closeOverlay(); + useShellStore.getState().returnToMainMenu(); + }} />; +} diff --git a/src/ui/DungeonSelectScreen.tsx b/src/ui/DungeonSelectScreen.tsx index 51ab76b2..e28bef1d 100644 --- a/src/ui/DungeonSelectScreen.tsx +++ b/src/ui/DungeonSelectScreen.tsx @@ -21,7 +21,10 @@ import { import { useMenuController, type MenuAction } from "../input/useMenuController"; import { LazyLocalCompanionPanel } from "./LazyLocalCompanionPanel"; import { ControllerButton } from "./ControllerButton"; -import { DungeonMapGraphic } from "./DungeonMapGraphic"; +import { DungeonLeaderboard } from "./DungeonLeaderboard"; +import { useDungeonLeaderboard } from "./useDungeonLeaderboard"; +import type { DungeonClearFilter } from "../game/dungeonRunTypes"; +import { DUNGEON_LEADERBOARD_PAGE_SIZE } from "../app/dungeonLeaderboardClient"; import { BrandMark, ControllerLegend, FrontSurface } from "./FrontSurface"; import { OnlineGroupPanel } from "./OnlineGroupPanel"; import { useOnlineGroupStore } from "../app/onlineGroupStore"; @@ -77,6 +80,20 @@ export function DungeonSelectScreen() { ?? firstCategoryDungeon ?? null; const selectedDefinition = selectedDungeon ? dungeonDefinitionById(selectedDungeon.id) : null; + const [clearFilter, setClearFilter] = useState("final"); + const [difficultyId, setDifficultyId] = useState(""); + const [expandedRuns, setExpandedRuns] = useState>(new Set()); + const leaderboardDifficulty = selectedDefinition?.difficultyVariants.find((variant) => variant.id === difficultyId) + ?? selectedDefinition?.difficultyVariants.find((variant) => variant.id === selectedDefinition.defaultDifficultyId); + const leaderboard = useDungeonLeaderboard(selectedDungeon?.id ?? "", leaderboardDifficulty?.id ?? "normal", clearFilter); + const toggleRun = (id: string) => setExpandedRuns((current) => { + const next = new Set(current); if (next.has(id)) next.delete(id); else next.add(id); return next; + }); + const cycleDifficulty = () => { + const variants = selectedDefinition?.difficultyVariants ?? []; + const index = variants.findIndex((variant) => variant.id === leaderboardDifficulty?.id); + if (variants.length) setDifficultyId(variants[(index + 1) % variants.length].id); + }; const selectedPackInstalled = selectedDungeon ? dungeonContentIsUsable(selectedDungeon.id) : false; const canStart = Boolean(selectedDungeon?.available && selectedPackInstalled); @@ -176,8 +193,17 @@ export function DungeonSelectScreen() { id: `dungeon-${dungeon.id}`, run: () => selectDungeon(dungeon.id), enabled: true, - neighbors: index === 0 ? { up: `dungeon-category-${selectedCategory}` } : undefined, + neighbors: { ...(index === 0 ? { up: `dungeon-category-${selectedCategory}` } : {}), right: "leaderboard-final" }, })), + { id: "leaderboard-final", run: () => setClearFilter("final") }, + { id: "leaderboard-full", run: () => setClearFilter("full") }, + { id: "leaderboard-difficulty", run: cycleDifficulty, enabled: (selectedDefinition?.difficultyVariants.length ?? 0) > 1 }, + { id: "leaderboard-refresh", run: leaderboard.refresh }, + ...leaderboard.data.entries.map((entry) => ({ id: `leaderboard-run-${entry.id}`, run: () => toggleRun(entry.id) })), + { id: "leaderboard-prev", run: () => leaderboard.setOffset(Math.max(0, leaderboard.offset - DUNGEON_LEADERBOARD_PAGE_SIZE)), + enabled: !leaderboard.loading && leaderboard.offset > 0 }, + { id: "leaderboard-next", run: () => leaderboard.setOffset(leaderboard.offset + DUNGEON_LEADERBOARD_PAGE_SIZE), + enabled: !leaderboard.loading && leaderboard.offset + DUNGEON_LEADERBOARD_PAGE_SIZE < leaderboard.data.total }, ...(rolePickerOpen ? availableRoles.map((role) => ({ id: `dungeon-role-${role}`, @@ -193,7 +219,7 @@ export function DungeonSelectScreen() { run: () => setRolePickerOpen(false), }] : []), { id: "dungeon-back", run: returnToMainMenu }, - ], [availableRoles, categoryDungeons, downloading, firstCategoryDungeon, pendingDownload, returnToMainMenu, rolePickerOpen, selectedCategory, selectedDungeon]); + ], [availableRoles, categoryDungeons, downloading, firstCategoryDungeon, pendingDownload, returnToMainMenu, rolePickerOpen, selectedCategory, selectedDungeon, selectedDefinition, leaderboard, expandedRuns, leaderboardDifficulty]); const controller = useMenuController(actions, { initialId: firstAvailable ? `dungeon-${firstAvailable.id}` : "dungeon-back", onBack: pendingDownload @@ -289,17 +315,12 @@ export function DungeonSelectScreen() {
{selectedDungeon ? ( <> -
- {selectedDefinition ? ( - - ) : ( - - )} -
+ 1 ? cycleDifficulty : undefined} + selectedId={controller.selectedId} select={controller.select} />

{canStart ? "Playable expedition" : selectedDungeon.available ? "Content required" : "In development"}

{selectedDungeon.title}

diff --git a/src/ui/Hud.tsx b/src/ui/Hud.tsx index 337cd53c..81c74418 100644 --- a/src/ui/Hud.tsx +++ b/src/ui/Hud.tsx @@ -37,6 +37,8 @@ import { ThreatMeterWidget } from "./ThreatMeterWidget"; import { BossLootWindow } from "./BossLootWindow"; import { EditableHudElement } from "./EditableHudElement"; import { DpsMeter } from "./DpsMeter"; +import { useDungeonRunStore } from "../game/dungeonRunStore"; +import { formatDungeonDuration } from "../game/dungeonRunTypes"; import { DesktopActionBars } from "./DesktopActionBars"; import { currentUiDevice, useUiLayoutStore } from "../game/uiLayoutStore"; import { SummonFrames } from "./SummonFrames"; @@ -140,20 +142,28 @@ export function DefeatedPrompt({ export function DungeonCompletePrompt({ dungeonTitle, onFinish, + onResults, + durationMs, + finalBossOnly = false, }: { readonly dungeonTitle: string; readonly onFinish: () => void; + readonly onResults?: () => void; + readonly durationMs?: number | null; + readonly finalBossOnly?: boolean; }) { return (

{dungeonTitle} complete - Every required encounter is defeated. Loot the room or finish the run. + {finalBossOnly ? "Final boss defeated." : "Every required encounter is defeated."} Loot the room or finish the run. + {durationMs != null ? Time to final boss: {formatDungeonDuration(durationMs)} : null}

+ {onResults ? : null}
); } @@ -218,8 +228,9 @@ export function Hud() { activeDungeonEncounterRevision, activeDungeonEncounterRevision, ); - const dungeonComplete = gameMode === "dungeon" - && activeDungeonRunIsComplete(activeDungeonId); + const runProgress = useDungeonRunStore((state) => state.progress); + const finalBossDefeated = runProgress?.dungeonId === activeDungeonId && runProgress.finishedAt !== null; + const dungeonComplete = gameMode === "dungeon" && (finalBossDefeated || activeDungeonRunIsComplete(activeDungeonId)); useEffect(() => { const timer = window.setInterval(() => setClock(Date.now()), 100); @@ -384,8 +395,10 @@ export function Hud() { - {dungeonComplete && health > 0 ? ( - + {dungeonComplete ? ( + useGameStore.getState().openOverlay("dungeon-results") : undefined} /> ) : null} {feedback?.kind === "loot" ? (
diff --git a/src/ui/dungeonRuns.css b/src/ui/dungeonRuns.css new file mode 100644 index 00000000..5ebd2434 --- /dev/null +++ b/src/ui/dungeonRuns.css @@ -0,0 +1,102 @@ +.dungeon-leaderboard, .dungeon-results { + --run-gold: var(--front-gold, #d8ae57); + --run-green: var(--front-green-bright, #c8e28d); + --run-line: var(--front-line, rgba(204, 224, 180, .18)); + --run-muted: #b1b7a3; + color: #f0ecdc; +} +.dungeon-leaderboard { + display: flex; + min-width: 0; + min-height: 0; + flex-direction: column; + overflow: hidden; + border: 1px solid var(--run-line); + border-radius: 13px 4px 13px 4px; + background: rgba(4, 16, 11, .9); +} +.dungeon-leaderboard > header { padding: 15px 12px 10px; border-bottom: 1px solid var(--run-line); } +.dungeon-leaderboard h3 { margin: 6px 0 14px; color: var(--run-gold); font: 24px Georgia, serif; } +.dungeon-leaderboard__filters { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; } +.dungeon-leaderboard button { color: inherit; cursor: pointer; } +.dungeon-leaderboard__filters button { + min-height: 48px; + padding: 6px; + border: 1px solid var(--run-line); + border-radius: 5px; + background: #0c1a12; + font-size: 11px; +} +.dungeon-leaderboard__filters button[aria-pressed="true"] { border-color: var(--run-gold); background: #27301a; color: #f2dfaa; } +.dungeon-leaderboard__filters small { display: block; margin-top: 3px; color: var(--run-muted); font-size: 9px; } +.dungeon-leaderboard__meta { display: flex; margin-top: 10px; align-items: center; justify-content: space-between; gap: 7px; color: var(--run-muted); font-size: 10px; } +.dungeon-leaderboard__meta button { padding: 5px 2px; border: 0; background: transparent; color: var(--run-green); font-size: 10px; } +.dungeon-leaderboard__entries { min-height: 0; flex: 1; overflow: auto; overscroll-behavior: contain; } +.dungeon-leaderboard__entries ol { margin: 0; padding: 0; list-style: none; } +.dungeon-leaderboard__entries > ol > li { border-bottom: 1px solid var(--run-line); } +.dungeon-leaderboard__run { display: grid; width: 100%; min-height: 63px; grid-template-columns: 31px minmax(0, 1fr) auto 10px; gap: 7px; padding: 10px; align-items: center; border: 0; background: transparent; text-align: left; } +.dungeon-leaderboard__run:hover, .dungeon-leaderboard__run[aria-expanded="true"] { background: rgba(143, 181, 92, .09); } +.dungeon-leaderboard__run > b { color: var(--run-gold); font: 16px Georgia, serif; } +.dungeon-leaderboard__run > span { display: flex; min-width: 0; flex-direction: column; gap: 4px; } +.dungeon-leaderboard__run strong { overflow: hidden; font-size: 12px; text-overflow: ellipsis; white-space: nowrap; } +.dungeon-leaderboard__run small { color: var(--run-muted); font-size: 9px; } +.dungeon-leaderboard__run time { color: var(--run-green); font-size: 14px; font-variant-numeric: tabular-nums; } +.dungeon-leaderboard__run em { color: var(--run-gold); font-size: 16px; font-style: normal; } +.dungeon-leaderboard__empty { padding: 22px 15px; color: var(--run-muted); font-size: 12px; line-height: 1.7; } +.dungeon-leaderboard__pages { display: flex; padding: 8px; align-items: center; justify-content: space-between; gap: 5px; border-top: 1px solid var(--run-line); font-size: 9px; } +.dungeon-leaderboard__pages button { padding: 7px; border: 1px solid var(--run-line); border-radius: 4px; background: #122019; font-size: 10px; } +.dungeon-leaderboard button:disabled { opacity: .4; cursor: default; } +.dungeon-run-details { padding: 8px 12px 12px; } +.dungeon-run-details h4 { margin: 9px 0; color: var(--run-gold); font-size: 11px; font-weight: 500; } +.dungeon-run-details ul { margin: 0; padding: 0; list-style: none; } +.dungeon-run-party li { display: flex; padding: 6px 0; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 9px; border-bottom: 1px solid rgba(204, 224, 180, .07); } +.dungeon-run-party strong { font-size: 11px; } +.dungeon-run-party strong small { color: var(--run-muted); font-size: 8px; font-weight: 400; } +.dungeon-run-party span { width: 100%; order: 3; color: #d6dcc9; font-size: 10px; } +.dungeon-run-party li > small { color: var(--run-muted); font-size: 9px; } +.dungeon-run-bosses li { display: flex; padding: 5px 0; align-items: center; gap: 7px; color: var(--run-muted); font-size: 10px; } +.dungeon-run-bosses li.is-defeated { color: var(--run-green); } +.dungeon-run-bosses small { margin-left: auto; color: var(--run-muted); font-size: 8px; } +.dungeon-results-backdrop { z-index: 110; padding: 16px; pointer-events: auto; } +.dungeon-results { + display: flex; + width: min(580px, calc(100vw - 32px)); + max-height: calc(100dvh - 32px); + flex-direction: column; + padding: 23px; + border: 1px solid var(--run-gold); + border-radius: 12px; + outline: 0; + background: linear-gradient(145deg, #172319, #07100d); + box-shadow: 0 20px 65px #0009; +} +.dungeon-results > header { flex: 0 0 auto; } +.dungeon-results h2 { margin: 7px 0; color: #f3deb0; font: 29px Georgia, serif; } +.dungeon-results header > p:last-child { margin: 9px 0 16px; color: var(--run-muted); font-size: 12px; } +.dungeon-results__score { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; flex: 0 0 auto; } +.dungeon-results__score > div { display: flex; padding: 14px; flex-direction: column; gap: 6px; border: 1px solid var(--run-line); border-radius: 6px; background: #ffffff04; } +.dungeon-results__score small { color: var(--run-muted); font-size: 11px; } +.dungeon-results__score strong { color: var(--run-green); font: 31px Georgia, serif; font-variant-numeric: tabular-nums; } +.dungeon-results__score span { color: var(--run-muted); font-size: 10px; } +.dungeon-results__full-rank { margin: 12px 0 0; color: var(--run-gold); font-size: 12px; } +.dungeon-results__note { margin: 12px 0 0; color: var(--run-muted); font-size: 11px; line-height: 1.5; } +.dungeon-results__details { min-height: 40px; margin-top: 13px; overflow: auto; overscroll-behavior: contain; border-block: 1px solid var(--run-line); } +.dungeon-results__details .dungeon-run-details { padding-inline: 0; } +.dungeon-results > footer { display: flex; margin-top: 16px; flex: 0 0 auto; flex-wrap: wrap; gap: 8px; } +.dungeon-results > footer button { min-height: 38px; flex: 1; white-space: nowrap; } +@media (max-width: 960px) { + .dungeon-select-detail { grid-template-rows: minmax(145px, 1fr) minmax(120px, .8fr); gap: 9px; } + .dungeon-leaderboard > header { padding: 9px; } + .dungeon-leaderboard h3 { margin: 4px 0 8px; font-size: 19px; } + .dungeon-leaderboard__filters button { min-height: 39px; font-size: 10px; } + .dungeon-leaderboard__meta { margin-top: 5px; } + .dungeon-leaderboard__run { min-height: 52px; } +} +@media (max-height: 520px) { + .dungeon-results { padding: 13px; } + .dungeon-results h2 { font-size: 23px; } + .dungeon-results header > p:last-child { margin-bottom: 9px; } + .dungeon-results__score > div { padding: 8px 12px; } + .dungeon-results__score strong { font-size: 25px; } + .dungeon-results > footer { margin-top: 9px; } +} diff --git a/src/ui/dungeonRuns.test.ts b/src/ui/dungeonRuns.test.ts new file mode 100644 index 00000000..795337cb --- /dev/null +++ b/src/ui/dungeonRuns.test.ts @@ -0,0 +1,39 @@ +import { createElement } from "react"; +import { renderToStaticMarkup } from "react-dom/server"; +import { describe, expect, it } from "vitest"; +import { DungeonLeaderboard } from "./DungeonLeaderboard"; +import { DungeonRunResultsDialog } from "./DungeonRunResults"; +import catalog from "../game/generated/dungeonLeaderboardCatalog.generated.json"; +import type { DungeonRunProgress } from "../game/dungeonRunTypes"; + +const caves = catalog.dungeons.find((entry) => entry.id === "barren-caves")!; +const progress: DungeonRunProgress = { id: "ui-caves-test", dungeonId: caves.id, difficultyId: "normal", timed: true, + startedAt: 1000, finishedAt: 13345, durationMs: 12345, killedBossIds: [caves.finalBossId], + party: [{ name: "Tester", classId: "rom-priest", level: 3, role: "healer", npc: false }, + { name: "Garrick", classId: "warrior", level: 3, role: "tank", npc: true }] }; +const noop = () => undefined; +describe("dungeon results and leaderboard", () => { + it("announces the final boss, frozen time, both rankings, party levels, and explicit loot/menu choices", () => { + const html = renderToStaticMarkup(createElement(DungeonRunResultsDialog, { progress, + ranking: { scope: "world", loading: false, placement: { finalRank: 2, finalTotal: 10, fullRank: 1, fullTotal: 4 } }, + onFinish: noop, onLoot: noop, onRetry: noop })); + for (const text of ["Barren Caves complete", "00:12.3", "#2", "#1", "Anglo", "Level 3", "Garrick", "Stay and loot", "Return to main menu"]) + expect(html).toContain(text); + expect(html).toContain('aria-modal="true"'); + }); + it("labels disconnected rankings as device-only and offers submission retry", () => { + const html = renderToStaticMarkup(createElement(DungeonRunResultsDialog, { progress, + ranking: { scope: "device", loading: false, placement: { finalRank: 1, finalTotal: 1, fullRank: 1, fullTotal: 1 } }, + onFinish: noop, onLoot: noop, onRetry: noop })); + expect(html).toContain("This device leaderboard"); expect(html).toContain("Retry submission"); + }); + it("offers both clear filters and expandable party/boss records in the dungeon picker", () => { + const html = renderToStaticMarkup(createElement(DungeonLeaderboard, { dungeonTitle: caves.title, + data: { entries: [{ ...progress, durationMs: progress.durationMs!, finishedAt: progress.finishedAt!, fullClear: true, rank: 1 }], total: 1 }, + scope: "world", loading: false, filter: "full", onFilter: noop, expanded: new Set([progress.id]), + onExpand: noop, offset: 0, onPage: noop, onRefresh: noop, difficultyLabel: "Normal", selectedId: "", select: noop })); + for (const text of ["Final Boss clears", "All entries", "Full dungeon clears", "All bosses", "Anglo", "Level 3", "00:12.3"]) + expect(html).toContain(text); + expect(html).toContain('aria-expanded="true"'); + }); +}); diff --git a/src/ui/useDungeonLeaderboard.ts b/src/ui/useDungeonLeaderboard.ts new file mode 100644 index 00000000..8c75b66e --- /dev/null +++ b/src/ui/useDungeonLeaderboard.ts @@ -0,0 +1,32 @@ +import { useEffect, useState } from "react"; +import { fetchDungeonLeaderboard, localDungeonLeaderboard, subscribeDungeonLeaderboard, + type LeaderboardScope } from "../app/dungeonLeaderboardClient"; +import type { DungeonClearFilter, DungeonLeaderboard } from "../game/dungeonRunTypes"; + +export function useDungeonLeaderboard(dungeonId: string, difficultyId: string, filter: DungeonClearFilter) { + const [offset, setOffset] = useState(0); + const [revision, setRevision] = useState(0); + const [state, setState] = useState<{ data: DungeonLeaderboard; scope: LeaderboardScope; loading: boolean }>({ + data: { entries: [], total: 0 }, scope: "world", loading: true, + }); + useEffect(() => { setOffset(0); }, [dungeonId, difficultyId, filter]); + useEffect(() => subscribeDungeonLeaderboard(() => setRevision((value) => value + 1)), []); + useEffect(() => { + const refresh = () => setRevision((value) => value + 1); + window.addEventListener("focus", refresh); + return () => window.removeEventListener("focus", refresh); + }, []); + useEffect(() => { + const controller = new AbortController(); + let live = true; + const timeout = setTimeout(() => controller.abort(), 8000); + setState({ data: { entries: [], total: 0 }, scope: "world", loading: true }); + if (dungeonId) void fetchDungeonLeaderboard(dungeonId, difficultyId, filter, offset, controller.signal) + .then((data) => { if (live) setState({ data, scope: "world", loading: false }); }) + .catch(() => { if (live) setState({ data: localDungeonLeaderboard(dungeonId, difficultyId, filter, offset), + scope: "device", loading: false }); }); + else setState({ data: { entries: [], total: 0 }, scope: "world", loading: false }); + return () => { live = false; controller.abort(); clearTimeout(timeout); }; + }, [dungeonId, difficultyId, filter, offset, revision]); + return { ...state, offset, setOffset, refresh: () => setRevision((value) => value + 1) }; +}