forked from phenom/healer-man
Update Healer Man server
This commit is contained in:
1 parent
35dfdaed61
commit
4fb7a0b9c8
161 files changed
+24618
-529
No files matched your search
@@ -8,9 +8,15 @@ import { DEFAULT_ENEMY_ATTACK } from "./enemyAttacks";
|
||||
import { usePartyStore } from "./partyStore";
|
||||
import { romEffectsForCast, type RomCastMechanics } from "./romCombatMechanics";
|
||||
import {
|
||||
ROM_WARDEN_BRIAR_SHIELD_TAG,
|
||||
ROM_WARDEN_DAMAGE_TRANSFER_FRACTION,
|
||||
ROM_WARDEN_DAMAGE_TRANSFER_TAG,
|
||||
ROM_WARDEN_OAK_BLESSING_TAG,
|
||||
ROM_WARDEN_TOUGH_OAK_TAG,
|
||||
ROM_WARDEN_WILL_ATTACK_TAG,
|
||||
romWardenBriarShieldReflectDamage,
|
||||
romWardenBriarShieldReflectFraction,
|
||||
romWardenBriarShieldShouldReflect,
|
||||
} from "./romWardenOverrides";
|
||||
|
||||
const emptyContext: RomCastMechanics = {
|
||||
@@ -30,7 +36,7 @@ function effectsFor(spellId: number, context: Partial<RomCastMechanics> = {}) {
|
||||
|
||||
function initializeRomCharacter(
|
||||
classId: "rom-warden" | "rom-warrior" | "rom-druid",
|
||||
secondaryClassId?: "rom-warden" | "rom-warrior",
|
||||
secondaryClassId?: "rom-warden" | "rom-warrior" | "rom-mage",
|
||||
) {
|
||||
useCombatStore.getState().initializeCharacter({
|
||||
classId,
|
||||
@@ -53,6 +59,140 @@ describe("RoM Warden and Warden-linked source behaviors", () => {
|
||||
useCombatStore.setState({ playerPosition: [0, 0, 0] });
|
||||
});
|
||||
|
||||
it("casts Briar Shield as its source-backed defense aura without an immediate damage hit", () => {
|
||||
initializeRomCharacter("rom-warden");
|
||||
const ability = ROM_ALL_ABILITIES.find((entry) => entry.dbcSpellId === 493642)!;
|
||||
expect(ability.effects).toHaveLength(1);
|
||||
expect(ability.effects[0]).toMatchObject({
|
||||
kind: "apply-aura",
|
||||
recipient: "caster",
|
||||
aura: {
|
||||
durationMs: 900_000,
|
||||
modifiers: [{ kind: "stat", stat: "armor", operation: "percent", value: 0.05 }],
|
||||
utilityTags: expect.arrayContaining([ROM_WARDEN_BRIAR_SHIELD_TAG]),
|
||||
},
|
||||
});
|
||||
expect(ability.effects.some((effect) => effect.kind === "damage")).toBe(false);
|
||||
|
||||
const mobId = "briar-shield-cast-target";
|
||||
useCombatStore.getState().registerMob(mobId, { name: "Briar Shield target", maxHealth: 100_000 });
|
||||
setMobPosition(mobId, [2, 0, 0]);
|
||||
useCombatStore.getState().selectMob(mobId);
|
||||
const now = 10_000;
|
||||
expect(useCombatStore.getState().castAbility(ability.id, undefined, now)).toMatchObject({ ok: true });
|
||||
|
||||
const state = useCombatStore.getState();
|
||||
expect(state.mobs[mobId]?.health).toBe(100_000);
|
||||
expect(state.auras.find((aura) => aura.targetId === PLAYER_AURA_ENTITY_ID
|
||||
&& aura.definition.utilityTags?.includes(ROM_WARDEN_BRIAR_SHIELD_TAG))).toMatchObject({
|
||||
appliedAt: now,
|
||||
expiresAt: now + 900_000,
|
||||
definition: {
|
||||
modifiers: [{ kind: "stat", stat: "armor", operation: "percent", value: 0.05 }],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("maps Briar Shield's reflected component to source-level main-hand DPS", () => {
|
||||
const weapon = { damageMin: 100, damageMax: 120, damageSchool: "physical" as const, speedMs: 2_000 };
|
||||
expect(romWardenBriarShieldReflectFraction(80)).toBeCloseTo(0.074);
|
||||
expect(romWardenBriarShieldReflectFraction(105)).toBeCloseTo(0.094);
|
||||
expect(romWardenBriarShieldReflectDamage(80, weapon)).toBe(4);
|
||||
expect(romWardenBriarShieldReflectDamage(105, weapon)).toBe(5);
|
||||
expect(romWardenBriarShieldReflectDamage(80, null)).toBe(0);
|
||||
|
||||
const validPhysicalHit = {
|
||||
auraActive: true,
|
||||
landed: true,
|
||||
school: "physical",
|
||||
attackKind: "melee" as const,
|
||||
withinRange: true,
|
||||
};
|
||||
expect(romWardenBriarShieldShouldReflect(validPhysicalHit)).toBe(true);
|
||||
expect(romWardenBriarShieldShouldReflect({ ...validPhysicalHit, attackKind: "ranged" })).toBe(true);
|
||||
expect(romWardenBriarShieldShouldReflect({ ...validPhysicalHit, school: "fire" })).toBe(false);
|
||||
expect(romWardenBriarShieldShouldReflect({ ...validPhysicalHit, attackKind: "spell" })).toBe(false);
|
||||
expect(romWardenBriarShieldShouldReflect({ ...validPhysicalHit, landed: false })).toBe(false);
|
||||
expect(romWardenBriarShieldShouldReflect({ ...validPhysicalHit, auraActive: false })).toBe(false);
|
||||
expect(romWardenBriarShieldShouldReflect({ ...validPhysicalHit, withinRange: false })).toBe(false);
|
||||
});
|
||||
|
||||
it("reflects landed physical projectiles, but not magic or attacks beyond 50 range", () => {
|
||||
const reflectedFrom = (school: "physical" | "fire", attackerDistance: number) => {
|
||||
clearMobRuntimeRegistry();
|
||||
initializeRomCharacter("rom-warden");
|
||||
const weapon = { damageMin: 100, damageMax: 120, damageSchool: "physical" as const, speedMs: 2_000 };
|
||||
useCombatStore.setState((state) => ({
|
||||
gearStats: { ...state.gearStats, meleeWeapon: weapon },
|
||||
}));
|
||||
const mobId = `briar-reflect-${school}-${attackerDistance}`;
|
||||
useCombatStore.getState().registerMob(mobId, {
|
||||
name: "Briar Shield attacker",
|
||||
maxHealth: 100_000,
|
||||
level: 100,
|
||||
attacks: [{
|
||||
...DEFAULT_ENEMY_ATTACK,
|
||||
id: `briar-reflect-${school}`,
|
||||
delivery: "projectile",
|
||||
school,
|
||||
range: 100,
|
||||
initialCooldownMs: [0, 0],
|
||||
}],
|
||||
});
|
||||
setMobPosition(mobId, [attackerDistance, 0, 0]);
|
||||
useCombatStore.getState().selectMob(mobId);
|
||||
expect(useCombatStore.getState().castAbility("rom-warden-general-1", undefined, 1_000))
|
||||
.toMatchObject({ ok: true });
|
||||
expect(useCombatStore.getState().castAbility("rom-warden-primary-11", undefined, 3_000))
|
||||
.toMatchObject({ ok: true });
|
||||
useCombatStore.getState().damageMob(mobId, 1, 3_000);
|
||||
const healthBeforeIncomingHit = useCombatStore.getState().mobs[mobId]!.health;
|
||||
const incomingDamage = useCombatStore.getState().advanceMobCombat(3_450, [0, 0, 0]);
|
||||
const healthAfterIncomingHit = useCombatStore.getState().mobs[mobId]!.health;
|
||||
return { incomingDamage, reflectedDamage: healthBeforeIncomingHit - healthAfterIncomingHit };
|
||||
};
|
||||
|
||||
const physicalHit = reflectedFrom("physical", 2);
|
||||
expect(physicalHit.incomingDamage).toBeGreaterThan(0);
|
||||
expect(physicalHit.reflectedDamage).toBe(4);
|
||||
const magicHit = reflectedFrom("fire", 2);
|
||||
expect(magicHit.incomingDamage).toBeGreaterThan(0);
|
||||
expect(magicHit.reflectedDamage).toBe(0);
|
||||
const outOfRangePhysicalHit = reflectedFrom("physical", 60);
|
||||
expect(outOfRangePhysicalHit.incomingDamage).toBeGreaterThan(0);
|
||||
expect(outOfRangePhysicalHit.reflectedDamage).toBe(0);
|
||||
});
|
||||
|
||||
it("uses Briar Shield's defense aura against physical enemy attacks", () => {
|
||||
const incomingDamage = (withBriarShield: boolean) => {
|
||||
clearMobRuntimeRegistry();
|
||||
initializeRomCharacter("rom-warden", "rom-warrior");
|
||||
const mobId = "briar-shield-defense-attacker";
|
||||
useCombatStore.getState().registerMob(mobId, {
|
||||
name: "Briar Shield attacker",
|
||||
maxHealth: 100_000,
|
||||
level: 100,
|
||||
attacks: [{ ...DEFAULT_ENEMY_ATTACK, id: "briar-shield-physical-swing", delivery: "projectile", initialCooldownMs: [0, 0] }],
|
||||
});
|
||||
setMobPosition(mobId, [2, 0, 0]);
|
||||
useCombatStore.getState().selectMob(mobId);
|
||||
if (withBriarShield) {
|
||||
expect(useCombatStore.getState().castAbility("rom-warden-general-1", undefined, 1_000))
|
||||
.toMatchObject({ ok: true });
|
||||
}
|
||||
expect(useCombatStore.getState().castAbility("rom-warden-primary-11", undefined, 3_000))
|
||||
.toMatchObject({ ok: true });
|
||||
useCombatStore.getState().damageMob(mobId, 1, 3_000);
|
||||
return useCombatStore.getState().advanceMobCombat(3_450, [0, 0, 0]);
|
||||
};
|
||||
|
||||
const withoutBriarShield = incomingDamage(false);
|
||||
const withBriarShield = incomingDamage(true);
|
||||
expect(withoutBriarShield).toBeGreaterThan(0);
|
||||
expect(withBriarShield).toBeGreaterThan(0);
|
||||
expect(withBriarShield).toBeLessThan(withoutBriarShield);
|
||||
});
|
||||
|
||||
it("gates Will Attack's 10% damage reduction on the passive and places it on Frantic Briar's target", () => {
|
||||
const passive = effectsFor(494610).find((effect) => effect.kind === "apply-aura");
|
||||
expect(passive?.kind).toBe("apply-aura");
|
||||
@@ -122,6 +262,66 @@ describe("RoM Warden and Warden-linked source behaviors", () => {
|
||||
expect(reducedDamage).toBeCloseTo(normalDamage * 0.9, 0);
|
||||
});
|
||||
|
||||
it("reduces Warden Punch and Sepal Stab cooldowns by one second with Tough Oak", () => {
|
||||
expect(effectsFor(493382).some((effect) => effect.kind === "cooldown-reset")).toBe(false);
|
||||
initializeRomCharacter("rom-warden", "rom-mage");
|
||||
const state = useCombatStore.getState();
|
||||
const toughOak = state.abilities.find((ability) => ability.dbcSpellId === 494207);
|
||||
expect(toughOak).toMatchObject({ passive: true, secondaryClassId: "rom-mage" });
|
||||
expect(state.auras.some((aura) => aura.targetId === PLAYER_AURA_ENTITY_ID
|
||||
&& aura.definition.utilityTags?.includes(ROM_WARDEN_TOUGH_OAK_TAG))).toBe(true);
|
||||
|
||||
const mobId = "tough-oak-target";
|
||||
useCombatStore.getState().registerMob(mobId, { name: "Tough Oak target", maxHealth: 100_000 });
|
||||
setMobPosition(mobId, [3, 0, 0]);
|
||||
useCombatStore.getState().selectMob(mobId);
|
||||
|
||||
expect(useCombatStore.getState().castAbility("rom-warden-primary-2", undefined, 1_000).ok).toBe(true);
|
||||
expect(useCombatStore.getState().cooldowns["rom-warden-primary-2"]).toBe(5_000);
|
||||
expect(useCombatStore.getState().castAbility("rom-warden-primary-14", undefined, 2_500).ok).toBe(true);
|
||||
expect(useCombatStore.getState().cooldowns["rom-warden-primary-14"]).toBe(6_500);
|
||||
});
|
||||
|
||||
it("activates Oak Blessing's attack and periodic-healing riders only with Heart of the Oak", () => {
|
||||
initializeRomCharacter("rom-warden", "rom-mage");
|
||||
const passive = useCombatStore.getState().abilities.find((ability) => ability.dbcSpellId === 494011)!;
|
||||
expect(passive).toMatchObject({ passive: true, secondaryClassId: "rom-mage" });
|
||||
expect(passive.effects).toHaveLength(1);
|
||||
expect(passive.effects[0]).toMatchObject({
|
||||
kind: "apply-aura",
|
||||
aura: { utilityTags: [ROM_WARDEN_OAK_BLESSING_TAG] },
|
||||
});
|
||||
expect(useCombatStore.getState().auras.some((aura) => aura.targetId === PLAYER_AURA_ENTITY_ID
|
||||
&& aura.definition.utilityTags?.includes(ROM_WARDEN_OAK_BLESSING_TAG))).toBe(true);
|
||||
expect(useCombatStore.getState().auras.some((aura) => aura.targetId === PLAYER_AURA_ENTITY_ID
|
||||
&& aura.definition.modifiers?.some((modifier) => modifier.kind === "stat"
|
||||
&& modifier.stat === "attack-power"))).toBe(false);
|
||||
|
||||
const now = 10_000;
|
||||
expect(useCombatStore.getState().castAbility("rom-warden-primary-10", undefined, now)).toMatchObject({ ok: true });
|
||||
const stateAfterCast = useCombatStore.getState();
|
||||
expect(stateAfterCast.auras.find((aura) => aura.targetId === PLAYER_AURA_ENTITY_ID
|
||||
&& aura.definition.modifiers?.some((modifier) => modifier.kind === "stat"
|
||||
&& modifier.stat === "attack-power"))).toMatchObject({
|
||||
expiresAt: now + 60_000,
|
||||
definition: {
|
||||
modifiers: [{ kind: "stat", stat: "attack-power", operation: "percent", value: 0.05 }],
|
||||
},
|
||||
});
|
||||
expect(stateAfterCast.effects.find((effect) => effect.kind === "hot"
|
||||
&& effect.sourceAbilityId === "rom-warden-primary-10")).toMatchObject({
|
||||
remainingTicks: 20,
|
||||
intervalMs: 3_000,
|
||||
nextTickAt: now + 3_000,
|
||||
percentMaxHealth: 0.01,
|
||||
});
|
||||
|
||||
const healthBefore = stateAfterCast.maxHealth - 200;
|
||||
useCombatStore.setState({ health: healthBefore });
|
||||
useCombatStore.getState().tick(3, now + 3_000);
|
||||
expect(useCombatStore.getState().health).toBe(healthBefore + Math.round(stateAfterCast.maxHealth * 0.01));
|
||||
});
|
||||
|
||||
it("keeps Nature Crystal active and makes its owner immune while blocking attack skills", () => {
|
||||
initializeRomCharacter("rom-warden");
|
||||
const now = Date.now();
|
||||
@@ -271,7 +471,7 @@ describe("RoM Warden and Warden-linked source behaviors", () => {
|
||||
expect(baseline.filter((effect) => effect.kind === "heal")).toHaveLength(1);
|
||||
expect(baseline.some((effect) => effect.kind === "resource-drain")).toBe(false);
|
||||
expect(empowered.filter((effect) => effect.kind === "heal")).toHaveLength(2);
|
||||
expect(empowered).toContainEqual({ kind: "resource-drain", amount: 1, burn: false, resource: "nature-power" });
|
||||
expect(empowered).toContainEqual({ kind: "resource-drain", amount: 1, burn: false, resource: "nature-power", recipient: "caster" });
|
||||
});
|
||||
|
||||
it("scales Gift Pulse by spent Nature Power and targets nearby party members", () => {
|
||||
@@ -281,7 +481,7 @@ describe("RoM Warden and Warden-linked source behaviors", () => {
|
||||
expect(empoweredHeal?.kind).toBe("heal");
|
||||
if (empoweredHeal?.kind === "heal") expect(empoweredHeal.percentMaxHealth).toBeCloseTo(0.3);
|
||||
expect(empowered.find((effect) => effect.kind === "resource-drain"))
|
||||
.toEqual({ kind: "resource-drain", amount: 3, burn: false, resource: "nature-power" });
|
||||
.toEqual({ kind: "resource-drain", amount: 3, burn: false, resource: "nature-power", recipient: "caster" });
|
||||
expect(effectsFor(499936, { naturePower: 20 }).find((effect) => effect.kind === "heal"))
|
||||
.toMatchObject({ percentMaxHealth: 1 });
|
||||
expect(ROM_ALL_ABILITIES.find((ability) => ability.dbcSpellId === 499936))
|
||||
|
||||
Reference in new issue
Block a user