@drmxrcy/tcg-lorcana-types 0.0.0-202602060544
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +46 -0
- package/src/abilities/ability-types.ts +766 -0
- package/src/abilities/condition-types.ts +1202 -0
- package/src/abilities/cost-types.ts +344 -0
- package/src/abilities/effect-types/amount-types.ts +115 -0
- package/src/abilities/effect-types/basic-effects.ts +200 -0
- package/src/abilities/effect-types/combined-types.ts +564 -0
- package/src/abilities/effect-types/control-flow.ts +317 -0
- package/src/abilities/effect-types/index.ts +136 -0
- package/src/abilities/effect-types/modifier-effects.ts +248 -0
- package/src/abilities/effect-types/movement-effects.ts +216 -0
- package/src/abilities/effect-types/scry-effects.ts +269 -0
- package/src/abilities/helpers/Abilities.ts +172 -0
- package/src/abilities/helpers/Conditions.ts +266 -0
- package/src/abilities/helpers/Costs.ts +83 -0
- package/src/abilities/helpers/Effects.ts +182 -0
- package/src/abilities/helpers/Targets.ts +193 -0
- package/src/abilities/helpers/Triggers.ts +167 -0
- package/src/abilities/helpers/index.ts +42 -0
- package/src/abilities/index.ts +401 -0
- package/src/abilities/target-types.ts +791 -0
- package/src/abilities/trigger-types.ts +530 -0
- package/src/cards/card-types.ts +502 -0
- package/src/cards/classifications.ts +86 -0
- package/src/cards/deck-validation.ts +71 -0
- package/src/cards/index.ts +77 -0
- package/src/cards/ink-types.ts +55 -0
- package/src/game/index.ts +14 -0
- package/src/game/state-types.ts +258 -0
- package/src/index.ts +16 -0
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trigger Types for Lorcana Abilities
|
|
3
|
+
*
|
|
4
|
+
* Defines when triggered abilities activate. Lorcana uses three timing words:
|
|
5
|
+
* - "When" - triggers once when event occurs
|
|
6
|
+
* - "Whenever" - triggers each time event occurs
|
|
7
|
+
* - "At" - triggers at a specific game phase
|
|
8
|
+
*
|
|
9
|
+
* The trigger system uses the same targeting DSL as the rest of the codebase
|
|
10
|
+
* to filter what causes a trigger to fire.
|
|
11
|
+
*
|
|
12
|
+
* @example "When you play this character, draw 2 cards"
|
|
13
|
+
* ```typescript
|
|
14
|
+
* { event: "play", timing: "when", on: "SELF" }
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* @example "Whenever this character quests, gain 1 lore"
|
|
18
|
+
* ```typescript
|
|
19
|
+
* { event: "quest", timing: "whenever", on: "SELF" }
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @example "Whenever you play a character, draw a card"
|
|
23
|
+
* ```typescript
|
|
24
|
+
* { event: "play", timing: "whenever", on: { controller: "you", cardType: "character" } }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import type { Condition } from "./condition-types";
|
|
29
|
+
import type {
|
|
30
|
+
CardReference,
|
|
31
|
+
CharacterFilter,
|
|
32
|
+
ItemFilter,
|
|
33
|
+
LocationFilter,
|
|
34
|
+
TargetController,
|
|
35
|
+
} from "./target-types";
|
|
36
|
+
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// Trigger Timing
|
|
39
|
+
// ============================================================================
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* When the trigger activates relative to the event
|
|
43
|
+
*
|
|
44
|
+
* - "when" - typically one-time triggers
|
|
45
|
+
* - "whenever" - repeatable triggers
|
|
46
|
+
* - "at" - phase-based triggers
|
|
47
|
+
*/
|
|
48
|
+
export type TriggerTiming = "when" | "whenever" | "at";
|
|
49
|
+
|
|
50
|
+
// ============================================================================
|
|
51
|
+
// Trigger Events
|
|
52
|
+
// ============================================================================
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Base trigger events - what action occurred
|
|
56
|
+
*
|
|
57
|
+
* These are the fundamental game actions. Use the `on` field
|
|
58
|
+
* to specify what entity triggered the event.
|
|
59
|
+
*/
|
|
60
|
+
export type TriggerEvent =
|
|
61
|
+
// Card actions
|
|
62
|
+
| "play" // A card is played
|
|
63
|
+
| "banish" // A card is banished
|
|
64
|
+
| "leave-play" // A card leaves play (any reason)
|
|
65
|
+
|
|
66
|
+
// Character actions
|
|
67
|
+
| "quest" // A character quests
|
|
68
|
+
| "challenge" // A character challenges another
|
|
69
|
+
| "challenged" // A character is challenged
|
|
70
|
+
| "damage" // A card takes damage
|
|
71
|
+
| "exert" // A card is exerted
|
|
72
|
+
| "ready" // A card is readied
|
|
73
|
+
| "move" // A character moves to a location
|
|
74
|
+
| "sing" // A character sings a song
|
|
75
|
+
| "be-chosen" // A card is chosen by an action or ability
|
|
76
|
+
|
|
77
|
+
// Combat-specific
|
|
78
|
+
| "banish-in-challenge" // Banished specifically during a challenge
|
|
79
|
+
| "deal-damage" // Deals damage (as opposed to taking damage)
|
|
80
|
+
|
|
81
|
+
// Resource events
|
|
82
|
+
| "draw" // A card is drawn
|
|
83
|
+
| "discard" // A card is discarded
|
|
84
|
+
| "ink" // A card is put into inkwell
|
|
85
|
+
| "gain-lore" // Lore is gained
|
|
86
|
+
| "lose-lore" // Lore is lost
|
|
87
|
+
|
|
88
|
+
// Turn phases
|
|
89
|
+
| "start-turn" // Start of turn
|
|
90
|
+
| "end-turn" // End of turn
|
|
91
|
+
|
|
92
|
+
// Additional events for parser support
|
|
93
|
+
| "remove-damage" // Damage is removed from a character
|
|
94
|
+
| "return-to-hand" // A card is returned to hand
|
|
95
|
+
// Event aliases for parser compatibility
|
|
96
|
+
| "start-of-turn" // Alias for start-turn
|
|
97
|
+
| "end-of-turn" // Alias for end-turn
|
|
98
|
+
// Extended events for card text coverage
|
|
99
|
+
| "put-into-inkwell" // A card is put into inkwell
|
|
100
|
+
| "add-to-inkwell" // Alias for put-into-inkwell
|
|
101
|
+
| "put-card-under" // A card is put under another card
|
|
102
|
+
// Additional trigger events
|
|
103
|
+
| "support" // Support ability triggers
|
|
104
|
+
| "inkwell"; // Card put into inkwell
|
|
105
|
+
|
|
106
|
+
// ============================================================================
|
|
107
|
+
// Trigger Subject (what triggers the event)
|
|
108
|
+
// ============================================================================
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Card type filter for triggers
|
|
112
|
+
*/
|
|
113
|
+
export type TriggerCardType =
|
|
114
|
+
| "character"
|
|
115
|
+
| "action"
|
|
116
|
+
| "item"
|
|
117
|
+
| "location"
|
|
118
|
+
| "song"
|
|
119
|
+
| "card"; // any card type
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Simple trigger subject - common patterns as string literals
|
|
123
|
+
*/
|
|
124
|
+
export type TriggerSubjectEnum =
|
|
125
|
+
| "SELF" // This card
|
|
126
|
+
| "YOUR_CHARACTERS" // Any of your characters
|
|
127
|
+
| "YOUR_OTHER_CHARACTERS" // Your characters except this one
|
|
128
|
+
| "OPPONENT_CHARACTERS" // Opponent's characters
|
|
129
|
+
| "OPPOSING_CHARACTERS" // Alias for OPPONENT_CHARACTERS
|
|
130
|
+
| "OTHER_CHARACTERS" // Any character except this one
|
|
131
|
+
| "ANY_CHARACTER" // Any character
|
|
132
|
+
| "YOUR_ITEMS" // Any of your items
|
|
133
|
+
| "YOUR_OTHER_ITEMS" // Your items except this one
|
|
134
|
+
| "YOUR_LOCATIONS" // Any of your locations
|
|
135
|
+
| "YOUR_ACTIONS" // Any of your actions
|
|
136
|
+
| "YOUR_SONGS" // Any of your songs
|
|
137
|
+
| "YOU" // The controller (for lore/draw events)
|
|
138
|
+
| "OPPONENT" // The opponent (for lore/draw events)
|
|
139
|
+
| "ANY_PLAYER" // Any player
|
|
140
|
+
// Classification-based triggers
|
|
141
|
+
| "FLOODBORN_CHARACTERS" // Floodborn characters you play
|
|
142
|
+
| "SELF_OR_SEVEN_DWARFS_CHARACTERS" // This character or Seven Dwarfs
|
|
143
|
+
| "CINDERELLA_CHARACTERS" // Characters named Cinderella
|
|
144
|
+
| "YOUR_CHARACTERS_COST_4_OR_MORE" // Your characters with cost 4+
|
|
145
|
+
// Extended trigger subjects for card text coverage
|
|
146
|
+
| "SONGS" // Songs (for song-related triggers)
|
|
147
|
+
| "YOUR_BROOM_CHARACTERS" // Your Broom characters
|
|
148
|
+
| "YOUR_MUSKETEER_CHARACTERS" // Your Musketeer characters
|
|
149
|
+
| "YOUR_BODYGUARD_CHARACTERS" // Your Bodyguard characters
|
|
150
|
+
| "CONTROLLER" // The controller of this card
|
|
151
|
+
| "CHARACTERS_HERE" // Characters at this location
|
|
152
|
+
| "YOUR_OTHER_STEEL_CHARACTERS" // Your other Steel characters
|
|
153
|
+
// Additional trigger subjects for more card coverage
|
|
154
|
+
| "CHARACTERS_AT_LOCATION" // Characters at a location
|
|
155
|
+
| "CHARACTERS_MOVED_HERE" // Characters that moved here
|
|
156
|
+
| "OPPONENTS_CARDS" // Opponent's cards
|
|
157
|
+
| "YOUR_PIRATE_CHARACTERS" // Your Pirate characters
|
|
158
|
+
| "CHARACTER_HERE" // Character at this location (singular)
|
|
159
|
+
| "SONG" // Song (singular)
|
|
160
|
+
| "YOUR_CHARACTERS_OR_LOCATIONS" // Your characters or locations
|
|
161
|
+
| "YOUR_OTHER_AMETHYST_CHARACTERS" // Your other Amethyst characters
|
|
162
|
+
| "YOUR_CHARACTERS_OR_LOCATIONS_WITH_CARD_UNDER"; // Your characters or locations with card under
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Query-based trigger subject for complex filtering
|
|
166
|
+
*
|
|
167
|
+
* Uses the same DSL patterns as targeting
|
|
168
|
+
*/
|
|
169
|
+
export interface TriggerSubjectQuery {
|
|
170
|
+
/** Whose card triggers this */
|
|
171
|
+
controller?: TargetController;
|
|
172
|
+
|
|
173
|
+
/** What type of card */
|
|
174
|
+
cardType?: TriggerCardType;
|
|
175
|
+
|
|
176
|
+
/** Additional filters (e.g., damaged, has keyword) - supports all card types */
|
|
177
|
+
filters?: (CharacterFilter | ItemFilter | LocationFilter)[];
|
|
178
|
+
|
|
179
|
+
/** Exclude self from matching */
|
|
180
|
+
excludeSelf?: boolean;
|
|
181
|
+
|
|
182
|
+
/** Classification filter (e.g., "Floodborn", "Princess") */
|
|
183
|
+
classification?: string;
|
|
184
|
+
|
|
185
|
+
/** Name filter */
|
|
186
|
+
name?: string;
|
|
187
|
+
|
|
188
|
+
/** Has specific keyword */
|
|
189
|
+
hasKeyword?: string;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Challenge-specific trigger context
|
|
194
|
+
*
|
|
195
|
+
* Used for triggers that fire during challenges to specify
|
|
196
|
+
* whether we're interested in the attacker or defender role
|
|
197
|
+
*/
|
|
198
|
+
export interface ChallengeTriggerContext {
|
|
199
|
+
/** Which role in the challenge triggers this */
|
|
200
|
+
role: "attacker" | "defender" | "either";
|
|
201
|
+
|
|
202
|
+
/** Additional filters for the character in that role */
|
|
203
|
+
filters?: CharacterFilter[];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Extended trigger for challenge events with combat context
|
|
208
|
+
*
|
|
209
|
+
* @example "Whenever this character challenges a damaged character"
|
|
210
|
+
* ```typescript
|
|
211
|
+
* {
|
|
212
|
+
* event: "challenge",
|
|
213
|
+
* timing: "whenever",
|
|
214
|
+
* on: "SELF",
|
|
215
|
+
* defender: { filters: [{ type: "damaged" }] }
|
|
216
|
+
* }
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
export interface ChallengeTrigger extends BaseTrigger {
|
|
220
|
+
event: "challenge" | "challenged" | "banish-in-challenge";
|
|
221
|
+
|
|
222
|
+
/** Filter/context for the defender in the challenge */
|
|
223
|
+
defender?: {
|
|
224
|
+
filters?: CharacterFilter[];
|
|
225
|
+
controller?: TargetController;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
/** Filter/context for the attacker in the challenge */
|
|
229
|
+
attacker?: {
|
|
230
|
+
filters?: CharacterFilter[];
|
|
231
|
+
controller?: TargetController;
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Type guard to check if a trigger is a challenge trigger
|
|
237
|
+
*/
|
|
238
|
+
export function isChallengeTrigger(
|
|
239
|
+
trigger: Trigger,
|
|
240
|
+
): trigger is ChallengeTrigger {
|
|
241
|
+
return (
|
|
242
|
+
trigger.event === "challenge" ||
|
|
243
|
+
trigger.event === "challenged" ||
|
|
244
|
+
trigger.event === "banish-in-challenge"
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Helper type for effects that need to reference the trigger source
|
|
250
|
+
*
|
|
251
|
+
* @example "Whenever a character is banished, draw a card for each damage on IT"
|
|
252
|
+
* Here "IT" refers to the trigger source (the banished character)
|
|
253
|
+
*/
|
|
254
|
+
export type TriggerSourceReference = CardReference & { ref: "trigger-source" };
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* What entity triggers the event
|
|
258
|
+
*/
|
|
259
|
+
export type TriggerSubject = TriggerSubjectEnum | TriggerSubjectQuery;
|
|
260
|
+
|
|
261
|
+
// ============================================================================
|
|
262
|
+
// Trigger Definition
|
|
263
|
+
// ============================================================================
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Complete trigger definition
|
|
267
|
+
*
|
|
268
|
+
* @example "When you play this character"
|
|
269
|
+
* ```typescript
|
|
270
|
+
* { event: "play", timing: "when", on: "SELF" }
|
|
271
|
+
* ```
|
|
272
|
+
*
|
|
273
|
+
* @example "Whenever one of your characters quests"
|
|
274
|
+
* ```typescript
|
|
275
|
+
* { event: "quest", timing: "whenever", on: "YOUR_CHARACTERS" }
|
|
276
|
+
* ```
|
|
277
|
+
*
|
|
278
|
+
* @example "Whenever you play a Floodborn character"
|
|
279
|
+
* ```typescript
|
|
280
|
+
* {
|
|
281
|
+
* event: "play",
|
|
282
|
+
* timing: "whenever",
|
|
283
|
+
* on: { controller: "you", cardType: "character", classification: "Floodborn" }
|
|
284
|
+
* }
|
|
285
|
+
* ```
|
|
286
|
+
*
|
|
287
|
+
* @example "Whenever an opposing damaged character is banished"
|
|
288
|
+
* ```typescript
|
|
289
|
+
* {
|
|
290
|
+
* event: "banish",
|
|
291
|
+
* timing: "whenever",
|
|
292
|
+
* on: { controller: "opponent", cardType: "character", filters: [{ type: "damaged" }] }
|
|
293
|
+
* }
|
|
294
|
+
* ```
|
|
295
|
+
*
|
|
296
|
+
* @example "At the start of your turn"
|
|
297
|
+
* ```typescript
|
|
298
|
+
* { event: "start-turn", timing: "at", on: "YOU" }
|
|
299
|
+
* ```
|
|
300
|
+
*
|
|
301
|
+
* @example "The first time each turn this character quests"
|
|
302
|
+
* ```typescript
|
|
303
|
+
* {
|
|
304
|
+
* event: "quest",
|
|
305
|
+
* timing: "whenever",
|
|
306
|
+
* on: "SELF",
|
|
307
|
+
* restrictions: [{ type: "first-time-each-turn" }]
|
|
308
|
+
* }
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
|
|
312
|
+
export interface BaseTrigger {
|
|
313
|
+
/** The event that causes this trigger to fire */
|
|
314
|
+
event?: TriggerEvent;
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Multiple events that can cause this trigger to fire
|
|
318
|
+
* Used for "When you play this character and when he leaves play"
|
|
319
|
+
*/
|
|
320
|
+
events?: TriggerEvent[] | Array<{ event: string; on: string }>;
|
|
321
|
+
|
|
322
|
+
/** Timing word (when/whenever/at) - optional for parser compatibility */
|
|
323
|
+
timing?: TriggerTiming | "when-or-whenever";
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* What entity triggers this event
|
|
327
|
+
* - For card events: which card (SELF, query, etc.)
|
|
328
|
+
* - For player events: which player (YOU, OPPONENT, etc.)
|
|
329
|
+
* - For turn events: whose turn (YOU, OPPONENT)
|
|
330
|
+
*/
|
|
331
|
+
on?: TriggerSubject;
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Additional restrictions on when this trigger fires
|
|
335
|
+
* Uses the shared Restriction type from ability-types
|
|
336
|
+
*/
|
|
337
|
+
restrictions?: TriggerRestriction[];
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Challenge context - alternative to defender/attacker for challenge events
|
|
341
|
+
* Used by parser for simpler challenge-related triggers
|
|
342
|
+
*/
|
|
343
|
+
challengeContext?: ChallengeTriggerContext;
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Condition that must be true for the trigger to fire
|
|
347
|
+
* Used by parser for conditional triggers
|
|
348
|
+
*/
|
|
349
|
+
condition?: Condition;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export type Trigger = BaseTrigger | ChallengeTrigger;
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Restrictions specific to triggers
|
|
356
|
+
* Note: This is a subset of the full Restriction type, kept separate
|
|
357
|
+
* to avoid circular imports. The types are compatible.
|
|
358
|
+
*/
|
|
359
|
+
export type TriggerRestriction =
|
|
360
|
+
// Usage tracking
|
|
361
|
+
| { type: "once-per-turn" }
|
|
362
|
+
| { type: "first-time-each-turn" }
|
|
363
|
+
|
|
364
|
+
// Turn phase
|
|
365
|
+
| { type: "during-turn"; whose: "your" | "opponent" }
|
|
366
|
+
|
|
367
|
+
// Context
|
|
368
|
+
| { type: "in-challenge" };
|
|
369
|
+
|
|
370
|
+
// ============================================================================
|
|
371
|
+
// Pre-built Trigger Patterns
|
|
372
|
+
// ============================================================================
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Common trigger patterns for convenience
|
|
376
|
+
*/
|
|
377
|
+
export const COMMON_TRIGGERS = {
|
|
378
|
+
/** "When you play this character" */
|
|
379
|
+
WHEN_PLAY_SELF: {
|
|
380
|
+
event: "play",
|
|
381
|
+
timing: "when",
|
|
382
|
+
on: "SELF",
|
|
383
|
+
} as const satisfies Trigger,
|
|
384
|
+
|
|
385
|
+
/** "Whenever this character quests" */
|
|
386
|
+
WHENEVER_QUEST_SELF: {
|
|
387
|
+
event: "quest",
|
|
388
|
+
timing: "whenever",
|
|
389
|
+
on: "SELF",
|
|
390
|
+
} as const satisfies Trigger,
|
|
391
|
+
|
|
392
|
+
/** "Whenever this character challenges" */
|
|
393
|
+
WHENEVER_CHALLENGE_SELF: {
|
|
394
|
+
event: "challenge",
|
|
395
|
+
timing: "whenever",
|
|
396
|
+
on: "SELF",
|
|
397
|
+
} as const satisfies Trigger,
|
|
398
|
+
|
|
399
|
+
/** "Whenever this character is challenged" */
|
|
400
|
+
WHENEVER_CHALLENGED_SELF: {
|
|
401
|
+
event: "challenged",
|
|
402
|
+
timing: "whenever",
|
|
403
|
+
on: "SELF",
|
|
404
|
+
} as const satisfies Trigger,
|
|
405
|
+
|
|
406
|
+
/** "When this character is banished" */
|
|
407
|
+
WHEN_BANISH_SELF: {
|
|
408
|
+
event: "banish",
|
|
409
|
+
timing: "when",
|
|
410
|
+
on: "SELF",
|
|
411
|
+
} as const satisfies Trigger,
|
|
412
|
+
|
|
413
|
+
/** "When this character is banished in a challenge" */
|
|
414
|
+
WHEN_BANISH_IN_CHALLENGE: {
|
|
415
|
+
event: "banish-in-challenge",
|
|
416
|
+
timing: "when",
|
|
417
|
+
on: "SELF",
|
|
418
|
+
} as const satisfies Trigger,
|
|
419
|
+
|
|
420
|
+
/** "At the start of your turn" */
|
|
421
|
+
AT_START_OF_TURN: {
|
|
422
|
+
event: "start-turn",
|
|
423
|
+
timing: "at",
|
|
424
|
+
on: "YOU",
|
|
425
|
+
} as const satisfies Trigger,
|
|
426
|
+
|
|
427
|
+
/** "At the end of your turn" */
|
|
428
|
+
AT_END_OF_TURN: {
|
|
429
|
+
event: "end-turn",
|
|
430
|
+
timing: "at",
|
|
431
|
+
on: "YOU",
|
|
432
|
+
} as const satisfies Trigger,
|
|
433
|
+
|
|
434
|
+
/** "Whenever you play a character" */
|
|
435
|
+
WHENEVER_PLAY_CHARACTER: {
|
|
436
|
+
event: "play",
|
|
437
|
+
timing: "whenever",
|
|
438
|
+
on: { controller: "you", cardType: "character" },
|
|
439
|
+
} as const satisfies Trigger,
|
|
440
|
+
|
|
441
|
+
/** "Whenever you play a song" */
|
|
442
|
+
WHENEVER_PLAY_SONG: {
|
|
443
|
+
event: "play",
|
|
444
|
+
timing: "whenever",
|
|
445
|
+
on: { controller: "you", cardType: "song" },
|
|
446
|
+
} as const satisfies Trigger,
|
|
447
|
+
|
|
448
|
+
/** "Whenever you play a Floodborn character" */
|
|
449
|
+
WHENEVER_PLAY_FLOODBORN: {
|
|
450
|
+
event: "play",
|
|
451
|
+
timing: "whenever",
|
|
452
|
+
on: {
|
|
453
|
+
controller: "you",
|
|
454
|
+
cardType: "character",
|
|
455
|
+
classification: "Floodborn",
|
|
456
|
+
},
|
|
457
|
+
} as const satisfies Trigger,
|
|
458
|
+
|
|
459
|
+
/** "When this character leaves play" */
|
|
460
|
+
WHEN_LEAVE_PLAY: {
|
|
461
|
+
event: "leave-play",
|
|
462
|
+
timing: "when",
|
|
463
|
+
on: "SELF",
|
|
464
|
+
} as const satisfies Trigger,
|
|
465
|
+
|
|
466
|
+
/** "Whenever one of your other characters is banished" */
|
|
467
|
+
WHENEVER_YOUR_OTHER_CHARACTER_BANISHED: {
|
|
468
|
+
event: "banish",
|
|
469
|
+
timing: "whenever",
|
|
470
|
+
on: "YOUR_OTHER_CHARACTERS",
|
|
471
|
+
} as const satisfies Trigger,
|
|
472
|
+
|
|
473
|
+
/** "Whenever an opposing character is banished" */
|
|
474
|
+
WHENEVER_OPPONENT_CHARACTER_BANISHED: {
|
|
475
|
+
event: "banish",
|
|
476
|
+
timing: "whenever",
|
|
477
|
+
on: "OPPONENT_CHARACTERS",
|
|
478
|
+
} as const satisfies Trigger,
|
|
479
|
+
|
|
480
|
+
/** "Whenever you draw a card" */
|
|
481
|
+
WHENEVER_YOU_DRAW: {
|
|
482
|
+
event: "draw",
|
|
483
|
+
timing: "whenever",
|
|
484
|
+
on: "YOU",
|
|
485
|
+
} as const satisfies Trigger,
|
|
486
|
+
|
|
487
|
+
/** "Whenever you gain lore" */
|
|
488
|
+
WHENEVER_YOU_GAIN_LORE: {
|
|
489
|
+
event: "gain-lore",
|
|
490
|
+
timing: "whenever",
|
|
491
|
+
on: "YOU",
|
|
492
|
+
} as const satisfies Trigger,
|
|
493
|
+
} as const;
|
|
494
|
+
|
|
495
|
+
// ============================================================================
|
|
496
|
+
// Type Guards
|
|
497
|
+
// ============================================================================
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Check if trigger subject is a query object
|
|
501
|
+
*/
|
|
502
|
+
export function isTriggerSubjectQuery(
|
|
503
|
+
subject: TriggerSubject,
|
|
504
|
+
): subject is TriggerSubjectQuery {
|
|
505
|
+
return typeof subject === "object" && subject !== null;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Check if a trigger is a self-referential trigger
|
|
510
|
+
*/
|
|
511
|
+
export function isSelfTrigger(trigger: Trigger): boolean {
|
|
512
|
+
return trigger.on === "SELF";
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Check if a trigger is phase-based (at start/end of turn)
|
|
517
|
+
*/
|
|
518
|
+
export function isPhaseTrigger(trigger: Trigger): boolean {
|
|
519
|
+
return trigger.event === "start-turn" || trigger.event === "end-turn";
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Check if trigger has a specific restriction
|
|
524
|
+
*/
|
|
525
|
+
export function hasRestriction(
|
|
526
|
+
trigger: Trigger,
|
|
527
|
+
type: TriggerRestriction["type"],
|
|
528
|
+
): boolean {
|
|
529
|
+
return trigger.restrictions?.some((r) => r.type === type) ?? false;
|
|
530
|
+
}
|