Release Healer Man 0.1.21
This commit is contained in:
1 parent
699fc858c9
commit
f6f2375145
210 files changed
+976039
-6247
No files matched your search
@@ -7,6 +7,7 @@ import { BufferAttribute, BufferGeometry, Matrix4, Vector3 } from "three";
|
||||
import {
|
||||
buildPartyNavigationGraph,
|
||||
} from "../../src/game/partyNavigation.ts";
|
||||
import { generateEncounterImplementationReport } from "./lib/encounter-implementation-report.mjs";
|
||||
import { auditOrderedObjectiveRoutes } from "./lib/objective-route-audit.mjs";
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||
@@ -156,6 +157,19 @@ function validateAttack(dungeonId, entityId, attack, issues) {
|
||||
}
|
||||
}
|
||||
|
||||
function formulaMatchesCatalog(attackFormula, catalogFormula) {
|
||||
if (!catalogFormula) return attackFormula === undefined;
|
||||
if (!attackFormula || typeof attackFormula !== "object") return false;
|
||||
const expected = {
|
||||
...catalogFormula,
|
||||
// NPC assignments carry their source spell level. Script-derived boss
|
||||
// references use the documented zero sentinel because no assignment level
|
||||
// exists in the packaged row.
|
||||
spellLevel: attackFormula.spellLevel,
|
||||
};
|
||||
return JSON.stringify(attackFormula) === JSON.stringify(expected);
|
||||
}
|
||||
|
||||
function renderMarkdown(report) {
|
||||
const rows = report.dungeons.map((dungeon) => [
|
||||
"|", dungeon.id, "|", dungeon.sourceRows, "|", dungeon.runtimeSpawns, "|",
|
||||
@@ -171,7 +185,7 @@ function renderMarkdown(report) {
|
||||
return [
|
||||
"# RuneWaker gameplay content audit",
|
||||
"",
|
||||
"Structural validation: **" + report.validationStatus + "** ",
|
||||
"Structural validation: **" + report.validationStatus + "**",
|
||||
"Original-mechanics fidelity: **" + report.fidelityStatus + "**",
|
||||
"",
|
||||
"This offline audit reads packaged recipes, snapshots, generated TypeScript, reports,",
|
||||
@@ -200,6 +214,10 @@ function renderMarkdown(report) {
|
||||
"- Boss signaling includes " + summary.telegraphedBossMechanics
|
||||
+ " spatial telegraphs and " + summary.healthTriggeredBossMechanics
|
||||
+ " visible health-triggered mechanics.",
|
||||
"- The exhaustive encounter implementation ledger tracks "
|
||||
+ report.encounterImplementationLedger.deferredSourceRows + " deferred source rows and "
|
||||
+ report.encounterImplementationLedger.uniqueMechanicsEvidence
|
||||
+ " unique mechanics evidence records without inflating overlapping regex labels.",
|
||||
"",
|
||||
"## Authentic source data currently used",
|
||||
"",
|
||||
@@ -215,10 +233,12 @@ function renderMarkdown(report) {
|
||||
"- " + summary.builderAuthoredAttacks + " attacks are builder exceptions outside the modular boss catalog.",
|
||||
"- " + summary.sourceBackedDamageFamilyAttacks
|
||||
+ " executable attacks carry a conservative source-backed direct-damage family mapping.",
|
||||
"- Source-backed mapping preserves spell identity, damage family, school, range, and cooldown;",
|
||||
" HealerMan's 0.85 damage coefficient is intentionally generic, not claimed as source-exact.",
|
||||
"- Scaling, movement/leash, clustering, aggro, death state, loot, and objective ordering are generic.",
|
||||
"- Unmapped complex doors, multi-actor phases, patrols, resets, interactions, and source loot remain review-required.",
|
||||
"- Source-backed mapping preserves spell identity, damage family, school, range, cooldown,",
|
||||
" source power fields, and the exact native formula shape/provenance.",
|
||||
"- Damage execution uses a labeled HealerMan baseline adapter because native ATK/MATK,",
|
||||
" DEF/MDEF, weapon power, resist, critical, level-delta, and global rate inputs are unavailable.",
|
||||
"- Scaling, movement/leash, clustering, aggro, death state, loot item stats/slots, and objective ordering are generic.",
|
||||
"- Unmapped complex doors, multi-actor phases, patrols, resets, and interactions remain review-required.",
|
||||
"- Procedural animation states are HealerMan-authored; native GLBs must provide idle, move,",
|
||||
" attack, cast, wound, and death families.",
|
||||
"",
|
||||
@@ -530,11 +550,16 @@ for (const recipeName of recipeNames) {
|
||||
+ executableSpellId + ".");
|
||||
} else {
|
||||
counts.sourceBackedDamageFamilyAttacks += 1;
|
||||
if (attack.spellId && [
|
||||
const runtimeMappingDrift = !catalogAuthored && [
|
||||
"name", "school", "animation", "cooldownMs", "damageMultiplier",
|
||||
].some((field) => attack[field] !== (
|
||||
field === "name" ? catalogSpell.name : catalogSpell.runtime[field]
|
||||
))) {
|
||||
));
|
||||
const formulaDrift = !formulaMatchesCatalog(
|
||||
attack.runewakerFormula,
|
||||
catalogSpell.runtime.formula,
|
||||
);
|
||||
if (attack.spellId && (runtimeMappingDrift || formulaDrift)) {
|
||||
issue(issues, dungeonId, entityId + " spell " + executableSpellId
|
||||
+ " drifted from the portable source-backed runtime mapping.");
|
||||
}
|
||||
@@ -749,8 +774,8 @@ summary.portableDamageFamilyRecords = spellCatalogSnapshot?.damageFamilyCount ??
|
||||
summary.portableEvidenceOnlyRecords = spellCatalogSnapshot?.evidenceOnlyCount ?? 0;
|
||||
reviewNotes.push(summary.sourceGapBossMechanics
|
||||
+ " boss mechanics are conservative, telegraphed fallbacks for preserved bosses whose scripts expose no executable combat spell.");
|
||||
reviewNotes.push("Source-backed spell coefficients are HealerMan-generic; the original RuneWaker "
|
||||
+ "formula/stat scaling has not been reconstructed.");
|
||||
reviewNotes.push("Source formula structure is preserved, but live damage uses a labeled HealerMan "
|
||||
+ "baseline adapter because the required native combat-stat inputs are not packaged.");
|
||||
const navigationBlockerEntries = dungeons.flatMap((dungeon) => (
|
||||
dungeon.objectiveRouteEvidence
|
||||
.filter((route) => route.orderAuthoritative && !route.reachable)
|
||||
@@ -784,6 +809,12 @@ if (summary.derivedObjectiveRoutes) {
|
||||
+ summary.unreachableDerivedObjectiveRoutes + " unreachable heuristic legs remain navigation/order review evidence.");
|
||||
}
|
||||
|
||||
const encounterImplementation = await generateEncounterImplementationReport();
|
||||
if (encounterImplementation.report.issues.length) {
|
||||
issues.push("Encounter implementation ledger is blocked: "
|
||||
+ encounterImplementation.report.issues.join("; "));
|
||||
}
|
||||
|
||||
const report = {
|
||||
schemaVersion: 1,
|
||||
validationStatus: issues.length ? "blocked" : "green",
|
||||
@@ -797,11 +828,11 @@ const report = {
|
||||
],
|
||||
genericRuntimeSystems: [
|
||||
"source-gap attacks", "health/damage scaling", "movement/leash", "clustering/aggro",
|
||||
"combat/death state", "loot policy", "objective ordering", "procedural animation",
|
||||
"combat/death state", "loot item stats/slots", "objective ordering", "procedural animation",
|
||||
],
|
||||
deferredMechanics: [
|
||||
"complex doors/gates", "unmapped multi-actor waves", "unmapped phases", "patrols", "interactions",
|
||||
"encounter resets", "source loot",
|
||||
"encounter resets", "blocked source-loot predicates", "source item stat/slot mapping",
|
||||
],
|
||||
blockerGroups: {
|
||||
navigation: {
|
||||
@@ -829,6 +860,14 @@ const report = {
|
||||
mappingPolicy: "Only positively classified direct-HP damage families execute; "
|
||||
+ "named source auras, controls, summons, and health gates use conservative modular effects; unknown semantics remain evidence-only.",
|
||||
},
|
||||
encounterImplementationLedger: {
|
||||
json: encounterImplementation.reportJson,
|
||||
markdown: encounterImplementation.reportMarkdown,
|
||||
validationStatus: encounterImplementation.report.validationStatus,
|
||||
completionStatus: encounterImplementation.report.completionStatus,
|
||||
deferredSourceRows: encounterImplementation.report.summary.deferredSourceRows,
|
||||
uniqueMechanicsEvidence: encounterImplementation.report.summary.uniqueMechanicsEvidence,
|
||||
},
|
||||
resolvedRuntimeObservations,
|
||||
documentedExceptions,
|
||||
issues,
|
||||
|
||||
Reference in new issue
Block a user