Update Healer Man server
This commit is contained in:
1 parent
4c768fbec6
commit
b16821e92e
330 files changed
+35406
-1760
No files matched your search
@@ -8,22 +8,30 @@ import { romEffectsForCast } from "./romCombatMechanics";
|
||||
import { PLAYER_AGGRO_ID } from "./aggro";
|
||||
import { registerCombatSpatialProvider, resetCombatSpatialProvider } from "./combatSpatial";
|
||||
import { DEFAULT_ENEMY_ATTACK } from "./enemyAttacks";
|
||||
import { subscribeCombatPresentation, type CombatPresentationEvent } from "./combatPresentation";
|
||||
import { resolvedEquipmentLoadout } from "./heirloomEquipment";
|
||||
import { PLAYER_EQUIPMENT_OWNER_ID } from "./equipment";
|
||||
import { combatAttackTable } from "./combatResults";
|
||||
import { usePartyStore } from "./partyStore";
|
||||
import {
|
||||
ROM_ROGUE_COERCE_DAMAGE_MULTIPLIER,
|
||||
ROM_ROGUE_COERCE_ENERGY_COST_REDUCTION,
|
||||
ROM_ROGUE_COERCE_LOW_BLOW_SPELL_ID,
|
||||
ROM_ROGUE_COERCE_PASSIVE_SPELL_ID,
|
||||
ROM_ROGUE_COERCE_TAG,
|
||||
ROM_ROGUE_DAGGER_MASTERY_TAG,
|
||||
ROM_ROGUE_INSTANT_SHADOW_READY_TAG,
|
||||
ROM_ROGUE_OFF_HAND_MASTERY_TAG,
|
||||
ROM_ROGUE_SHADOW_WALKER_PASSIVE_SPELL_ID,
|
||||
ROM_ROGUE_TREASURE_HUNTER_TAG,
|
||||
romRogueCoerceAbilityAdjustment,
|
||||
romRogueCoerceLowBlowDamageMultiplier,
|
||||
romRogueDaggerDamageMultiplier,
|
||||
romRogueEnchantedThrowShouldProc,
|
||||
romRogueShadowWalkerAdjustment,
|
||||
romRogueTreasureHunterLootChance,
|
||||
} from "./romRogueMechanics";
|
||||
import {
|
||||
ROM_ROGUE_MAGE_ENCHANTED_THROW_TAG,
|
||||
ROM_ROGUE_MAGE_CHAOS_DAGGER_TAG,
|
||||
ROM_ROGUE_PRIEST_SINISTER_METHODS_COOLDOWN_KEY,
|
||||
ROM_ROGUE_PRIEST_SINISTER_METHODS_TAG,
|
||||
romRogueMageDayOfRainBaseMissPercent,
|
||||
@@ -127,7 +135,90 @@ describe("RoM Rogue source mechanics", () => {
|
||||
.toContain(ROM_ROGUE_DAGGER_MASTERY_TAG);
|
||||
expect(treasureHunter?.kind === "apply-aura" && treasureHunter.aura.utilityTags)
|
||||
.toContain(ROM_ROGUE_TREASURE_HUNTER_TAG);
|
||||
expect(abilityById("rom-rogue-primary-12")!.effects).toEqual([]);
|
||||
expect(effectAuras("rom-rogue-primary-12")[0]).toEqual(expect.objectContaining({
|
||||
kind: "apply-aura",
|
||||
aura: expect.objectContaining({ utilityTags: [ROM_ROGUE_OFF_HAND_MASTERY_TAG] }),
|
||||
}));
|
||||
});
|
||||
|
||||
it("translates Coerce to a learned Rogue/Scout marker for current Low Blow behavior", () => {
|
||||
useCombatStore.getState().initializeCharacter({
|
||||
classId: "rom-rogue", secondaryClassId: "rom-scout", raceId: "rom-human", level: 80,
|
||||
});
|
||||
const passive = abilityById("rom-rogue-rom-scout-elite-45")!;
|
||||
const [marker] = effectAuras(passive.id);
|
||||
expect(marker?.kind === "apply-aura" && marker.aura.utilityTags).toContain(ROM_ROGUE_COERCE_TAG);
|
||||
expect(marker?.kind === "apply-aura" && marker.aura.modifiers).toBeUndefined();
|
||||
});
|
||||
|
||||
it("applies Coerce's sourced 20.2% Low Blow multiplier only to learned Rogue/Scout Low Blow", () => {
|
||||
const context = {
|
||||
classId: "rom-rogue",
|
||||
abilitySource: "runewaker",
|
||||
abilitySpellId: ROM_ROGUE_COERCE_LOW_BLOW_SPELL_ID,
|
||||
passiveTags: [ROM_ROGUE_COERCE_TAG],
|
||||
} as const;
|
||||
expect(romRogueCoerceLowBlowDamageMultiplier(context)).toBe(ROM_ROGUE_COERCE_DAMAGE_MULTIPLIER);
|
||||
expect(romRogueCoerceLowBlowDamageMultiplier({ ...context, abilitySpellId: 490324 })).toBe(1);
|
||||
expect(romRogueCoerceLowBlowDamageMultiplier({ ...context, passiveTags: [] })).toBe(1);
|
||||
});
|
||||
|
||||
it("reduces only learned Rogue/Scout Low Blow's Energy cost by the local sourced amount", () => {
|
||||
const passiveSpellIds = [ROM_ROGUE_COERCE_PASSIVE_SPELL_ID];
|
||||
expect(romRogueCoerceAbilityAdjustment(
|
||||
"rom-rogue", "rom-scout", "runewaker", ROM_ROGUE_COERCE_LOW_BLOW_SPELL_ID, passiveSpellIds,
|
||||
)).toMatchObject({ energyCostReduction: ROM_ROGUE_COERCE_ENERGY_COST_REDUCTION, cooldownIncreaseMs: 0 });
|
||||
expect(romRogueCoerceAbilityAdjustment(
|
||||
"rom-rogue", "rom-scout", "runewaker", 490324, passiveSpellIds,
|
||||
).energyCostReduction).toBe(0);
|
||||
expect(romRogueCoerceAbilityAdjustment(
|
||||
"rom-rogue", "rom-priest", "runewaker", ROM_ROGUE_COERCE_LOW_BLOW_SPELL_ID, passiveSpellIds,
|
||||
).energyCostReduction).toBe(0);
|
||||
expect(romRogueCoerceAbilityAdjustment(
|
||||
"rom-rogue", "rom-scout", "runewaker", ROM_ROGUE_COERCE_LOW_BLOW_SPELL_ID, [],
|
||||
).energyCostReduction).toBe(0);
|
||||
});
|
||||
|
||||
it("applies Coerce's reduced Low Blow Energy cost during a Rogue/Scout player cast", () => {
|
||||
useCombatStore.getState().initializeCharacter({
|
||||
classId: "rom-rogue", secondaryClassId: "rom-scout", raceId: "rom-human", level: 80,
|
||||
});
|
||||
const targetId = "rogue-coerce-target";
|
||||
useCombatStore.getState().registerMob(targetId, {
|
||||
name: "Coerce target", maxHealth: 100_000, level: 80, xpReward: 0,
|
||||
});
|
||||
setMobPosition(targetId, [0, 0, 2]);
|
||||
useCombatStore.getState().selectMob(targetId);
|
||||
useCombatStore.setState((state) => ({
|
||||
gearStats: { ...state.gearStats, hitRating: 1_000_000 },
|
||||
resourcePools: { ...state.resourcePools, energy: state.maxResourcePools.energy ?? 100 },
|
||||
}));
|
||||
const beforeEnergy = useCombatStore.getState().resourcePools.energy!;
|
||||
expect(useCombatStore.getState().castAbility("rom-rogue-primary-1", { targetActorId: targetId, now: 1_000 }))
|
||||
.toMatchObject({ ok: true });
|
||||
expect(beforeEnergy - useCombatStore.getState().resourcePools.energy!).toBe(25);
|
||||
});
|
||||
|
||||
it("scales Low Blow's damage effects by the current Coerce value for player and party cast contexts", () => {
|
||||
const lowBlow = abilityById("rom-rogue-primary-1")!;
|
||||
const context = {
|
||||
sealStacks: 0,
|
||||
bleedCount: 0,
|
||||
charged: false,
|
||||
mainHandDagger: false,
|
||||
casterMaxHealth: 1_000,
|
||||
targetHealthPercent: 1,
|
||||
passiveTags: [ROM_ROGUE_COERCE_TAG],
|
||||
} as const;
|
||||
const base = romEffectsForCast(lowBlow, { ...context, passiveTags: [] });
|
||||
const coerced = romEffectsForCast(lowBlow, context);
|
||||
const baseDamage = base.filter((effect) => effect.kind === "damage");
|
||||
const coercedDamage = coerced.filter((effect) => effect.kind === "damage");
|
||||
expect(coercedDamage).toHaveLength(baseDamage.length);
|
||||
expect(coercedDamage.map((effect) => effect.kind === "damage" && effect.coefficient))
|
||||
.toEqual(baseDamage.map((effect) => effect.kind === "damage" && effect.coefficient * ROM_ROGUE_COERCE_DAMAGE_MULTIPLIER));
|
||||
expect(romEffectsForCast(lowBlow, { ...context, passiveTags: [] }).filter((effect) => effect.kind === "damage"))
|
||||
.toEqual(baseDamage);
|
||||
});
|
||||
|
||||
it("learns Sinister Methods as a proc marker without an always-on critical aura", () => {
|
||||
@@ -301,54 +392,143 @@ describe("RoM Rogue source mechanics", () => {
|
||||
expect(romRogueDaggerDamageMultiplier({ ...context, passiveTags: [] })).toBe(1);
|
||||
});
|
||||
|
||||
it("adds the referenced Throw payload to landed Rogue/Mage normal attacks while Enchanted Throw is active", () => {
|
||||
const simulateAutoAttacks = (enchanted: boolean) => {
|
||||
clearMobRuntimeRegistry();
|
||||
useCombatStore.getState().initializeCharacter({
|
||||
classId: "rom-rogue", secondaryClassId: "rom-mage", raceId: "rom-human", level: 30,
|
||||
});
|
||||
useCombatStore.getState().registerMob("enchanted-throw-target", { name: "Target", maxHealth: 1_000_000 });
|
||||
setMobPosition("enchanted-throw-target", [0, 0, 3]);
|
||||
if (enchanted) {
|
||||
expect(useCombatStore.getState().castAbility("rom-rogue-rom-mage-elite-15", undefined, 1_000).ok).toBe(true);
|
||||
}
|
||||
useCombatStore.setState({ autoAttackTargetId: "enchanted-throw-target", nextAutoAttackAt: 1_500 });
|
||||
for (let now = 1_500; now <= 20_000; now += 250) {
|
||||
useCombatStore.getState().tick(0.25, now);
|
||||
if (useCombatStore.getState().combatEvents.some((event) => (
|
||||
event.kind === "damage" && event.abilityId === "rom-rogue-general-1"
|
||||
))) break;
|
||||
}
|
||||
return useCombatStore.getState().combatEvents.filter((event) => (
|
||||
event.kind === "damage" && event.targetActorId === "enchanted-throw-target"
|
||||
));
|
||||
it("adds the CoA Fire packet from each landed Chaos Dagger hand and mitigates it as Fire", () => {
|
||||
const weaponStats = { ...useCombatStore.getState().gearStats, hitRating: 1_000_000 };
|
||||
const mainHandDamage = { damageMin: 100, damageMax: 100, damageSchool: "physical" as const, speedMs: 2_000 };
|
||||
const offHandDamage = { damageMin: 40, damageMax: 40, damageSchool: "physical" as const, speedMs: 2_000 };
|
||||
const mainHand = testWeapon("dagger", "chaos-dagger-player-main", { ...weaponStats, meleeWeapon: mainHandDamage });
|
||||
const offHand = {
|
||||
...testWeapon("dagger", "chaos-dagger-player-off", { ...weaponStats, meleeWeapon: offHandDamage }),
|
||||
inventorySlot: "off-hand" as const,
|
||||
};
|
||||
clearMobRuntimeRegistry();
|
||||
useCombatStore.getState().initializeCharacter({
|
||||
classId: "rom-rogue", secondaryClassId: "rom-mage", raceId: "rom-human", level: 80,
|
||||
inventory: [mainHand, offHand],
|
||||
equipment: { player: { "main-hand": mainHand.instanceId, "off-hand": offHand.instanceId } },
|
||||
});
|
||||
const manaBefore = useCombatStore.getState().resourcePools.mana;
|
||||
useCombatStore.getState().registerMob("chaos-dagger-target", {
|
||||
name: "Chaos Dagger target", maxHealth: 1_000_000, level: 80,
|
||||
schoolImmunities: ["fire"],
|
||||
});
|
||||
setMobPosition("chaos-dagger-target", [0, 0, 3]);
|
||||
useCombatStore.setState({
|
||||
autoAttackTargetId: "chaos-dagger-target",
|
||||
nextAutoAttackAt: 1_500,
|
||||
nextOffhandAutoAttackAt: 1_500,
|
||||
});
|
||||
useCombatStore.getState().tick(0.25, 1_500);
|
||||
|
||||
const enchantedHits = simulateAutoAttacks(true);
|
||||
expect(enchantedHits.some((event) => event.abilityId === "rom-rogue-general-1"
|
||||
&& event.school === "physical" && event.effectiveAmount > 0)).toBe(true);
|
||||
|
||||
const ordinaryHits = simulateAutoAttacks(false);
|
||||
expect(ordinaryHits.some((event) => event.abilityId === undefined)).toBe(true);
|
||||
expect(ordinaryHits.some((event) => event.abilityId === "rom-rogue-general-1")).toBe(false);
|
||||
const hits = useCombatStore.getState().combatEvents.filter((event) => (
|
||||
event.kind === "damage" && event.targetActorId === "chaos-dagger-target"
|
||||
));
|
||||
const fireHits = hits.filter((event) => event.school === "fire");
|
||||
expect(fireHits.map((event) => event.rawAmount).sort((a, b) => a - b)).toEqual([34, 85]);
|
||||
expect(fireHits.every((event) => event.effectiveAmount === 0)).toBe(true);
|
||||
expect(hits.some((event) => event.school === "physical" && event.effectiveAmount > 0)).toBe(true);
|
||||
expect(useCombatStore.getState().resourcePools.mana).toBe(manaBefore);
|
||||
});
|
||||
|
||||
it("only qualifies landed Rogue/Mage player normal attacks for Enchanted Throw", () => {
|
||||
const context = {
|
||||
classId: "rom-rogue",
|
||||
secondaryClassId: "rom-mage",
|
||||
sourceActorId: "player",
|
||||
playerActorId: "player",
|
||||
attackKind: "melee",
|
||||
isAutoAttack: true,
|
||||
attackLanded: true,
|
||||
enchantedThrowActive: true,
|
||||
it.each([
|
||||
["unlearned below level 15", 14, "rom-mage", 80],
|
||||
["wrong secondary class", 80, "rom-warrior", 80],
|
||||
["missed attack", 80, "rom-mage", 255],
|
||||
] as const)("does not add Chaos Dagger Fire damage for %s", (_label, level, secondaryClassId, targetLevel) => {
|
||||
clearMobRuntimeRegistry();
|
||||
const gear = { ...useCombatStore.getState().gearStats, hitRating: targetLevel === 255 ? 0 : 1_000_000 };
|
||||
const mainHand = testWeapon("dagger", `chaos-dagger-gated-main-${level}-${secondaryClassId}-${targetLevel}`, {
|
||||
...gear,
|
||||
meleeWeapon: { damageMin: 100, damageMax: 100, damageSchool: "physical" as const, speedMs: 2_000 },
|
||||
});
|
||||
const offHand = {
|
||||
...testWeapon("dagger", `chaos-dagger-gated-off-${level}-${secondaryClassId}-${targetLevel}`, {
|
||||
...gear,
|
||||
meleeWeapon: { damageMin: 40, damageMax: 40, damageSchool: "physical" as const, speedMs: 2_000 },
|
||||
}),
|
||||
inventorySlot: "off-hand" as const,
|
||||
};
|
||||
useCombatStore.getState().initializeCharacter({
|
||||
classId: "rom-rogue", secondaryClassId, raceId: "rom-human", level,
|
||||
inventory: [mainHand, offHand],
|
||||
equipment: { player: { "main-hand": mainHand.instanceId, "off-hand": offHand.instanceId } },
|
||||
});
|
||||
useCombatStore.getState().registerMob("chaos-dagger-gated-target", {
|
||||
name: "Target", maxHealth: 1_000_000, level: targetLevel,
|
||||
});
|
||||
setMobPosition("chaos-dagger-gated-target", [0, 0, 3]);
|
||||
useCombatStore.setState({
|
||||
autoAttackTargetId: "chaos-dagger-gated-target",
|
||||
nextAutoAttackAt: 1_500,
|
||||
nextOffhandAutoAttackAt: 1_500,
|
||||
});
|
||||
for (let now = 1_500; now <= 20_000; now += 250) {
|
||||
useCombatStore.getState().tick(0.25, now);
|
||||
if (now === 1_500 && targetLevel === 255) break;
|
||||
}
|
||||
const damage = useCombatStore.getState().combatEvents.filter((event) => (
|
||||
event.kind === "damage" && event.targetActorId === "chaos-dagger-gated-target"
|
||||
));
|
||||
expect(damage.some((event) => event.school === "fire")).toBe(false);
|
||||
});
|
||||
|
||||
it("applies Off Hand Mastery damage only to the RoM Rogue player's equipped off-hand swing", () => {
|
||||
const simulateOffhand = (masteryActive: boolean) => {
|
||||
const weaponStats = {
|
||||
...useCombatStore.getState().gearStats,
|
||||
hitRating: 1_000_000,
|
||||
meleeWeapon: { damageMin: 2_000, damageMax: 2_000, damageSchool: "physical" as const, speedMs: 2_000 },
|
||||
};
|
||||
const mainHand = testWeapon("dagger", `rom-offhand-main-${masteryActive}`, weaponStats);
|
||||
const offHand = {
|
||||
...testWeapon("dagger", `rom-offhand-second-${masteryActive}`, weaponStats),
|
||||
inventorySlot: "off-hand" as const,
|
||||
};
|
||||
useCombatStore.getState().initializeCharacter({
|
||||
classId: "rom-rogue",
|
||||
raceId: "rom-human",
|
||||
level: 80,
|
||||
inventory: [mainHand, offHand],
|
||||
equipment: { player: { "main-hand": mainHand.instanceId, "off-hand": offHand.instanceId } },
|
||||
});
|
||||
expect(useCombatStore.getState().auras.some((aura) => (
|
||||
aura.definition.utilityTags?.includes(ROM_ROGUE_OFF_HAND_MASTERY_TAG)
|
||||
))).toBe(true);
|
||||
useCombatStore.getState().registerMob("rom-offhand-mastery-target", {
|
||||
name: "Off-hand Mastery Target", maxHealth: 10_000_000, level: 80, xpReward: 0,
|
||||
});
|
||||
setMobPosition("rom-offhand-mastery-target", [0, 0, 3]);
|
||||
useCombatStore.setState((state) => ({
|
||||
...(masteryActive ? {} : { auras: state.auras.filter((aura) => (
|
||||
!aura.definition.utilityTags?.includes(ROM_ROGUE_OFF_HAND_MASTERY_TAG)
|
||||
)) }),
|
||||
autoAttackTargetId: "rom-offhand-mastery-target",
|
||||
nextAutoAttackAt: 1_500,
|
||||
nextOffhandAutoAttackAt: 1_500,
|
||||
}));
|
||||
const presentations: CombatPresentationEvent[] = [];
|
||||
const unsubscribe = subscribeCombatPresentation((message) => {
|
||||
if (message.kind === "event") presentations.push(message.event);
|
||||
});
|
||||
for (let now = 1_500; now <= 20_000; now += 250) {
|
||||
useCombatStore.getState().tick(0.25, now);
|
||||
if (presentations.some((event) => event.abilityId === "player-auto-attack-offhand")) break;
|
||||
}
|
||||
unsubscribe();
|
||||
const combat = useCombatStore.getState();
|
||||
return {
|
||||
offhandSwingPresented: presentations.some((event) => event.abilityId === "player-auto-attack-offhand"),
|
||||
totalDamage: combat.combatEvents
|
||||
.filter((event) => event.kind === "damage" && event.targetActorId === "rom-offhand-mastery-target")
|
||||
.reduce((total, event) => total + event.effectiveAmount, 0),
|
||||
};
|
||||
};
|
||||
expect(romRogueEnchantedThrowShouldProc(context)).toBe(true);
|
||||
expect(romRogueEnchantedThrowShouldProc({ ...context, attackLanded: false })).toBe(false);
|
||||
expect(romRogueEnchantedThrowShouldProc({ ...context, enchantedThrowActive: false })).toBe(false);
|
||||
expect(romRogueEnchantedThrowShouldProc({ ...context, isAutoAttack: false })).toBe(false);
|
||||
expect(romRogueEnchantedThrowShouldProc({ ...context, secondaryClassId: "rom-warrior" })).toBe(false);
|
||||
|
||||
const withoutMastery = simulateOffhand(false);
|
||||
const withMastery = simulateOffhand(true);
|
||||
expect(withoutMastery.offhandSwingPresented).toBe(true);
|
||||
expect(withMastery.offhandSwingPresented).toBe(true);
|
||||
expect(withMastery.totalDamage).toBeGreaterThan(withoutMastery.totalDamage);
|
||||
});
|
||||
|
||||
it("adds Treasure Hunter only to the supported bonus loot chance and clamps at one", () => {
|
||||
@@ -391,13 +571,13 @@ describe("RoM Rogue source mechanics", () => {
|
||||
expect(useCombatStore.getState().cooldowns["rom-rogue-rom-mage-elite-30"]).toBe(25_000);
|
||||
});
|
||||
|
||||
it("applies Day of Rain's exact 50% miss rate only while Enchanted Throw is active", () => {
|
||||
it("applies Day of Rain's exact 50% miss rate only while Chaos Dagger is learned", () => {
|
||||
const dayOfRain = abilityById("rom-rogue-rom-mage-elite-30")!;
|
||||
const beforeBuff = romRogueMageDayOfRainBaseMissPercent(
|
||||
"rom-rogue", "rom-mage", dayOfRain.dbcSpellId, [], 6,
|
||||
);
|
||||
const withBuff = romRogueMageDayOfRainBaseMissPercent(
|
||||
"rom-rogue", "rom-mage", dayOfRain.dbcSpellId, [ROM_ROGUE_MAGE_ENCHANTED_THROW_TAG], 6,
|
||||
"rom-rogue", "rom-mage", dayOfRain.dbcSpellId, [ROM_ROGUE_MAGE_CHAOS_DAGGER_TAG], 6,
|
||||
);
|
||||
expect(beforeBuff).toBeNull();
|
||||
expect(withBuff).toBe(56);
|
||||
@@ -410,12 +590,12 @@ describe("RoM Rogue source mechanics", () => {
|
||||
rules: { baseMissChancePercent: withBuff! },
|
||||
}).missPercent).toBe(50);
|
||||
expect(romRogueMageDayOfRainBaseMissPercent(
|
||||
"rom-rogue", "rom-warrior", dayOfRain.dbcSpellId, [ROM_ROGUE_MAGE_ENCHANTED_THROW_TAG], 6,
|
||||
"rom-rogue", "rom-warrior", dayOfRain.dbcSpellId, [ROM_ROGUE_MAGE_CHAOS_DAGGER_TAG], 6,
|
||||
)).toBeNull();
|
||||
});
|
||||
|
||||
it("uses the Enchanted Throw hit penalty in Day of Rain's resolved combat result", () => {
|
||||
const outcomeFor = (now: number, enchantedThrow: boolean): string | undefined => {
|
||||
it("uses the learned Chaos Dagger hit penalty in Day of Rain's resolved combat result", () => {
|
||||
const outcomeFor = (now: number, chaosDaggerLearned: boolean): string | undefined => {
|
||||
clearMobRuntimeRegistry();
|
||||
useCombatStore.getState().initializeCharacter({
|
||||
classId: "rom-rogue", secondaryClassId: "rom-mage", raceId: "rom-human", level: 30,
|
||||
@@ -423,8 +603,10 @@ describe("RoM Rogue source mechanics", () => {
|
||||
useCombatStore.getState().registerMob("rogue-day-rain-roll-target", { name: "Target", maxHealth: 100_000 });
|
||||
setMobPosition("rogue-day-rain-roll-target", [0, 0, 1]);
|
||||
useCombatStore.getState().selectMob("rogue-day-rain-roll-target");
|
||||
if (enchantedThrow) {
|
||||
expect(useCombatStore.getState().castAbility("rom-rogue-rom-mage-elite-15", undefined, 1_000).ok).toBe(true);
|
||||
if (!chaosDaggerLearned) {
|
||||
useCombatStore.setState((state) => ({
|
||||
auras: state.auras.filter((aura) => !aura.definition.utilityTags?.includes(ROM_ROGUE_MAGE_CHAOS_DAGGER_TAG)),
|
||||
}));
|
||||
}
|
||||
expect(useCombatStore.getState().castAbility("rom-rogue-rom-mage-elite-30", undefined, now).ok).toBe(true);
|
||||
return useCombatStore.getState().combatEvents.find((event) => (
|
||||
@@ -444,17 +626,17 @@ describe("RoM Rogue source mechanics", () => {
|
||||
expect(foundEnchantedOnlyMiss).toBe(true);
|
||||
});
|
||||
|
||||
it("marks the active Rogue/Mage Enchanted Throw buff for its Day of Rain hit-rate rider", () => {
|
||||
const enchantedThrow = abilityById("rom-rogue-rom-mage-elite-15")!;
|
||||
const enchantedAuras = enchantedThrow.effects.filter((effect) => effect.kind === "apply-aura");
|
||||
expect(enchantedAuras.length).toBeGreaterThan(0);
|
||||
expect(enchantedAuras.every((effect) => !effect.aura.procs?.length)).toBe(true);
|
||||
it("marks the learned Chaos Dagger passive for Day of Rain's hit-rate rider", () => {
|
||||
const chaosDagger = abilityById("rom-rogue-rom-mage-elite-15")!;
|
||||
expect(chaosDagger.passive).toBe(true);
|
||||
expect(chaosDagger.costs).toEqual([]);
|
||||
expect(chaosDagger.description).toContain("lists 25 MP");
|
||||
expect(chaosDagger.description).toContain("no Mana charge is modeled");
|
||||
useCombatStore.getState().initializeCharacter({
|
||||
classId: "rom-rogue", secondaryClassId: "rom-mage", raceId: "rom-human", level: 15,
|
||||
});
|
||||
expect(useCombatStore.getState().castAbility("rom-rogue-rom-mage-elite-15", undefined, 1_000).ok).toBe(true);
|
||||
expect(useCombatStore.getState().auras.some((aura) => (
|
||||
aura.definition.utilityTags?.includes(ROM_ROGUE_MAGE_ENCHANTED_THROW_TAG)
|
||||
aura.definition.utilityTags?.includes(ROM_ROGUE_MAGE_CHAOS_DAGGER_TAG)
|
||||
))).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user