// Card definitions as the engine sees them. The definitions themselves live in `content`. import type { Action, Ctx } from "./queue"; /** * A card's behaviour: given where and for whom it runs, the actions to perform. * Module 2 writes these as functions in `content`; Module 5 turns them into data. */ export type EffectFn = (ctx: Ctx) => Action[]; /** What a triggered ability listens for. */ export type TriggerOn = | "selfDamaged" // this minion takes damage | "friendlyMinionDamaged" // a minion with the same owner (this one included) takes damage | "anyMinionDamaged" // any minion takes damage | "heroDamaged"; // either hero takes damage /** The trigger's family name, as reported in `Triggered` events. */ export function triggerLabel(on: TriggerOn): string { switch (on) { case "selfDamaged": case "friendlyMinionDamaged": case "anyMinionDamaged": case "heroDamaged": return "damaged"; } } export interface Trigger { on: TriggerOn; effect: EffectFn; } /** What the engine needs to know about a card. */ export interface CardDef { name: string; attack: number; health: number; /** Triggered abilities, in printed order. */ triggers: Trigger[]; /** A spell's effect when cast; absent for minions. */ onCast?: EffectFn; } export function minion(name: string, attack: number, health: number, triggers: Trigger[] = []): CardDef { return { name, attack, health, triggers }; } export function spell(name: string, onCast: EffectFn): CardDef { return { name, attack: 0, health: 0, triggers: [], onCast }; } /** Every card the game can create, looked up by name. */ export class CardLibrary { private readonly cards = new Map(); constructor(defs: CardDef[]) { for (const def of defs) this.cards.set(def.name, def); } get(name: string): CardDef { const def = this.cards.get(name); if (!def) throw new Error(`unknown card "${name}": define it in src/content`); return def; } }