Update class mechanics, stat rulesets, and CoA reference catalogs

This commit is contained in:
phenom committed 2026-10-04 12:56:30 -04:00
1 parent b16821e92e
commit 39458d5de6
221 files changed
+2584962 -568

No files matched your search

+261 -1
View File
@@ -1,7 +1,8 @@
import { beforeEach, describe, expect, it } from "vitest";
import { applyAura, expireAuras, resolveDamageValue } from "./combatAuras";
import { ROM_ALL_ABILITIES } from "./romAbilityCatalog";
import { clearMobRuntimeRegistry, getMobPosition, setMobPosition } from "./mobRuntimeRegistry";
import { PLAYER_AURA_ENTITY_ID, useCombatStore } from "./combatStore";
import { PLAYER_AURA_ENTITY_ID, stableCombatUnit, useCombatStore } from "./combatStore";
import { romEffectsForCast, type RomCastMechanics } from "./romCombatMechanics";
import {
ROM_CHAMPION_SOUL_FORGE_PROC_ACTION_ID,
@@ -10,6 +11,10 @@ import {
ROM_CHAMPION_WAY_OF_FRENZY_PROFILE,
romChampionCastEffects,
} from "./romChampionOverrides";
import {
ROM_CHAMPION_CHAIN_DRIVE_WINDOW_AURA_ID,
ROM_CHAMPION_FORGE_RANK_TWO_AURA_ID,
} from "./romChampionForgeRuntime";
const empty: RomCastMechanics = {
sealStacks: 0,
@@ -139,6 +144,191 @@ describe("RoM Champion source-specific behavior", () => {
expect(useCombatStore.getState().resourcePools.rage).toBeGreaterThanOrEqual(10);
});
it("keeps rank 1 at level 21 and unlocks the rank 2 Forge source aura at level 22", () => {
useCombatStore.getState().initializeCharacter({
classId: "rom-champion", secondaryClassId: "rom-rogue", raceId: "rom-dwarf", level: 21,
});
const forge = sourceAbility(498523);
expect(useCombatStore.getState().castAbility(forge.id, undefined, 1_000)).toMatchObject({ ok: true });
expect(useCombatStore.getState().auras.some((aura) => aura.definition.id === "rom-champion-forge-active"))
.toBe(true);
expect(useCombatStore.getState().auras.some((aura) => aura.definition.id === ROM_CHAMPION_FORGE_RANK_TWO_AURA_ID))
.toBe(false);
useCombatStore.setState({ level: 22 });
expect(useCombatStore.getState().castAbility(forge.id, undefined, 4_000)).toMatchObject({ ok: true });
const rankTwo = useCombatStore.getState().auras.find((aura) => (
aura.definition.id === ROM_CHAMPION_FORGE_RANK_TWO_AURA_ID
));
expect(rankTwo?.expiresAt).toBe(304_000);
expect(useCombatStore.getState().auras.some((aura) => aura.definition.id === "rom-champion-forge-active"))
.toBe(false);
});
it("grants Chain Drive on an eligible rank 2 auto attack and resets only learned owner cooldowns", () => {
useCombatStore.getState().initializeCharacter({
classId: "rom-champion", secondaryClassId: "rom-rogue", raceId: "rom-dwarf", level: 22,
});
useCombatStore.getState().registerMob("champion-chain-drive-target", {
name: "Chain Drive Target", maxHealth: 100_000,
});
const forge = sourceAbility(498523);
const runePulse = sourceAbility(498529);
const shadowPulse = sourceAbility(498726);
expect(useCombatStore.getState().castAbility(forge.id, undefined, 1_000)).toMatchObject({ ok: true });
useCombatStore.setState((state) => ({
resource: 0,
resourcePools: { ...state.resourcePools, rage: 0 },
cooldowns: { ...state.cooldowns, [runePulse.id]: 90_000, [shadowPulse.id]: 90_000 },
}));
const hitSeed = Array.from({ length: 100 }, (_, index) => `champion-chain-drive-${index}`)
.find((seed) => stableCombatUnit(`${seed}:498590:0`) < 0.3);
expect(hitSeed).toBeDefined();
const hit = useCombatStore.getState().damageMob(
"champion-chain-drive-target",
100,
2_000,
undefined,
"physical",
{
attackKind: "melee",
seed: hitSeed!,
abilityId: "party-auto-attack",
rules: { isAutoAttack: true, canMiss: false, canDodge: false, canParry: false, canBlock: false, canCrit: false },
},
);
expect(hit.amount).toBeGreaterThan(0);
expect(useCombatStore.getState().auras.some((aura) => (
aura.definition.id === ROM_CHAMPION_CHAIN_DRIVE_WINDOW_AURA_ID
&& aura.targetId === PLAYER_AURA_ENTITY_ID
&& aura.expiresAt === 10_000
))).toBe(true);
expect(useCombatStore.getState().cooldowns[runePulse.id]).toBe(0);
expect(useCombatStore.getState().cooldowns[shadowPulse.id]).toBe(0);
expect(useCombatStore.getState().resourcePools.rage).toBeGreaterThanOrEqual(10);
expect(useCombatStore.getState().auras.find((aura) => aura.definition.id === "rom-champion-forge-stack")?.stacks)
.toBe(1);
});
it("keeps the rank 2 stack and Rage when Chain Drive's fallback roll fails", () => {
useCombatStore.getState().initializeCharacter({
classId: "rom-champion", secondaryClassId: "rom-rogue", raceId: "rom-dwarf", level: 22,
});
useCombatStore.getState().registerMob("champion-chain-drive-failed-target", {
name: "Chain Drive Failed Target", maxHealth: 100_000,
});
const forge = sourceAbility(498523);
expect(useCombatStore.getState().castAbility(forge.id, undefined, 1_000)).toMatchObject({ ok: true });
useCombatStore.setState((state) => ({ resourcePools: { ...state.resourcePools, rage: 0 }, resource: 0 }));
const failedSeed = Array.from({ length: 100 }, (_, index) => `chain-drive-failed-${index}`)
.find((seed) => stableCombatUnit(`${seed}:498590:0`) >= 0.8
&& stableCombatUnit(`${seed}:498590:1`) >= 0.3)!;
const hit = useCombatStore.getState().damageMob(
"champion-chain-drive-failed-target", 100, 2_000, undefined, "physical", {
attackKind: "melee",
seed: failedSeed,
abilityId: "party-auto-attack",
rules: { isAutoAttack: true, canMiss: false, canDodge: false, canParry: false, canBlock: false, canCrit: false },
},
);
expect(hit.amount).toBeGreaterThan(0);
expect(useCombatStore.getState().auras.find((aura) => aura.definition.id === "rom-champion-forge-stack")?.stacks)
.toBe(1);
expect(useCombatStore.getState().auras.some((aura) => (
aura.definition.id === ROM_CHAMPION_CHAIN_DRIVE_WINDOW_AURA_ID
))).toBe(false);
expect(useCombatStore.getState().resourcePools.rage).toBeGreaterThanOrEqual(10);
});
it("does not proc rank 2 Forge from zero or fully absorbed physical packets", () => {
useCombatStore.getState().initializeCharacter({
classId: "rom-champion", secondaryClassId: "rom-rogue", raceId: "rom-dwarf", level: 22,
});
useCombatStore.getState().registerMob("champion-forge-zero-target", {
name: "Zero Packet Target", maxHealth: 100_000,
});
useCombatStore.getState().registerMob("champion-forge-absorbed-target", {
name: "Absorbed Packet Target", maxHealth: 100_000,
});
const forge = sourceAbility(498523);
expect(useCombatStore.getState().castAbility(forge.id, undefined, 1_000)).toMatchObject({ ok: true });
const zeroHit = useCombatStore.getState().damageMob(
"champion-forge-zero-target", 0, 2_000, undefined, "physical", {
attackKind: "melee",
seed: "champion-forge-zero-hit",
abilityId: "party-auto-attack",
rules: { isAutoAttack: true, canMiss: false, canDodge: false, canParry: false, canBlock: false, canCrit: false },
},
);
expect(zeroHit.amount).toBe(0);
const shieldedAuras = applyAura(useCombatStore.getState().auras, {
definition: {
id: "test:champion-forge-physical-absorb",
name: "Physical absorb fixture",
disposition: "buff",
durationMs: null,
absorbs: [{ id: "test:champion-forge-physical-absorb-pool", amount: 1_000_000, school: "physical" }],
},
sourceId: "champion-forge-absorbed-target",
targetId: "champion-forge-absorbed-target",
now: 3_000,
}).auras;
useCombatStore.setState({ auras: shieldedAuras });
const absorbedHit = useCombatStore.getState().damageMob(
"champion-forge-absorbed-target", 100, 3_000, undefined, "physical", {
attackKind: "melee",
seed: "champion-forge-absorbed-hit",
abilityId: "party-auto-attack",
rules: { isAutoAttack: true, canMiss: false, canDodge: false, canParry: false, canBlock: false, canCrit: false },
},
);
expect(absorbedHit.amount).toBe(0);
const absorbedEvent = useCombatStore.getState().combatEvents.filter((event) => (
event.kind === "damage" && event.targetActorId === "champion-forge-absorbed-target"
)).at(-1);
expect(absorbedEvent?.absorbed).toBeGreaterThan(0);
expect(useCombatStore.getState().auras.some((aura) => aura.definition.id === "rom-champion-forge-stack"))
.toBe(false);
expect(useCombatStore.getState().auras.some((aura) => aura.definition.id === ROM_CHAMPION_CHAIN_DRIVE_WINDOW_AURA_ID))
.toBe(false);
expect(useCombatStore.getState().resourcePools.rage).toBe(0);
});
it("does not proc rank 2 Forge from a positive melee spell packet", () => {
useCombatStore.getState().initializeCharacter({ classId: "rom-champion", raceId: "rom-dwarf", level: 22 });
useCombatStore.getState().registerMob("champion-forge-melee-spell-target", {
name: "Melee Spell Target", maxHealth: 100_000,
});
expect(useCombatStore.getState().castAbility(sourceAbility(498523).id, undefined, 1_000))
.toMatchObject({ ok: true });
useCombatStore.setState((state) => ({ resource: 0, resourcePools: { ...state.resourcePools, rage: 0 } }));
useCombatStore.getState().damageMob(
"champion-forge-melee-spell-target",
100,
2_000,
undefined,
"physical",
{
attackKind: "melee",
seed: "champion-direct-melee-spell",
abilityId: sourceAbility(498529).id,
rules: { canMiss: false, canDodge: false, canParry: false, canBlock: false, canCrit: false },
},
);
expect(useCombatStore.getState().auras.some((aura) => aura.definition.id === "rom-champion-forge-stack"))
.toBe(false);
expect(useCombatStore.getState().auras.some((aura) => aura.definition.id === ROM_CHAMPION_CHAIN_DRIVE_WINDOW_AURA_ID))
.toBe(false);
expect(useCombatStore.getState().resourcePools.rage).toBe(0);
});
it("restores Rune Pulse's source-defined Rage only when its damage lands", () => {
const runePulse = sourceAbility(498529);
expect(runePulse.description).toContain("When it hits, it will restore");
@@ -149,6 +339,76 @@ describe("RoM Champion source-specific behavior", () => {
if (hit?.kind === "damage") expect(hit.onDamageResource).toEqual({ resource: "rage", amount: 5 });
expect(effects.some((effect) => effect.kind === "apply-aura" && effect.aura.id.includes("-622327-")))
.toBe(false);
expect(effects.some((effect) => effect.kind === "apply-aura" && effect.aura.id === "rom-aura-498604-621253-bonus"))
.toBe(false);
expect(castEffects(498726)
.some((effect) => effect.kind === "apply-aura" && effect.aura.id === "rom-aura-498604-621253-bonus"))
.toBe(false);
});
it("grants Rune Pulse's one-second bonus at cast start and expires it before the two-second impact", () => {
useCombatStore.getState().initializeCharacter({
classId: "rom-champion", secondaryClassId: "rom-rogue", raceId: "rom-dwarf", level: 22,
});
useCombatStore.getState().registerMob("champion-rune-pulse-target", {
name: "Rune Pulse Target", maxHealth: 100_000,
});
setMobPosition("champion-rune-pulse-target", [3, 0, 0]);
useCombatStore.getState().selectMob("champion-rune-pulse-target");
const forge = sourceAbility(498523);
expect(useCombatStore.getState().castAbility(forge.id, undefined, 1_000)).toMatchObject({ ok: true });
useCombatStore.setState((state) => ({ cooldowns: { ...state.cooldowns, [forge.id]: 0 } }));
const hitSeed = Array.from({ length: 100 }, (_, index) => `rune-pulse-window-${index}`)
.find((seed) => stableCombatUnit(`${seed}:498590:0`) < 0.3)!;
useCombatStore.getState().damageMob(
"champion-rune-pulse-target", 100, 3_000, undefined, "physical", {
attackKind: "melee",
seed: hitSeed,
abilityId: "party-auto-attack",
rules: { isAutoAttack: true, canMiss: false, canDodge: false, canParry: false, canBlock: false, canCrit: false },
},
);
expect(useCombatStore.getState().auras.some((aura) => (
aura.definition.id === ROM_CHAMPION_CHAIN_DRIVE_WINDOW_AURA_ID
&& aura.expiresAt === 11_000
))).toBe(true);
const runePulse = sourceAbility(498529);
expect(runePulse.castTimeMs).toBe(2_000);
expect(useCombatStore.getState().castAbility(runePulse.id, undefined, 3_100)).toMatchObject({ ok: true });
const startedAura = useCombatStore.getState().auras.find((aura) => (
aura.definition.id === "rom-aura-498604-621253-bonus"
));
const runePulseBonuses = useCombatStore.getState().auras.filter((aura) => (
aura.definition.id === "rom-aura-498604-621253-bonus"
));
expect(startedAura?.appliedAt).toBe(3_100);
expect(startedAura?.expiresAt).toBe(4_100);
expect(runePulseBonuses).toHaveLength(1);
const activeAurasDuringBonus = expireAuras(useCombatStore.getState().auras, 3_500).active;
const activeAurasWithoutBonus = activeAurasDuringBonus.filter((aura) => (
aura.definition.id !== "rom-aura-498604-621253-bonus"
));
const outgoingDamageWithBonus = resolveDamageValue(100, activeAurasDuringBonus, {
direction: "dealt", school: "physical", attackKind: "melee",
});
const outgoingDamageWithoutBonus = resolveDamageValue(100, activeAurasWithoutBonus, {
direction: "dealt", school: "physical", attackKind: "melee",
});
expect(outgoingDamageWithBonus - outgoingDamageWithoutBonus).toBeCloseTo(20, 10);
useCombatStore.getState().tick(2, 5_100);
const aurasAtImpact = expireAuras(useCombatStore.getState().auras, 5_100).active;
const runePulseBonusesAtImpact = aurasAtImpact.filter((aura) => (
aura.definition.id === "rom-aura-498604-621253-bonus"
));
const outgoingDamageAtImpact = resolveDamageValue(100, aurasAtImpact, {
direction: "dealt", school: "physical", attackKind: "melee",
});
expect(outgoingDamageAtImpact).toBe(resolveDamageValue(100, aurasAtImpact.filter((aura) => (
aura.definition.id !== "rom-aura-498604-621253-bonus"
)), { direction: "dealt", school: "physical", attackKind: "melee" }));
expect(runePulseBonusesAtImpact).toHaveLength(0);
});
it("routes Rune Siphon's positive source component to the caster as healing", () => {