Release Healer Man 0.1.45
This commit is contained in:
1 parent
756ee4f64a
commit
f82daf1c13
242 files changed
+1440
-101
No files matched your search
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env node
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||
const catalog = JSON.parse(fs.readFileSync(
|
||||
path.join(projectRoot, "src/game/generated/dungeonCampaignCatalog.json"),
|
||||
"utf8",
|
||||
));
|
||||
const manifest = JSON.parse(fs.readFileSync(
|
||||
path.join(projectRoot, "public/assets/game/combat-effects/manifest.json"),
|
||||
"utf8",
|
||||
));
|
||||
|
||||
const highConfidenceEffectName = /\b(?:dominat\w*|polymorph|hex|silence|slow|hamstring|frost nova|chill nova|summon|raise dead|ward|frost armor|drain life|death coil|vampiric touch|leech poison|pierce armor|sunder armor|dark shell|shock barrier|damage immunity)\b/i;
|
||||
const auxiliarySpellIds = new Set([21_708, 47_669, 54_888]);
|
||||
const manifestBySpellId = new Map();
|
||||
for (const entry of manifest.entries ?? []) manifestBySpellId.set(Number(entry.spellId), entry);
|
||||
|
||||
const bossCasts = [];
|
||||
for (const dungeon of catalog.definitions ?? []) {
|
||||
for (const entity of Object.values(dungeon.entities ?? {})) {
|
||||
if (entity.kind !== "boss") continue;
|
||||
for (const attack of entity.combat?.attacks ?? []) {
|
||||
if (!attack.spellId) continue;
|
||||
bossCasts.push({ dungeonId: dungeon.id, boss: entity.name, ...attack });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const effectCasts = bossCasts.filter((attack) => attack.effect);
|
||||
const suspicious = bossCasts.filter((attack) => (
|
||||
!attack.effect && highConfidenceEffectName.test(attack.name)
|
||||
));
|
||||
const auxiliaryLeaks = bossCasts.filter((attack) => auxiliarySpellIds.has(attack.spellId));
|
||||
const invalidComposites = effectCasts.filter((attack) => (
|
||||
attack.effect.kind === "composite" && !attack.effect.effects?.length
|
||||
));
|
||||
const atomicEffects = (attack) => attack.effect?.kind === "composite"
|
||||
? attack.effect.effects
|
||||
: attack.effect ? [attack.effect] : [];
|
||||
const harmful = (attack) => atomicEffects(attack).some((effect) => (
|
||||
["damage", "leech", "target-aura", "control", "pull"].includes(effect.kind)
|
||||
));
|
||||
const inferredGroundTelegraph = (attack) => harmful(attack) && (
|
||||
attack.delivery === "area"
|
||||
|| attack.radius !== undefined
|
||||
|| /\b(?:breath|cleave|frontal|sweep|cone|beam|charge|line|lance)\b/i.test(attack.name)
|
||||
);
|
||||
const presentation = effectCasts.map((attack) => {
|
||||
const entry = manifestBySpellId.get(attack.spellId);
|
||||
const originalVisual = Boolean(entry && Object.values(entry.phases ?? {}).some(
|
||||
(assets) => Array.isArray(assets) && assets.length > 0,
|
||||
));
|
||||
const originalAudio = Boolean(entry?.sounds?.some((sound) => sound.url));
|
||||
return { ...attack, originalVisual, originalAudio };
|
||||
});
|
||||
|
||||
const issues = [...suspicious, ...auxiliaryLeaks, ...invalidComposites].map((attack) => (
|
||||
`${attack.dungeonId}:${attack.boss}:${attack.spellId}:${attack.name}`
|
||||
));
|
||||
const report = {
|
||||
status: issues.length ? "failed" : "complete",
|
||||
bosses: new Set(bossCasts.map((attack) => `${attack.dungeonId}:${attack.boss}`)).size,
|
||||
spellCasts: bossCasts.length,
|
||||
explicitEffectCasts: effectCasts.length,
|
||||
presentation: {
|
||||
originalVisual: presentation.filter((attack) => attack.originalVisual).length,
|
||||
generatedVisualFallback: presentation.filter((attack) => !attack.originalVisual).length,
|
||||
originalAudio: presentation.filter((attack) => attack.originalAudio).length,
|
||||
generatedAudioFallback: presentation.filter((attack) => !attack.originalAudio).length,
|
||||
},
|
||||
indicators: {
|
||||
castBars: effectCasts.filter((attack) => (
|
||||
(attack.castTimeMs ?? 0) > 0
|
||||
|| attack.animation === "cast"
|
||||
|| inferredGroundTelegraph(attack)
|
||||
)).length,
|
||||
groundTelegraphs: effectCasts.filter((attack) => (
|
||||
attack.telegraph || inferredGroundTelegraph(attack)
|
||||
)).length,
|
||||
releaseOrImpactCues: effectCasts.length,
|
||||
auraOrControlHud: effectCasts.filter((attack) => {
|
||||
return atomicEffects(attack).some((effect) => (
|
||||
["aura", "target-aura", "control"].includes(effect.kind)
|
||||
));
|
||||
}).length,
|
||||
},
|
||||
issues,
|
||||
};
|
||||
|
||||
console.log(JSON.stringify(report, null, 2));
|
||||
if (issues.length) process.exitCode = 1;
|
||||
Reference in new issue
Block a user