Update Healer Man server

This commit is contained in:
phenom committed 2026-10-04 13:13:35 -04:00
1 parent 39458d5de6
commit 6cc1b9217f
203 files changed
+133929 -31271

No files matched your search

@@ -0,0 +1,93 @@
#!/usr/bin/env node
import { createHash } from "node:crypto";
import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
const projectRoot = path.resolve(scriptDirectory, "../..");
const inputPath = path.join(projectRoot, "src", "game", "generated", "romHeirloomReference.json");
const outputPath = path.join(projectRoot, "src", "game", "generated", "romNativeDamageTrainingProfiles.generated.json");
const classes = ["rom-knight", "rom-priest", "rom-scout", "rom-rogue", "rom-mage"];
const slots = ["main-hand", "off-hand", "ranged"];
const sha256 = (buffer) => createHash("sha256").update(buffer).digest("hex");
const inputBuffer = await readFile(inputPath);
const catalog = JSON.parse(inputBuffer.toString("utf8"));
const selectedItemIds = new Set(Object.values(catalog.profiles).flatMap((profile) =>
Object.values(profile).flatMap((curve) => curve.flatMap((row) => row.referenceItemIds))));
const selectedItems = catalog.sourceItems.filter((item) => selectedItemIds.has(item.itemId));
const itemById = new Map(selectedItems.map((item) => [item.itemId, item]));
if (selectedItems.length !== selectedItemIds.size || selectedItems.some((item) =>
item.unmappedEffects?.length || item.unresolvedAttributeIds?.length)) {
throw new Error("Training reference has unresolved native body channels.");
}
const profiles = {};
for (const classId of classes) {
const sourceProfile = catalog.profiles?.[classId];
if (!sourceProfile) throw new Error(`Missing source training profile for ${classId}.`);
const classProfile = {};
for (const slot of slots) {
const curve = sourceProfile[slot];
if (!curve) continue;
if (curve.length !== 79) throw new Error(`${classId}/${slot} must cover levels 1–79.`);
let previousShieldDefense = 0;
classProfile[slot] = curve.map((entry, index) => {
const weaponDamage = entry.stats?.weaponDamage ?? 0;
const magicalWeaponDamage = entry.stats?.magicalWeaponDamage ?? 0;
const speedTenths = entry.speedTenths ?? null;
const referenceItems = entry.referenceItemIds.map((id) => itemById.get(id));
const weaponType = referenceItems.length && referenceItems.every((item) => item?.sourceCatalog === "weaponobject.db"
&& item.type === referenceItems[0].type) ? referenceItems[0].type : null;
const shields = referenceItems.length && referenceItems.every((item) => item?.sourceCatalog === "armorobject.db"
&& item.type === 5) ? [...referenceItems].sort((left, right) => left.level - right.level) : [];
if (shields.length) {
const level = index + 1;
const lower = shields[0];
const upper = shields.at(-1);
const blend = level < lower.level ? level / lower.level : level > upper.level ? level / upper.level
: lower.level === upper.level ? 0 : (level - lower.level) / (upper.level - lower.level);
const rawDefense = level < lower.level ? (upper.baseStats.physicalDefense ?? 0) * blend
: level > upper.level ? (lower.baseStats.physicalDefense ?? 0) * blend
: (lower.baseStats.physicalDefense ?? 0) * (1 - blend) + (upper.baseStats.physicalDefense ?? 0) * blend;
previousShieldDefense = Math.max(previousShieldDefense, rawDefense > 0 ? Math.max(1, Math.round(rawDefense)) : 0);
}
if (![weaponDamage, magicalWeaponDamage].every(Number.isFinite)
|| (speedTenths !== null && !Number.isFinite(speedTenths))) {
throw new Error(`Non-finite source training weapon row ${classId}/${slot}/${index + 1}.`);
}
return {
level: index + 1,
sourceItemIds: entry.referenceItemIds,
weaponDamage,
magicalWeaponDamage,
speedTenths,
weaponType,
...(shields.length ? { shieldDefense: previousShieldDefense } : {}),
};
});
}
profiles[classId] = classProfile;
}
const output = {
schemaVersion: 1,
status: "compact source-selected training weapon references; simulator gear profile and level interpolation policy",
source: {
file: "src/game/generated/romHeirloomReference.json",
sha256: sha256(inputBuffer),
sourceItemDatabases: catalog.sourceFiles,
quality: catalog.quality,
levelCap: 79,
selectedItemCount: selectedItems.length,
// Selection excludes unmapped effects, including typed MPow/Resist/AllRange.
// These are item contributions only; actor auras and racial/source base inputs remain separate.
trainingItemBodyDefaults: {
perSchoolMagicPowerRate: 1, perSchoolResistance: 0, perSchoolResistanceRate: 1,
allRangeDamageRate: 1, throwDamageRate: 1, throwingWeaponDamage: 0,
},
},
profiles,
};
await writeFile(outputPath, `${JSON.stringify(output, null, 2)}\n`);
console.log(`Wrote compact native training weapon profiles to ${path.relative(projectRoot, outputPath)}.`);