The 5e-inherited card database carried 85 rows tagged expansion2; 6e has only the basic deck and Expansion Set #1, and buildDeck never dealt them. They kept leaking into things that cite the database (rulebook text, the IDIOT aid list), so the rows are gone and CardSet no longer names the set. Other editions can reintroduce their own sets when they arrive. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138A8CjeQRpvzKxuMfz1Bqc
95 lines
2.7 KiB
TypeScript
95 lines
2.7 KiB
TypeScript
// Card definitions loaded from data/cards.json (verified against the owner's
|
|
// physical 6th edition + Expansion Set #1), and physical-deck construction:
|
|
// each printed copy of a card becomes one CardInstance with a stable id.
|
|
|
|
import cardsData from "../data/cards.json";
|
|
|
|
export type CardSet = "basic" | "expansion1";
|
|
export type CardType =
|
|
| "attack"
|
|
| "neutral"
|
|
| "counteraction"
|
|
| "neutral/counteraction"
|
|
| "number"
|
|
| "object"
|
|
| "trap"
|
|
| "artifact"
|
|
| "special";
|
|
|
|
export interface CardDef {
|
|
id: string;
|
|
name: string;
|
|
set: CardSet;
|
|
cardType: CardType | null;
|
|
subtypes?: string[];
|
|
los: boolean | null;
|
|
/** Printed ADJACENT corner marking (Pick Lock, Master Key, Wizardblade). */
|
|
adjacent?: boolean;
|
|
text: string | null;
|
|
quantity: number | null;
|
|
value?: number; // number cards
|
|
alsoIn?: { set: CardSet; quantity: number }[];
|
|
faqRulings: string[];
|
|
}
|
|
|
|
export interface CardInstance {
|
|
/** e.g. "fireball#2" — stable across the whole game. */
|
|
instanceId: string;
|
|
cardId: string;
|
|
}
|
|
|
|
const defs: CardDef[] = (cardsData as { cards: CardDef[] }).cards;
|
|
const byId = new Map(defs.map((d) => [d.id, d]));
|
|
|
|
export function cardDef(cardId: string): CardDef {
|
|
const def = byId.get(cardId);
|
|
if (!def) throw new Error(`unknown card: ${cardId}`);
|
|
return def;
|
|
}
|
|
|
|
export function allCardDefs(): readonly CardDef[] {
|
|
return defs;
|
|
}
|
|
|
|
/**
|
|
* Build the physical deck for the chosen sets. The 6e basic deck is exactly
|
|
* 125 cards; adding Expansion Set #1 adds exactly 75 more (including its own
|
|
* number cards) — both counts verified against the owner's rulebook lists.
|
|
*/
|
|
export function buildDeck(sets: CardSet[]): CardInstance[] {
|
|
const instances: CardInstance[] = [];
|
|
for (const def of defs) {
|
|
let copies = 0;
|
|
if (sets.includes(def.set) && def.quantity != null) copies += def.quantity;
|
|
for (const extra of def.alsoIn ?? []) {
|
|
if (sets.includes(extra.set)) copies += extra.quantity;
|
|
}
|
|
for (let i = 1; i <= copies; i++) {
|
|
instances.push({ instanceId: `${def.id}#${i}`, cardId: def.id });
|
|
}
|
|
}
|
|
return instances;
|
|
}
|
|
|
|
export function isNumberCard(cardId: string): boolean {
|
|
return cardDef(cardId).cardType === "number";
|
|
}
|
|
|
|
export function numberValue(cardId: string): number {
|
|
const def = cardDef(cardId);
|
|
if (def.cardType !== "number" || def.value == null) {
|
|
throw new Error(`${cardId} is not a number card`);
|
|
}
|
|
return def.value;
|
|
}
|
|
|
|
/** TRAP! is discarded and redrawn if it comes up during the initial deal. */
|
|
export function isTrap(cardId: string): boolean {
|
|
return cardId === "trap";
|
|
}
|
|
|
|
/** Magic stones (Powerstone, Shieldstone, ...) — destroyed by Fireball. */
|
|
export function isMagicStone(cardId: string): boolean {
|
|
return cardDef(cardId).subtypes?.includes("stone") ?? false;
|
|
}
|