// Auras, the one routine that applies trigger multipliers, and the lookup for // damage redirects. // // An aura is data on a card: a continuous effect its minion has while in play. // The engine never asks "is that card on the board?" in the middle of resolving // something. It takes a snapshot of the auras in force at defined points (an Aura // Update) and every rule reads the snapshot. // // Hearthstone runs two kinds of Aura Update when the outermost Phase ends: one for // Health/Attack auras before the Death Creation Step, and one for every other aura // after it. Trigger multipliers are "Other" auras, so a minion that dies in a Death // Creation Step no longer multiplies the Deathrattles of that step's Death Phase. // Summoning a minion also runs an Aura Update. import type { TriggerOn } from "./card"; import type { EntityId, PlayerId, World } from "./world"; /** Aura Update (Other): replace the snapshot with the auras of every minion now in play. */ export function auraUpdateOther(world: World): void { world.setActiveAuras( world.minionsInPlay().flatMap((id) => world.card(world.cardName(id)).auras.map((aura) => ({ owner: world.ownerOf(id), source: id, aura })), ), ); } /** * The minion that takes damage meant for `player`'s hero, if a redirect is in force: * the oldest (order of play) of `player`'s minions with a `damageRedirect` aura in the * snapshot. The snapshot lists minions oldest first, so that is the first one found. */ export function redirectFor(world: World, player: PlayerId): EntityId | undefined { return world.activeAuras().find((a) => a.aura.type === "damageRedirect" && a.owner === player)?.source; } /** * How many times a trigger listening for `on`, on a minion `owner` controls, activates * when it resolves. Reads the snapshot from the last Aura Update. With no multiplier in * force the answer is 1. Multipliers with `stacking: "max"` contribute their largest * `times`; each `"additive"` one adds `times - 1` extra activations. */ export function activations(world: World, owner: PlayerId, on: TriggerOn): number { let max = 1; let extra = 0; for (const { owner: auraOwner, aura } of world.activeAuras()) { if (aura.type !== "triggerMultiplier") continue; const { trigger, times, stacking } = aura; if (auraOwner !== owner || trigger !== on) continue; if (stacking === "max") max = Math.max(max, times); else extra += times - 1; } return max + extra; }