Update Healer Man server

This commit is contained in:
phenom committed 2026-09-20 19:27:37 -04:00
1 parent 97ee0c13f9
commit f864b5aea7
20 files changed
+468 -211

No files matched your search

+49 -19
View File
@@ -78,6 +78,7 @@ function packLabel(packId) {
const [category, id = category] = packId.split(":", 2);
const name = id.split("-").map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`).join(" ");
if (category === "dungeon") return name;
if (category === "creatures") return `${name} Creatures`;
if (packId === "characters:wow335a-v1") return "Extra Wrath Character Models & Skins";
if (packId === "characters:ascension-v1") return "Ascension Character Models & Skins";
if (packId === "characters:runewaker-v1") return "RuneWaker Character Models & Skins";
@@ -107,12 +108,13 @@ export function contentPackForPath(logicalPath) {
return basicWrathHuman ? "core:character-basic" : `characters:${segments[2]}`;
}
if (segments[1] === "creatures" && segments[2]) {
return segments[2] === "shared" ? "shared:creatures" : `dungeon:${segments[2]}`;
return segments[2] === "shared" ? "shared:creatures" : `creatures:${segments[2]}`;
}
if (segments[1] === "game" && segments[2] === "creatures" && segments[3]) {
return segments[3] === "shared" ? "shared:creatures" : `dungeon:${segments[3]}`;
return segments[3] === "shared" ? "shared:creatures" : `creatures:${segments[3]}`;
}
if (segments[1] === "game" && segments[2] === "dungeons" && segments[3]) {
if (segments[4] === "creatures") return `creatures:${segments[3]}`;
return `dungeon:${segments[3]}`;
}
if (segments[1] === "game" && segments[2] === "arenas") return "core:arenas";
@@ -203,44 +205,58 @@ function assetPackId(url) {
/**
* Builds the logical install topology for the 54-dungeon WoW campaign.
* Environment files intentionally remain in their independently downloadable
* Manastorm packs; a dungeon pack owns its creatures and pulls in exactly the
* environment/shared-creature packs referenced by the generated definition.
* Environment and creature files remain independently downloadable. A dungeon
* pack pulls in exactly the environment and creature packs referenced by its
* generated definition, while randomized modes can install creatures alone.
*/
export function campaignDungeonPackDefinitions(definitions) {
const packs = new Map();
for (const definition of definitions) {
const dungeonPackId = `dungeon:${definition.id}`;
if (definition.id === "wailing-caverns") {
const referencedCreaturePacks = new Set([
"creatures:wailing-caverns",
...Object.values(definition.entities ?? {})
.map((entity) => entity.visual?.model?.url)
.filter(Boolean)
.map(assetPackId),
]);
const unexpectedCreaturePacks = [...referencedCreaturePacks]
.filter((id) => id !== "creatures:wailing-caverns" && id !== "shared:creatures");
if (unexpectedCreaturePacks.length) {
throw new Error(`${definition.id} references unexpected creature packs: ${unexpectedCreaturePacks.join(", ")}`);
}
packs.set(dungeonPackId, {
id: dungeonPackId,
label: definition.title ?? packLabel(dungeonPackId),
bundledOnAndroid: true,
dependencies: [],
dependencies: [...referencedCreaturePacks],
});
continue;
}
const environmentUrls = [
...(definition.assets?.visual ?? []).map((asset) => asset.url),
...(definition.assets?.collision ?? []).map((asset) => asset.url),
definition.assets?.navigation?.url,
].filter(Boolean);
const resolvedEnvironmentPacks = environmentUrls.map(assetPackId);
if (!resolvedEnvironmentPacks.length || resolvedEnvironmentPacks.some((id) => !id?.startsWith("manastorm:"))) {
throw new Error(`${definition.id} has an environment asset outside a Manastorm content pack.`);
if (!resolvedEnvironmentPacks.length
|| resolvedEnvironmentPacks.some((id) => !id?.startsWith("manastorm:"))) {
throw new Error(`${definition.id} has an environment asset outside its expected content pack.`);
}
const environmentPackIds = new Set(resolvedEnvironmentPacks);
if (environmentPackIds.size !== 1) {
throw new Error(`${definition.id} must resolve to exactly one Manastorm environment pack.`);
}
const [environmentPackId] = environmentPackIds;
packs.set(environmentPackId, {
id: environmentPackId,
label: packLabel(environmentPackId),
bundledOnAndroid: false,
dependencies: [],
});
if (environmentPackId !== dungeonPackId) {
packs.set(environmentPackId, {
id: environmentPackId,
label: packLabel(environmentPackId),
bundledOnAndroid: false,
dependencies: [],
});
}
const creatureUrls = Object.values(definition.entities ?? {})
.map((entity) => entity.visual?.model?.url)
@@ -249,16 +265,21 @@ export function campaignDungeonPackDefinitions(definitions) {
if (resolvedCreaturePacks.some((id) => !id)) {
throw new Error(`${definition.id} has a creature asset outside a content pack.`);
}
const referencedCreaturePacks = new Set(resolvedCreaturePacks.filter((id) => id !== dungeonPackId));
const unexpectedCreaturePacks = [...referencedCreaturePacks].filter((id) => id !== "shared:creatures");
const referencedCreaturePacks = new Set(resolvedCreaturePacks);
const expectedCreaturePackId = `creatures:${definition.id}`;
const unexpectedCreaturePacks = [...referencedCreaturePacks]
.filter((id) => id !== expectedCreaturePackId && id !== "shared:creatures");
if (unexpectedCreaturePacks.length) {
throw new Error(`${definition.id} references unexpected creature packs: ${unexpectedCreaturePacks.join(", ")}`);
}
packs.set(dungeonPackId, {
id: dungeonPackId,
label: definition.title ?? packLabel(dungeonPackId),
bundledOnAndroid: false,
dependencies: [environmentPackId, ...referencedCreaturePacks],
bundledOnAndroid: definition.id === "wailing-caverns",
dependencies: [
...(environmentPackId === dungeonPackId ? [] : [environmentPackId]),
...referencedCreaturePacks,
],
});
}
return [...packs.values()].sort((left, right) => left.id.localeCompare(right.id));
@@ -350,6 +371,15 @@ export function createContentManifest(files, options = {}) {
});
}
// RuneWaker dungeon definitions are generated TypeScript rather than part of
// the WoW JSON catalog. Their matching creature dependency is recoverable
// from the stable pack namespace without importing runtime code here.
for (const creaturePack of [...packs.values()].filter((pack) => pack.id.startsWith("creatures:"))) {
const dungeonPack = packs.get(`dungeon:${creaturePack.id.slice("creatures:".length)}`);
if (!dungeonPack || dungeonPack.dependencies.includes(creaturePack.id)) continue;
dungeonPack.dependencies = [...dungeonPack.dependencies, creaturePack.id];
}
const sortedPacks = [...packs.values()].sort((left, right) => left.id.localeCompare(right.id));
for (const pack of sortedPacks) {
const objects = new Map(files.filter((file) => file.packId === pack.id)