Files
systemic-game-design-course/course/02-action-queue/reference/ts/src/engine/card.ts
T
2026-10-29 02:30:26 +01:00

67 lines
1.9 KiB
TypeScript

// 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<string, CardDef>();
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;
}
}