Files
healer-man/src/game/wowDungeonEnvironment.ts
T
2026-09-13 11:07:56 -04:00

86 lines
5.0 KiB
TypeScript

import type { DungeonDefinition, DungeonPresentation, DungeonTraversalRamp } from "./dungeonTypes";
import stairCatalog from "./generated/wowDungeonStairs.generated.json";
type LightingProfile = Pick<DungeonPresentation, "background" | "ambientLight" | "hemisphereLight" | "directionalLight"> & {
readonly fog: Pick<DungeonPresentation["fog"], "color" | "near">;
};
// 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<Record<string, LightingProfile>> = {
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<Record<string, readonly string[]>> = {
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<Record<string, {
readonly collision: readonly { readonly url: string; readonly checksum: string }[];
readonly navigation?: { readonly url: string; readonly checksum: string };
readonly ramps: readonly DungeonTraversalRamp[];
}>>)[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 } : {}),
};
}