@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,791 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Target Types for Lorcana Abilities
|
|
3
|
+
*
|
|
4
|
+
* Defines how abilities select their targets. Uses a hybrid approach:
|
|
5
|
+
* - Common targeting patterns as string literal enums for simplicity
|
|
6
|
+
* - Complex targeting with query-based filters for advanced cases
|
|
7
|
+
*
|
|
8
|
+
* @example Simple targeting
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const target: CharacterTarget = "CHOSEN_CHARACTER";
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* @example Complex targeting with filters
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const target: CharacterTarget = {
|
|
16
|
+
* selector: "chosen",
|
|
17
|
+
* owner: "opponent",
|
|
18
|
+
* filter: [{ type: "damaged" }, { type: "strength-comparison", comparison: "less-or-equal", value: 3 }]
|
|
19
|
+
* };
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type { TargetDSL } from "@drmxrcy/tcg-core-types";
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Player Targeting
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Player target - who is affected by the ability
|
|
31
|
+
*
|
|
32
|
+
* @example "draw 2 cards" targets CONTROLLER
|
|
33
|
+
* @example "each opponent loses 1 lore" targets EACH_OPPONENT
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
export type PlayerTarget =
|
|
37
|
+
| "CONTROLLER" // The player who controls this card
|
|
38
|
+
| "OPPONENT" // A single opponent (2-player default)
|
|
39
|
+
| "OPPONENTS" // All opponents (alias for EACH_OPPONENT in 2-player)
|
|
40
|
+
| "EACH_PLAYER" // All players including controller
|
|
41
|
+
| "EACH_OPPONENT" // All opponents
|
|
42
|
+
| "CHOSEN_PLAYER" // A player chosen by the controller
|
|
43
|
+
| "CARD_OWNER" // The owner of the target card (context-dependent)
|
|
44
|
+
| "CURRENT_TURN" // The player whose turn it is
|
|
45
|
+
// Additional targets for parser support
|
|
46
|
+
| "NEXT_CHARACTER" // The next character played (for cost reduction)
|
|
47
|
+
| "SEVEN_DWARFS_CHARACTERS" // Seven Dwarfs characters (for lore gain)
|
|
48
|
+
| "THAT_PLAYER" // Reference to a previously mentioned player
|
|
49
|
+
| "CHALLENGER_OWNER" // Owner of the challenging character
|
|
50
|
+
| "THEIR_CHOSEN_CHARACTER" // Their chosen character (for each player effects)
|
|
51
|
+
| "PAWPSICLE_ITEM" // Specific item reference
|
|
52
|
+
// Extended targets for card text coverage
|
|
53
|
+
| "ALL_PLAYERS" // All players (for effects like "each player discards")
|
|
54
|
+
| "SELF" // Self reference (for gain lore effects on self)
|
|
55
|
+
| "BROOM_CHARACTERS" // Broom characters (for lore gain)
|
|
56
|
+
| "CHALLENGING_PLAYER" // The player doing the challenge
|
|
57
|
+
| "NEXT_ACTION" // The next action played (for cost reduction)
|
|
58
|
+
| "NEXT_ITEM" // The next item played (for cost reduction)
|
|
59
|
+
| "CARD_FROM_HAND" // A card from hand (for discard effects)
|
|
60
|
+
| "CHARACTERS_COST_3_OR_LESS" // Characters with cost 3 or less
|
|
61
|
+
| "CHARACTERS_COST_2_OR_LESS" // Characters with cost 2 or less
|
|
62
|
+
| "OPPOSING_CHARACTERS" // Alias for ALL_OPPOSING_CHARACTERS
|
|
63
|
+
| "LOCATIONS"; // All locations (for lore gain effects)
|
|
64
|
+
|
|
65
|
+
// ============================================================================
|
|
66
|
+
// Card References (Context-Aware)
|
|
67
|
+
// ============================================================================
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Context-aware card references for abilities
|
|
71
|
+
*
|
|
72
|
+
* These allow effects to reference cards based on the current game context
|
|
73
|
+
* rather than requiring explicit targeting.
|
|
74
|
+
*
|
|
75
|
+
* @example Reference the card that triggered an ability
|
|
76
|
+
* ```typescript
|
|
77
|
+
* { ref: "trigger-source" }
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* @example Reference the attacker in a challenge
|
|
81
|
+
* ```typescript
|
|
82
|
+
* { ref: "attacker" }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export type CardReference =
|
|
86
|
+
// Self-referential
|
|
87
|
+
| { ref: "self" } // This card (the one with the ability)
|
|
88
|
+
|
|
89
|
+
// Trigger context (for triggered abilities)
|
|
90
|
+
| { ref: "trigger-source" } // Card that triggered the ability
|
|
91
|
+
|
|
92
|
+
// Challenge context
|
|
93
|
+
| { ref: "attacker" } // Character doing the challenge
|
|
94
|
+
| { ref: "defender" } // Character being challenged
|
|
95
|
+
|
|
96
|
+
// Effect chain context
|
|
97
|
+
| { ref: "previous-target" } // Target selected earlier in effect chain
|
|
98
|
+
|
|
99
|
+
// Player context
|
|
100
|
+
| { ref: "controller" } // Controller of this card
|
|
101
|
+
| { ref: "opponent" }; // Opponent of controller (1v1)
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Check if a value is a CardReference
|
|
105
|
+
*/
|
|
106
|
+
export function isCardReference(value: unknown): value is CardReference {
|
|
107
|
+
return (
|
|
108
|
+
typeof value === "object" &&
|
|
109
|
+
value !== null &&
|
|
110
|
+
"ref" in value &&
|
|
111
|
+
typeof (value as CardReference).ref === "string"
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ============================================================================
|
|
116
|
+
// Lorcana Context
|
|
117
|
+
// ============================================================================
|
|
118
|
+
|
|
119
|
+
export interface LorcanaContext {
|
|
120
|
+
self?: boolean;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ============================================================================
|
|
124
|
+
// Character Targeting - Common Patterns (Enums)
|
|
125
|
+
// ============================================================================
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Common character targeting patterns as string literals
|
|
129
|
+
*
|
|
130
|
+
* These cover ~80% of targeting cases in Lorcana card texts
|
|
131
|
+
*/
|
|
132
|
+
export type CharacterTargetEnum =
|
|
133
|
+
// Self-referential
|
|
134
|
+
| "SELF" // This character
|
|
135
|
+
| "THIS_CHARACTER" // Alias for SELF
|
|
136
|
+
|
|
137
|
+
// Chosen (requires player choice)
|
|
138
|
+
| "CHOSEN_CHARACTER" // Any character
|
|
139
|
+
| "CHOSEN_OPPOSING_CHARACTER" // Opponent's character
|
|
140
|
+
| "CHOSEN_CHARACTER_OF_YOURS" // Your character
|
|
141
|
+
| "ANOTHER_CHOSEN_CHARACTER" // Any character except self
|
|
142
|
+
| "ANOTHER_CHOSEN_CHARACTER_OF_YOURS" // Your character except self
|
|
143
|
+
| "CHOSEN_DAMAGED_CHARACTER" // Any damaged character
|
|
144
|
+
| "CHOSEN_DAMAGED_OPPOSING_CHARACTER" // Opponent's damaged character
|
|
145
|
+
| "CHOSEN_EXERTED_CHARACTER" // Any exerted character
|
|
146
|
+
| "CHOSEN_OTHER_CHARACTER" // Another character (not self)
|
|
147
|
+
| "CHOSEN_CHALLENGED_CHARACTER" // Character being challenged
|
|
148
|
+
|
|
149
|
+
// Your chosen characters
|
|
150
|
+
| "YOUR_CHOSEN_CHARACTER" // Your chosen character
|
|
151
|
+
| "YOUR_CHOSEN_DAMAGED_CHARACTER" // Your chosen damaged character
|
|
152
|
+
| "YOUR_CHOSEN_VILLAIN" // Your chosen Villain character
|
|
153
|
+
| "YOUR_CHOSEN_ITEM" // Your chosen item
|
|
154
|
+
|
|
155
|
+
// All/Each (affects multiple)
|
|
156
|
+
| "ALL_CHARACTERS" // Every character in play
|
|
157
|
+
| "ALL_OPPOSING_CHARACTERS" // All of opponent's characters
|
|
158
|
+
| "YOUR_CHARACTERS" // All of your characters
|
|
159
|
+
| "YOUR_OTHER_CHARACTERS" // All of your characters except self
|
|
160
|
+
| "YOUR_OTHER_CHARACTER" // Another of your characters (singular)
|
|
161
|
+
| "YOUR_OTHER_2_CHARACTERS" // 2 other characters of yours
|
|
162
|
+
| "EACH_CHARACTER" // Same as ALL_CHARACTERS
|
|
163
|
+
| "EACH_OPPOSING_CHARACTER" // Same as ALL_OPPOSING_CHARACTERS
|
|
164
|
+
|
|
165
|
+
// Up to N characters
|
|
166
|
+
| "UP_TO_2_CHOSEN_CHARACTERS" // Up to 2 chosen characters
|
|
167
|
+
|
|
168
|
+
// Classification-based targets
|
|
169
|
+
| "YOUR_OTHER_SEVEN_DWARFS_CHARACTERS" // Your Seven Dwarfs except self
|
|
170
|
+
| "YOUR_PRINCE_PRINCESS_KING_QUEEN_CHARACTERS" // Your royalty characters
|
|
171
|
+
| "YOUR_EXERTED_CHARACTERS" // Your exerted characters
|
|
172
|
+
| "YOUR_EVASIVE_CHARACTERS" // Your Evasive characters
|
|
173
|
+
| "YOUR_RECKLESS_CHARACTERS" // Your Reckless characters
|
|
174
|
+
| "CHOSEN_DRAGON_CHARACTER" // Chosen Dragon character
|
|
175
|
+
|
|
176
|
+
// Played card reference
|
|
177
|
+
| "PLAYED_CARD" // The card that was just played
|
|
178
|
+
| "THEIR_CHOSEN_CHARACTER" // Their chosen character (for each player effects)
|
|
179
|
+
| "CHOSEN_OPPOSING_CHARACTER_3_STRENGTH_OR_LESS" // Opposing character with 3 or less strength
|
|
180
|
+
|
|
181
|
+
// Challenge context
|
|
182
|
+
| "challenging-character" // The character doing the challenge
|
|
183
|
+
| "challenged-character" // The character being challenged
|
|
184
|
+
|
|
185
|
+
// Extended character targets for card text coverage
|
|
186
|
+
| "YOUR_MUSKETEER_CHARACTERS" // Your Musketeer characters
|
|
187
|
+
| "YOUR_OTHER_MUSKETEER_CHARACTERS" // Your other Musketeer characters
|
|
188
|
+
| "YOUR_JETSAM_CHARACTERS" // Your Jetsam characters
|
|
189
|
+
| "YOUR_FLOTSAM_CHARACTERS" // Your Flotsam characters
|
|
190
|
+
| "YOUR_BROOM_CHARACTERS" // Your Broom characters
|
|
191
|
+
| "YOUR_PETER_PAN_CHARACTERS" // Your Peter Pan characters
|
|
192
|
+
| "YOUR_BODYGUARD_CHARACTERS" // Your Bodyguard characters
|
|
193
|
+
| "OPPOSING_CHARACTERS" // Alias for ALL_OPPOSING_CHARACTERS
|
|
194
|
+
| "OPPOSING_EVASIVE_CHARACTERS" // Opposing Evasive characters
|
|
195
|
+
| "CHOSEN_VILLAIN_CHARACTER" // Chosen Villain character
|
|
196
|
+
| "CHOSEN_TE_KA_CHARACTER" // Chosen Te Ka character
|
|
197
|
+
| "CHALLENGED_CHARACTER" // The character being challenged (uppercase)
|
|
198
|
+
| "CHALLENGING_CHARACTER" // The character doing the challenge (uppercase)
|
|
199
|
+
| "BANISHED_CHARACTER" // The character that was banished
|
|
200
|
+
// Extended character targets for additional card text coverage
|
|
201
|
+
| "CHARACTERS_HERE" // Characters at this location
|
|
202
|
+
| "YOUR_OTHER_STEEL_CHARACTERS" // Your other Steel characters
|
|
203
|
+
| "YOUR_DEITY_CHARACTERS" // Your Deity characters
|
|
204
|
+
| "UP_TO_2_YOUR_CHARACTERS" // Up to 2 of your characters
|
|
205
|
+
| "THAT_LOCATION" // Reference to a previously mentioned location
|
|
206
|
+
| "CHOSEN_OPPOSING_DEITY_CHARACTER" // Chosen opposing Deity character
|
|
207
|
+
| "CHOSEN_CHARACTER_OR_LOCATION" // Chosen character or location
|
|
208
|
+
// Additional targets for set-005 and beyond
|
|
209
|
+
| "CHOSEN_CHARACTER_IN_DISCARD" // Chosen character in discard
|
|
210
|
+
| "CHOSEN_CARD_IN_DISCARD" // Chosen card in discard
|
|
211
|
+
| "CHOSEN_CARD_FROM_DISCARD" // Chosen card from discard (alias)
|
|
212
|
+
| "ALL_CHARACTERS_WITH_NAME_IN_DISCARD" // All characters with named card in discard
|
|
213
|
+
| "CHOSEN_CHARACTER_HERE" // Chosen character at this location
|
|
214
|
+
| "CHARACTERS_WITH_SUPPORT_HERE" // Characters with Support at this location
|
|
215
|
+
| "CHOSEN_ITEM_OR_LOCATION" // Chosen item or location
|
|
216
|
+
| "CHOSEN_CHARACTER_ITEM_OR_LOCATION" // Chosen character, item, or location
|
|
217
|
+
| "CHOSEN_CHARACTERS_OR_LOCATIONS" // Chosen characters or locations (plural)
|
|
218
|
+
| "YOUR_VILLAIN_CHARACTERS" // Your Villain characters
|
|
219
|
+
| "YOUR_ALIEN_CHARACTERS" // Your Alien characters
|
|
220
|
+
| "YOUR_OTHER_FAIRY_CHARACTERS" // Your other Fairy characters
|
|
221
|
+
| "YOUR_PUPPY_CHARACTERS" // Your Puppy characters
|
|
222
|
+
| "YOUR_OTHER_HERO_CHARACTERS" // Your other Hero characters
|
|
223
|
+
| "YOUR_ALLY_CHARACTERS" // Your Ally characters
|
|
224
|
+
| "YOUR_OTHER_AMBER_CHARACTERS" // Your other Amber characters
|
|
225
|
+
| "YOUR_OTHER_RUBY_CHARACTERS" // Your other Ruby characters
|
|
226
|
+
| "YOUR_OTHER_SAPPHIRE_CHARACTERS" // Your other Sapphire characters
|
|
227
|
+
| "YOUR_OTHER_EMERALD_CHARACTERS" // Your other Emerald characters
|
|
228
|
+
| "YOUR_DETECTIVE_CHARACTERS" // Your Detective characters
|
|
229
|
+
| "CHOSEN_DETECTIVE_CHARACTER" // Chosen Detective character
|
|
230
|
+
| "CHOSEN_OPPOSING_DAMAGED_CHARACTER" // Chosen opposing damaged character
|
|
231
|
+
| "CHOSEN_CHARACTERS" // Chosen characters (plural)
|
|
232
|
+
| "OPPONENT_CHOSEN_CHARACTER" // Opponent's chosen character
|
|
233
|
+
| "CHOSEN_OPPONENT" // Chosen opponent (for multiplayer)
|
|
234
|
+
| "YOUR_CHARACTERS_OR_LOCATIONS" // Your characters or locations
|
|
235
|
+
| "YOUR_CHARACTERS_OR_LOCATIONS_WITH_CARD_UNDER" // Your characters or locations with card under
|
|
236
|
+
| "CHARACTERS_AT_LOCATION" // Characters at a location
|
|
237
|
+
| "REVEALED_CARD" // The revealed card
|
|
238
|
+
| "TOP_OF_DECK" // Top of deck
|
|
239
|
+
// Additional targets for more card coverage
|
|
240
|
+
| "CHARACTER_HERE" // Character at this location (singular)
|
|
241
|
+
| "YOUR_PIRATE_CHARACTERS" // Your Pirate characters
|
|
242
|
+
| "YOUR_HERO_CHARACTERS" // Your Hero characters
|
|
243
|
+
| "YOUR_GARGOYLE_CHARACTERS" // Your Gargoyle characters
|
|
244
|
+
| "YOUR_DEMONA_CHARACTERS" // Your Demona characters
|
|
245
|
+
| "YOUR_OTHER_DETECTIVE_CHARACTERS" // Your other Detective characters
|
|
246
|
+
| "YOUR_OTHER_AMETHYST_CHARACTERS" // Your other Amethyst characters
|
|
247
|
+
| "YOUR_HAND" // Your hand (for card targets)
|
|
248
|
+
| "TRIGGERING_CHARACTER" // The character that triggered the ability
|
|
249
|
+
| "TRIGGERING_CARD" // The card that triggered the ability
|
|
250
|
+
| "THAT_ITEM" // Reference to a previously mentioned item
|
|
251
|
+
// More additional targets
|
|
252
|
+
| "YOUR_OTHER_SAPPHIRE_CHARACTERS" // Your other Sapphire characters
|
|
253
|
+
| "YOUR_OTHER_DETECTIVE_CHARACTERS" // Your other Detective characters
|
|
254
|
+
| "SONG_CARD" // A song card
|
|
255
|
+
| "RANDOM_CARDS_IN_INKWELL" // Random cards in inkwell
|
|
256
|
+
| "YOUR_RECKLESS_CHARACTERS" // Your Reckless characters
|
|
257
|
+
| "OPPOSING_DAMAGED_CHARACTERS" // Opposing damaged characters
|
|
258
|
+
| "ALL_CARDS_IN_INKWELL"; // All cards in inkwell
|
|
259
|
+
|
|
260
|
+
// ============================================================================
|
|
261
|
+
// Character Targeting - Query-Based (Complex)
|
|
262
|
+
// ============================================================================
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Zone where targets can be found
|
|
266
|
+
*/
|
|
267
|
+
export type TargetZone = "play" | "hand" | "discard" | "deck" | "inkwell";
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Who controls the target
|
|
271
|
+
*/
|
|
272
|
+
export type TargetController = "you" | "opponent" | "any" | "CURRENT_TURN";
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Comparison operators for numeric filters
|
|
276
|
+
*/
|
|
277
|
+
export type ComparisonOperator =
|
|
278
|
+
| "equal"
|
|
279
|
+
| "not-equal"
|
|
280
|
+
| "less"
|
|
281
|
+
| "greater"
|
|
282
|
+
| "less-or-equal"
|
|
283
|
+
| "greater-or-equal"
|
|
284
|
+
// Alternative naming conventions (for parser compatibility)
|
|
285
|
+
| "greater-than"
|
|
286
|
+
| "less-than"
|
|
287
|
+
| "more-than" // Alias for greater
|
|
288
|
+
// Additional aliases for natural language
|
|
289
|
+
| "or-more" // Alias for greater-or-equal
|
|
290
|
+
| "or-less"; // Alias for less-or-equal
|
|
291
|
+
|
|
292
|
+
// ============================================================================
|
|
293
|
+
// Shared Filter Types (used across Character, Location, Item targeting)
|
|
294
|
+
// ============================================================================
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Base filters shared across all card types
|
|
298
|
+
* Uses a unified DSL so contributors only learn one pattern
|
|
299
|
+
*/
|
|
300
|
+
|
|
301
|
+
// State filters
|
|
302
|
+
export interface DamagedFilter {
|
|
303
|
+
type: "damaged";
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export interface UndamagedFilter {
|
|
307
|
+
type: "undamaged";
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export interface ExertedFilter {
|
|
311
|
+
type: "exerted";
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export interface ReadyFilter {
|
|
315
|
+
type: "ready";
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Property filters
|
|
319
|
+
export interface HasKeywordFilter {
|
|
320
|
+
type: "has-keyword";
|
|
321
|
+
keyword: string;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export interface HasClassificationFilter {
|
|
325
|
+
type: "has-classification";
|
|
326
|
+
classification: string;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export interface HasNameFilter {
|
|
330
|
+
type: "has-name";
|
|
331
|
+
name: string;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Numeric comparison filters
|
|
335
|
+
export interface CostComparisonFilter {
|
|
336
|
+
type: "cost-comparison";
|
|
337
|
+
comparison: ComparisonOperator;
|
|
338
|
+
value: number;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export interface StrengthComparisonFilter {
|
|
342
|
+
type: "strength-comparison";
|
|
343
|
+
comparison: ComparisonOperator;
|
|
344
|
+
value: number;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export interface WillpowerComparisonFilter {
|
|
348
|
+
type: "willpower-comparison";
|
|
349
|
+
comparison: ComparisonOperator;
|
|
350
|
+
value: number;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export interface LoreComparisonFilter {
|
|
354
|
+
type: "lore-comparison";
|
|
355
|
+
comparison: ComparisonOperator;
|
|
356
|
+
value: number;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export interface MoveCostComparisonFilter {
|
|
360
|
+
type: "move-cost-comparison";
|
|
361
|
+
comparison: ComparisonOperator;
|
|
362
|
+
value: number;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// ============================================================================
|
|
366
|
+
// Source/Reference Filters
|
|
367
|
+
// ============================================================================
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Filter by relationship to source card
|
|
371
|
+
*
|
|
372
|
+
* @example Filter to exclude self
|
|
373
|
+
* ```typescript
|
|
374
|
+
* { type: "source", ref: "other" }
|
|
375
|
+
* ```
|
|
376
|
+
*
|
|
377
|
+
* @example Filter to match the card that triggered this ability
|
|
378
|
+
* ```typescript
|
|
379
|
+
* { type: "source", ref: "trigger-source" }
|
|
380
|
+
* ```
|
|
381
|
+
*/
|
|
382
|
+
export interface SourceFilter {
|
|
383
|
+
type: "source";
|
|
384
|
+
ref: "self" | "other" | "trigger-source";
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Filter by challenge role
|
|
389
|
+
*
|
|
390
|
+
* @example Filter to match the attacker in a challenge
|
|
391
|
+
* ```typescript
|
|
392
|
+
* { type: "challenge-role", role: "attacker" }
|
|
393
|
+
* ```
|
|
394
|
+
*/
|
|
395
|
+
export interface ChallengeRoleFilter {
|
|
396
|
+
type: "challenge-role";
|
|
397
|
+
role: "attacker" | "defender";
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// ============================================================================
|
|
401
|
+
// Zone and Owner Filters
|
|
402
|
+
// ============================================================================
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Filter by zone
|
|
406
|
+
*
|
|
407
|
+
* @example Filter to cards in play
|
|
408
|
+
* ```typescript
|
|
409
|
+
* { type: "zone", zone: "play" }
|
|
410
|
+
* ```
|
|
411
|
+
*/
|
|
412
|
+
export interface ZoneFilter {
|
|
413
|
+
type: "zone";
|
|
414
|
+
zone: TargetZone | TargetZone[];
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Filter by owner/controller
|
|
419
|
+
*
|
|
420
|
+
* @example Filter to opponent's cards
|
|
421
|
+
* ```typescript
|
|
422
|
+
* { type: "owner", owner: "opponent" }
|
|
423
|
+
* ```
|
|
424
|
+
*/
|
|
425
|
+
export interface OwnerFilter {
|
|
426
|
+
type: "owner";
|
|
427
|
+
owner: "you" | "opponent" | "any";
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// ============================================================================
|
|
431
|
+
// Generic Attribute Filter
|
|
432
|
+
// ============================================================================
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Generic attribute comparison - extensible for future attributes
|
|
436
|
+
* This provides flexibility beyond the specific comparison filters
|
|
437
|
+
*/
|
|
438
|
+
export type AttributeFilter =
|
|
439
|
+
| AttributeNumericFilter
|
|
440
|
+
| AttributeStringFilter
|
|
441
|
+
| AttributeBooleanFilter;
|
|
442
|
+
|
|
443
|
+
export interface AttributeNumericFilter {
|
|
444
|
+
type: "attribute";
|
|
445
|
+
attribute: "cost" | "strength" | "willpower" | "lore";
|
|
446
|
+
comparison: ComparisonOperator;
|
|
447
|
+
value: number;
|
|
448
|
+
/** Ignore stat bonuses when comparing */
|
|
449
|
+
ignoreBonuses?: boolean;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
export interface AttributeStringFilter {
|
|
453
|
+
type: "attribute";
|
|
454
|
+
attribute: "name" | "title";
|
|
455
|
+
comparison: "equals" | "contains";
|
|
456
|
+
value: string;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export interface AttributeBooleanFilter {
|
|
460
|
+
type: "attribute";
|
|
461
|
+
attribute: "inkwell";
|
|
462
|
+
value: boolean;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* All filters that can be applied to any card type
|
|
467
|
+
* Specific card types may only support a subset
|
|
468
|
+
*/
|
|
469
|
+
export type CardFilter =
|
|
470
|
+
// State
|
|
471
|
+
| DamagedFilter
|
|
472
|
+
| UndamagedFilter
|
|
473
|
+
| ExertedFilter
|
|
474
|
+
| ReadyFilter
|
|
475
|
+
// Property
|
|
476
|
+
| HasKeywordFilter
|
|
477
|
+
| HasClassificationFilter
|
|
478
|
+
| HasNameFilter
|
|
479
|
+
// Numeric
|
|
480
|
+
| CostComparisonFilter
|
|
481
|
+
| StrengthComparisonFilter
|
|
482
|
+
| WillpowerComparisonFilter
|
|
483
|
+
| LoreComparisonFilter
|
|
484
|
+
| MoveCostComparisonFilter
|
|
485
|
+
// Source/Reference
|
|
486
|
+
| SourceFilter
|
|
487
|
+
| ChallengeRoleFilter
|
|
488
|
+
// Zone/Owner
|
|
489
|
+
| ZoneFilter
|
|
490
|
+
| OwnerFilter
|
|
491
|
+
// Generic Attribute
|
|
492
|
+
| AttributeFilter;
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Filters applicable to characters
|
|
496
|
+
* (all except move-cost which is location-specific)
|
|
497
|
+
*/
|
|
498
|
+
export type CharacterFilter =
|
|
499
|
+
// State
|
|
500
|
+
| DamagedFilter
|
|
501
|
+
| UndamagedFilter
|
|
502
|
+
| ExertedFilter
|
|
503
|
+
| ReadyFilter
|
|
504
|
+
// Property
|
|
505
|
+
| HasKeywordFilter
|
|
506
|
+
| HasClassificationFilter
|
|
507
|
+
| HasNameFilter
|
|
508
|
+
// Numeric comparisons
|
|
509
|
+
| CostComparisonFilter
|
|
510
|
+
| StrengthComparisonFilter
|
|
511
|
+
| WillpowerComparisonFilter
|
|
512
|
+
| LoreComparisonFilter
|
|
513
|
+
// Source/Reference
|
|
514
|
+
| SourceFilter
|
|
515
|
+
| ChallengeRoleFilter
|
|
516
|
+
// Zone/Owner
|
|
517
|
+
| ZoneFilter
|
|
518
|
+
| OwnerFilter
|
|
519
|
+
// Generic Attribute
|
|
520
|
+
| AttributeFilter;
|
|
521
|
+
|
|
522
|
+
// ============================================================================
|
|
523
|
+
// Character Targeting - Strict Query Variants
|
|
524
|
+
// ============================================================================
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Base properties shared by all character query variants
|
|
528
|
+
* Extended from generic TargetDSL
|
|
529
|
+
*/
|
|
530
|
+
export type CharacterQueryBase = TargetDSL<CharacterFilter[], LorcanaContext>;
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Target exactly N characters
|
|
534
|
+
*
|
|
535
|
+
* @example Target exactly 2 characters
|
|
536
|
+
* ```typescript
|
|
537
|
+
* {
|
|
538
|
+
* selector: "chosen",
|
|
539
|
+
* count: { exactly: 2 },
|
|
540
|
+
* owner: "opponent"
|
|
541
|
+
* }
|
|
542
|
+
* ```
|
|
543
|
+
*/
|
|
544
|
+
export interface ExactCountCharacterQuery extends CharacterQueryBase {
|
|
545
|
+
count: number | { exactly: number };
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Target up to N characters (player chooses 0 to maxCount)
|
|
550
|
+
*
|
|
551
|
+
* @example Target up to 2 damaged opposing characters
|
|
552
|
+
* ```typescript
|
|
553
|
+
* {
|
|
554
|
+
* selector: "chosen",
|
|
555
|
+
* owner: "opponent",
|
|
556
|
+
* filter: [{ type: "damaged" }],
|
|
557
|
+
* count: { upTo: 2 }
|
|
558
|
+
* }
|
|
559
|
+
* ```
|
|
560
|
+
*/
|
|
561
|
+
export interface UpToCountCharacterQuery extends CharacterQueryBase {
|
|
562
|
+
count: { upTo: number };
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Target all matching characters
|
|
567
|
+
*
|
|
568
|
+
* @example Target all opposing characters
|
|
569
|
+
* ```typescript
|
|
570
|
+
* {
|
|
571
|
+
* selector: "all",
|
|
572
|
+
* owner: "opponent"
|
|
573
|
+
* }
|
|
574
|
+
* ```
|
|
575
|
+
*/
|
|
576
|
+
export interface AllMatchingCharacterQuery extends CharacterQueryBase {
|
|
577
|
+
count: "all";
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Complex character targeting with query-based filters
|
|
582
|
+
*
|
|
583
|
+
* Uses discriminated union to ensure type safety:
|
|
584
|
+
* - `count: number` for exact targeting
|
|
585
|
+
* - `count: "up-to"` requires `maxCount`
|
|
586
|
+
* - `count: "all"` for all matching
|
|
587
|
+
*/
|
|
588
|
+
export type CharacterTargetQuery =
|
|
589
|
+
| ExactCountCharacterQuery
|
|
590
|
+
| UpToCountCharacterQuery
|
|
591
|
+
| AllMatchingCharacterQuery;
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Union type for all character targeting options
|
|
595
|
+
*/
|
|
596
|
+
export type CharacterTarget =
|
|
597
|
+
| CharacterTargetEnum
|
|
598
|
+
| CharacterTargetQuery
|
|
599
|
+
| CardReference;
|
|
600
|
+
|
|
601
|
+
// ============================================================================
|
|
602
|
+
// Location Targeting
|
|
603
|
+
// ============================================================================
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Common location targeting patterns
|
|
607
|
+
*/
|
|
608
|
+
export type LocationTargetEnum =
|
|
609
|
+
| "CHOSEN_LOCATION"
|
|
610
|
+
| "CHOSEN_OPPOSING_LOCATION"
|
|
611
|
+
| "YOUR_LOCATIONS"
|
|
612
|
+
| "ALL_OPPOSING_LOCATIONS"
|
|
613
|
+
| "THIS_LOCATION" // For abilities on locations
|
|
614
|
+
| "CHARACTERS_HERE"; // Characters at this location (for location abilities)
|
|
615
|
+
|
|
616
|
+
// ============================================================================
|
|
617
|
+
// Location Targeting - Strict Query Variants
|
|
618
|
+
// ============================================================================
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Filters applicable to locations
|
|
622
|
+
* Uses shared filter types for consistency
|
|
623
|
+
*/
|
|
624
|
+
export type LocationFilter =
|
|
625
|
+
| HasNameFilter
|
|
626
|
+
| WillpowerComparisonFilter
|
|
627
|
+
| MoveCostComparisonFilter
|
|
628
|
+
// Source/Reference
|
|
629
|
+
| SourceFilter
|
|
630
|
+
// Zone/Owner
|
|
631
|
+
| ZoneFilter
|
|
632
|
+
| OwnerFilter;
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Base properties shared by all location query variants
|
|
636
|
+
*/
|
|
637
|
+
export type LocationQueryBase = TargetDSL<LocationFilter[], LorcanaContext>;
|
|
638
|
+
|
|
639
|
+
/** Target exactly N locations */
|
|
640
|
+
export interface ExactCountLocationQuery extends LocationQueryBase {
|
|
641
|
+
count: number | { exactly: number };
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/** Target up to N locations */
|
|
645
|
+
export interface UpToCountLocationQuery extends LocationQueryBase {
|
|
646
|
+
count: { upTo: number };
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/** Target all matching locations */
|
|
650
|
+
export interface AllMatchingLocationQuery extends LocationQueryBase {
|
|
651
|
+
count: "all";
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Complex location targeting with filters
|
|
656
|
+
*/
|
|
657
|
+
export type LocationTargetQuery =
|
|
658
|
+
| ExactCountLocationQuery
|
|
659
|
+
| UpToCountLocationQuery
|
|
660
|
+
| AllMatchingLocationQuery;
|
|
661
|
+
|
|
662
|
+
export type LocationTarget =
|
|
663
|
+
| LocationTargetEnum
|
|
664
|
+
| LocationTargetQuery
|
|
665
|
+
| CardReference;
|
|
666
|
+
|
|
667
|
+
// ============================================================================
|
|
668
|
+
// Item Targeting
|
|
669
|
+
// ============================================================================
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* Common item targeting patterns
|
|
673
|
+
*/
|
|
674
|
+
export type ItemTargetEnum =
|
|
675
|
+
| "CHOSEN_ITEM"
|
|
676
|
+
| "CHOSEN_OPPOSING_ITEM"
|
|
677
|
+
| "YOUR_ITEMS"
|
|
678
|
+
| "YOUR_ITEM" // Singular - one of your items
|
|
679
|
+
| "ALL_ITEMS"
|
|
680
|
+
| "ALL_OPPOSING_ITEMS"
|
|
681
|
+
| "THIS_ITEM"; // For abilities on items
|
|
682
|
+
|
|
683
|
+
// ============================================================================
|
|
684
|
+
// Item Targeting - Strict Query Variants
|
|
685
|
+
// ============================================================================
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Filters applicable to items
|
|
689
|
+
* Uses shared filter types for consistency
|
|
690
|
+
*/
|
|
691
|
+
export type ItemFilter =
|
|
692
|
+
| HasNameFilter
|
|
693
|
+
| CostComparisonFilter
|
|
694
|
+
| ExertedFilter
|
|
695
|
+
| ReadyFilter
|
|
696
|
+
// Source/Reference
|
|
697
|
+
| SourceFilter
|
|
698
|
+
// Zone/Owner
|
|
699
|
+
| ZoneFilter
|
|
700
|
+
| OwnerFilter;
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Base properties shared by all item query variants
|
|
704
|
+
*/
|
|
705
|
+
export type ItemQueryBase = TargetDSL<ItemFilter[], LorcanaContext>;
|
|
706
|
+
|
|
707
|
+
/** Target exactly N items */
|
|
708
|
+
export interface ExactCountItemQuery extends ItemQueryBase {
|
|
709
|
+
count: number | { exactly: number };
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/** Target up to N items */
|
|
713
|
+
export interface UpToCountItemQuery extends ItemQueryBase {
|
|
714
|
+
count: { upTo: number };
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/** Target all matching items */
|
|
718
|
+
export interface AllMatchingItemQuery extends ItemQueryBase {
|
|
719
|
+
count: "all";
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Complex item targeting with filters
|
|
724
|
+
*/
|
|
725
|
+
export type ItemTargetQuery =
|
|
726
|
+
| ExactCountItemQuery
|
|
727
|
+
| UpToCountItemQuery
|
|
728
|
+
| AllMatchingItemQuery;
|
|
729
|
+
|
|
730
|
+
export type ItemTarget = ItemTargetEnum | ItemTargetQuery | CardReference;
|
|
731
|
+
|
|
732
|
+
// ============================================================================
|
|
733
|
+
// Card Targeting (any card type)
|
|
734
|
+
// ============================================================================
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* Common card targeting patterns (any type)
|
|
738
|
+
*/
|
|
739
|
+
export type CardTargetEnum =
|
|
740
|
+
| "CHOSEN_CARD"
|
|
741
|
+
| "CHOSEN_CARD_FROM_HAND"
|
|
742
|
+
| "CHOSEN_CARD_FROM_DISCARD"
|
|
743
|
+
| "TOP_CARD_OF_DECK"
|
|
744
|
+
| "revealed"
|
|
745
|
+
// Additional card targets for parser support
|
|
746
|
+
| "CHARACTER_FROM_DISCARD" // Character card from discard pile
|
|
747
|
+
| "SUPPORT_CHARACTER_FROM_DISCARD" // Support character from discard
|
|
748
|
+
| "CHOSEN_CHARACTER_OR_ITEM_COST_3_OR_LESS" // Character or item with cost 3 or less
|
|
749
|
+
// Extended card targets for card text coverage
|
|
750
|
+
| "CARD_FROM_ANY_DISCARD" // Card from any player's discard pile
|
|
751
|
+
| "ACTION_FROM_DISCARD" // Action card from discard pile
|
|
752
|
+
| "ITEM_FROM_DISCARD" // Item card from discard pile
|
|
753
|
+
| "CHARACTER_OR_ITEM" // Character or item card
|
|
754
|
+
| "BANISHED_CHARACTER"; // The character that was banished
|
|
755
|
+
|
|
756
|
+
export type CardTarget =
|
|
757
|
+
| CardTargetEnum
|
|
758
|
+
| CharacterTarget
|
|
759
|
+
| LocationTarget
|
|
760
|
+
| ItemTarget;
|
|
761
|
+
|
|
762
|
+
// ============================================================================
|
|
763
|
+
// Type Guards
|
|
764
|
+
// ============================================================================
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Check if a character target is a query (vs enum)
|
|
768
|
+
*/
|
|
769
|
+
export function isCharacterTargetQuery(
|
|
770
|
+
target: CharacterTarget,
|
|
771
|
+
): target is CharacterTargetQuery {
|
|
772
|
+
return typeof target === "object"; // && (target as any).selector !== undefined;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Check if a location target is a query (vs enum)
|
|
777
|
+
*/
|
|
778
|
+
export function isLocationTargetQuery(
|
|
779
|
+
target: LocationTarget,
|
|
780
|
+
): target is LocationTargetQuery {
|
|
781
|
+
return typeof target === "object"; // && (target as any).selector !== undefined;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Check if an item target is a query (vs enum)
|
|
786
|
+
*/
|
|
787
|
+
export function isItemTargetQuery(
|
|
788
|
+
target: ItemTarget,
|
|
789
|
+
): target is ItemTargetQuery {
|
|
790
|
+
return typeof target === "object"; // && (target as any).selector !== undefined;
|
|
791
|
+
}
|