Release Healer Man 0.1.14

This commit is contained in:
phenom committed 2026-09-05 11:40:50 -04:00
1 parent 67d7a8ebe7
commit 7b41b102fa
147 files changed
+169892 -1342

No files matched your search

@@ -42,6 +42,7 @@ const spellCatalogFile = path.join(
"snapshots",
"runewaker-npc-spell-catalog.json",
);
const bossMechanicsFile = path.join(projectRoot, "dungeon-pipeline", "boss-mechanics.json");
const paperdollCatalogFile = path.join(
pipelineDirectory,
"snapshots",
@@ -430,11 +431,10 @@ function yawFromDirection(direction) {
return Number(((((direction % turn) + turn) % turn) * Math.PI * 2 / turn).toFixed(9));
}
function attacksFor(template, sourceSpells, spellCatalog) {
if (Array.isArray(template.attacks) && template.attacks.length) return template.attacks;
function attacksFor(template, sourceSpells, spellCatalog, bossMechanics) {
const ooze = template.archetype === "ooze";
const basicAttack = {
id: runtimePrefix + "-basic-attack",
id: "basic-attack",
name: ooze ? "Corrosive Slam" : "Basic Attack",
delivery: "melee",
target: "primary",
@@ -462,13 +462,24 @@ function attacksFor(template, sourceSpells, spellCatalog) {
damageMultiplier: spell.runtime.damageMultiplier,
}];
});
return [basicAttack, ...sourceAttacks];
const recipeAttacks = Array.isArray(template.attacks)
? template.attacks.filter((attack) => attack.id !== "basic-attack")
: [];
const authored = template.classification === "boss"
? bossMechanics?.bosses?.[runtimePrefix + "-" + template.id]
: null;
const mechanics = authored?.mode === "replace"
? authored.mechanics ?? []
: [...(authored?.mechanics ?? []), ...recipeAttacks, ...sourceAttacks];
const unique = [...new Map(mechanics.map((attack) => [attack.id, attack])).values()];
return [basicAttack, ...unique];
}
function entityFor(template, actor, sourceSpells, spellCatalog) {
function entityFor(template, actor, sourceSpells, spellCatalog, bossMechanics) {
const boss = template.classification === "boss";
const defaults = recipe.combatDefaults ?? {};
const combat = template.combat ?? {};
const attacks = attacksFor(template, sourceSpells, spellCatalog, bossMechanics);
return {
id: runtimePrefix + "-" + template.id,
name: template.name,
@@ -481,7 +492,8 @@ function entityFor(template, actor, sourceSpells, spellCatalog) {
damageMultiplier: combat.damageMultiplier ?? (boss ? defaults.bossDamageMultiplier ?? 1.3 : defaults.damageMultiplier ?? 1),
moveSpeed: combat.moveSpeed ?? (boss ? defaults.bossMoveSpeed ?? 2.25 : defaults.moveSpeed ?? 2.15),
leashRange: combat.leashRange ?? (boss ? defaults.bossLeashRange ?? 32 : defaults.leashRange ?? 24),
attacks: attacksFor(template, sourceSpells, spellCatalog),
...(boss ? { mechanicIds: attacks.slice(1).map((attack) => attack.id) } : {}),
attacks,
},
visual: {
primaryColor: template.primaryColor,
@@ -537,12 +549,14 @@ async function generatePopulation() {
requireFile(manifestFile, "Run build-population.mjs --actors-only first."),
requireFile(resolveProject(recipe.files.environmentMetadata), "Build the environment first."),
requireFile(spellCatalogFile, "Run npm run runewaker:spells:refresh once to create the portable spell catalog."),
requireFile(bossMechanicsFile, "Run npm run runewaker:bosses:refresh to create the boss mechanic catalog."),
]);
const [snapshot, manifest, environment, spellCatalogSnapshot] = await Promise.all([
const [snapshot, manifest, environment, spellCatalogSnapshot, bossMechanics] = await Promise.all([
readFile(snapshotFile, "utf8").then(JSON.parse),
readFile(manifestFile, "utf8").then(JSON.parse),
readFile(resolveProject(recipe.files.environmentMetadata), "utf8").then(JSON.parse),
readFile(spellCatalogFile, "utf8").then(JSON.parse),
readFile(bossMechanicsFile, "utf8").then(JSON.parse),
]);
if (spellCatalogSnapshot.status !== "portable"
|| spellCatalogSnapshot.safety?.runtimeSqlDependency !== false
@@ -619,6 +633,7 @@ async function generatePopulation() {
actorForTemplate(template),
[...(sourceSpellsByTemplate.get(template.id)?.values() ?? [])],
spellCatalog,
bossMechanics,
);
}
const spawn = {