@drmxrcy/tcg-lorcana 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/README.md +160 -0
- package/package.json +45 -0
- package/src/__tests__/integration/move-enumeration.test.ts +256 -0
- package/src/__tests__/rules/section-01-concepts.test.ts +426 -0
- package/src/__tests__/rules/section-03-gameplay.test.ts +298 -0
- package/src/__tests__/rules/section-04-turn-structure.test.ts +708 -0
- package/src/__tests__/rules/section-05-cards.test.ts +158 -0
- package/src/__tests__/rules/section-06-card-types.test.ts +342 -0
- package/src/__tests__/rules/section-07-abilities.test.ts +333 -0
- package/src/__tests__/rules/section-08-zones.test.ts +231 -0
- package/src/__tests__/rules/section-09-damage.test.ts +148 -0
- package/src/__tests__/rules/section-10-keywords.test.ts +469 -0
- package/src/__tests__/spec-01-foundation-types.test.ts +534 -0
- package/src/__tests__/spec-02-zones-card-states.test.ts +295 -0
- package/src/card-utils.ts +302 -0
- package/src/cards/README.md +296 -0
- package/src/cards/abilities/index.ts +175 -0
- package/src/cards/index.ts +10 -0
- package/src/deck-validation.ts +175 -0
- package/src/engine/lorcana-engine.ts +625 -0
- package/src/game-definition/__tests__/core-zone-integration.test.ts +553 -0
- package/src/game-definition/__tests__/zone-operations.test.ts +362 -0
- package/src/game-definition/__tests__/zones.test.ts +176 -0
- package/src/game-definition/definition.ts +45 -0
- package/src/game-definition/flow/turn-flow.ts +216 -0
- package/src/game-definition/index.ts +31 -0
- package/src/game-definition/moves/abilities/activate-ability.ts +51 -0
- package/src/game-definition/moves/core/__tests__/move-parameter-enumeration.test.ts +316 -0
- package/src/game-definition/moves/core/challenge.test.ts +545 -0
- package/src/game-definition/moves/core/challenge.ts +81 -0
- package/src/game-definition/moves/core/play-card.ts +83 -0
- package/src/game-definition/moves/core/quest.test.ts +448 -0
- package/src/game-definition/moves/core/quest.ts +49 -0
- package/src/game-definition/moves/debug/manual-exert.ts +36 -0
- package/src/game-definition/moves/effects/resolve-bag.ts +35 -0
- package/src/game-definition/moves/effects/resolve-effect.ts +34 -0
- package/src/game-definition/moves/index.ts +85 -0
- package/src/game-definition/moves/locations/move-character-to-location.ts +42 -0
- package/src/game-definition/moves/resources/put-card-into-inkwell.test.ts +462 -0
- package/src/game-definition/moves/resources/put-card-into-inkwell.ts +51 -0
- package/src/game-definition/moves/setup/alter-hand.test.ts +395 -0
- package/src/game-definition/moves/setup/alter-hand.ts +210 -0
- package/src/game-definition/moves/setup/choose-first-player.test.ts +450 -0
- package/src/game-definition/moves/setup/choose-first-player.ts +105 -0
- package/src/game-definition/moves/setup/draw-cards.ts +37 -0
- package/src/game-definition/moves/songs/sing-together.ts +47 -0
- package/src/game-definition/moves/songs/sing.ts +56 -0
- package/src/game-definition/moves/standard/concede.test.ts +189 -0
- package/src/game-definition/moves/standard/concede.ts +72 -0
- package/src/game-definition/moves/standard/pass-turn.ts +49 -0
- package/src/game-definition/setup/game-setup.ts +19 -0
- package/src/game-definition/trackers/tracker-config.ts +23 -0
- package/src/game-definition/win-conditions/lore-victory.ts +26 -0
- package/src/game-definition/zone-operations.ts +405 -0
- package/src/game-definition/zones/zone-configs.ts +59 -0
- package/src/game-definition/zones.ts +283 -0
- package/src/index.ts +189 -0
- package/src/operations/index.ts +7 -0
- package/src/operations/lorcana-operations.ts +288 -0
- package/src/queries/README.md +56 -0
- package/src/resolvers/__tests__/condition-resolver.test.ts +301 -0
- package/src/resolvers/condition-registry.ts +70 -0
- package/src/resolvers/condition-resolver.ts +85 -0
- package/src/resolvers/conditions/basic.ts +81 -0
- package/src/resolvers/conditions/card-state.ts +12 -0
- package/src/resolvers/conditions/comparison.ts +102 -0
- package/src/resolvers/conditions/existence.ts +219 -0
- package/src/resolvers/conditions/history.ts +68 -0
- package/src/resolvers/conditions/index.ts +15 -0
- package/src/resolvers/conditions/logical.ts +55 -0
- package/src/resolvers/conditions/resolution.ts +41 -0
- package/src/resolvers/conditions/revealed.ts +42 -0
- package/src/resolvers/conditions/zone.ts +84 -0
- package/src/setup.test.ts +18 -0
- package/src/targeting/__tests__/filter-resolver.test.ts +294 -0
- package/src/targeting/__tests__/real-cards-targeting.test.ts +303 -0
- package/src/targeting/__tests__/targeting-dsl.test.ts +386 -0
- package/src/targeting/enum-expansion.ts +387 -0
- package/src/targeting/filter-registry.ts +322 -0
- package/src/targeting/filter-resolver.ts +145 -0
- package/src/targeting/index.ts +91 -0
- package/src/targeting/lorcana-target-dsl.ts +495 -0
- package/src/targeting/targeting-ui.ts +407 -0
- package/src/testing/index.ts +14 -0
- package/src/testing/lorcana-test-engine.ts +813 -0
- package/src/types/README.md +303 -0
- package/src/types/__tests__/lorcana-state.test.ts +168 -0
- package/src/types/__tests__/move-enumeration.test.ts +179 -0
- package/src/types/branded-types.ts +106 -0
- package/src/types/game-state.ts +184 -0
- package/src/types/index.ts +87 -0
- package/src/types/keywords.ts +187 -0
- package/src/types/lorcana-state.ts +260 -0
- package/src/types/move-enumeration.ts +126 -0
- package/src/types/move-params.ts +216 -0
- package/src/validators/index.ts +7 -0
- package/src/validators/move-validators.ts +374 -0
- package/src/zones/card-state.ts +234 -0
- package/src/zones/index.ts +42 -0
- package/src/zones/zone-config.ts +150 -0
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
import type { BaseContext, PlayerTargetDSL, TargetDSL } from "@drmxrcy/tcg-core";
|
|
2
|
+
import type { KeywordType } from "../types/keywords";
|
|
3
|
+
|
|
4
|
+
/** Lorcana zone IDs */
|
|
5
|
+
export type LorcanaZoneId = "deck" | "hand" | "play" | "discard" | "inkwell";
|
|
6
|
+
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Lorcana-Specific Filters
|
|
9
|
+
// ============================================================================
|
|
10
|
+
|
|
11
|
+
// --- State Filters ---
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Filter for damaged cards (have damage counters)
|
|
15
|
+
*/
|
|
16
|
+
export interface DamagedFilter {
|
|
17
|
+
type: "damaged";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Filter for undamaged cards (no damage counters)
|
|
22
|
+
*/
|
|
23
|
+
export interface UndamagedFilter {
|
|
24
|
+
type: "undamaged";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Filter for exerted cards
|
|
29
|
+
*/
|
|
30
|
+
export interface ExertedFilter {
|
|
31
|
+
type: "exerted";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Filter for ready (non-exerted) cards
|
|
36
|
+
*/
|
|
37
|
+
export interface ReadyFilter {
|
|
38
|
+
type: "ready";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Filter for dry cards (freshly played, can't be used)
|
|
43
|
+
*/
|
|
44
|
+
export interface DryFilter {
|
|
45
|
+
type: "dry";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// --- Property Filters ---
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Filter for cards with a specific keyword
|
|
52
|
+
*/
|
|
53
|
+
export interface HasKeywordFilter {
|
|
54
|
+
type: "has-keyword";
|
|
55
|
+
keyword: KeywordType;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Filter for cards with a specific classification (Hero, Villain, etc.)
|
|
60
|
+
*/
|
|
61
|
+
export interface HasClassificationFilter {
|
|
62
|
+
type: "has-classification";
|
|
63
|
+
classification: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Filter for inkable/non-inkable cards
|
|
68
|
+
*/
|
|
69
|
+
export interface InkableFilter {
|
|
70
|
+
type: "inkable";
|
|
71
|
+
value: boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// --- Numeric Comparison Filters ---
|
|
75
|
+
|
|
76
|
+
export type ComparisonOperator = "eq" | "ne" | "gt" | "gte" | "lt" | "lte";
|
|
77
|
+
|
|
78
|
+
export interface NumericComparison {
|
|
79
|
+
operator: ComparisonOperator;
|
|
80
|
+
value: number;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface RelativeComparison {
|
|
84
|
+
operator: ComparisonOperator;
|
|
85
|
+
value: "target"; // Compares against the target of the ability (parent context)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Filter by strength value
|
|
90
|
+
*/
|
|
91
|
+
export type StrengthFilter =
|
|
92
|
+
| {
|
|
93
|
+
type: "strength";
|
|
94
|
+
comparison: ComparisonOperator;
|
|
95
|
+
value: number;
|
|
96
|
+
ignoreBonuses?: boolean;
|
|
97
|
+
compareWithParentsTarget?: never;
|
|
98
|
+
}
|
|
99
|
+
| {
|
|
100
|
+
type: "strength";
|
|
101
|
+
comparison: ComparisonOperator;
|
|
102
|
+
value: "target";
|
|
103
|
+
ignoreBonuses?: boolean;
|
|
104
|
+
compareWithParentsTarget: true;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Filter by willpower value
|
|
109
|
+
*/
|
|
110
|
+
export type WillpowerFilter =
|
|
111
|
+
| {
|
|
112
|
+
type: "willpower";
|
|
113
|
+
comparison: ComparisonOperator;
|
|
114
|
+
value: number;
|
|
115
|
+
compareWithParentsTarget?: never;
|
|
116
|
+
}
|
|
117
|
+
| {
|
|
118
|
+
type: "willpower";
|
|
119
|
+
comparison: ComparisonOperator;
|
|
120
|
+
value: "target";
|
|
121
|
+
compareWithParentsTarget: true;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Filter by ink cost
|
|
126
|
+
*/
|
|
127
|
+
export type CostFilter =
|
|
128
|
+
| {
|
|
129
|
+
type: "cost";
|
|
130
|
+
comparison: ComparisonOperator;
|
|
131
|
+
value: number;
|
|
132
|
+
compareWithParentsTarget?: never;
|
|
133
|
+
}
|
|
134
|
+
| {
|
|
135
|
+
type: "cost";
|
|
136
|
+
comparison: ComparisonOperator;
|
|
137
|
+
value: "target";
|
|
138
|
+
compareWithParentsTarget: true;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Filter by lore value
|
|
143
|
+
*/
|
|
144
|
+
export type LoreValueFilter =
|
|
145
|
+
| {
|
|
146
|
+
type: "lore-value";
|
|
147
|
+
comparison: ComparisonOperator;
|
|
148
|
+
value: number;
|
|
149
|
+
compareWithParentsTarget?: never;
|
|
150
|
+
}
|
|
151
|
+
| {
|
|
152
|
+
type: "lore-value";
|
|
153
|
+
comparison: ComparisonOperator;
|
|
154
|
+
value: "target";
|
|
155
|
+
compareWithParentsTarget: true;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// --- Location Filters ---
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Filter for characters at a location
|
|
162
|
+
*/
|
|
163
|
+
export interface AtLocationFilter {
|
|
164
|
+
type: "at-location";
|
|
165
|
+
/** Specific location name, or undefined for any location */
|
|
166
|
+
location?: string;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Filter by move cost (for locations)
|
|
171
|
+
*/
|
|
172
|
+
export interface MoveCostFilter {
|
|
173
|
+
type: "move-cost";
|
|
174
|
+
comparison: ComparisonOperator;
|
|
175
|
+
value: number;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// --- Name Filters ---
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Filter by card name
|
|
182
|
+
*/
|
|
183
|
+
export type NameFilter =
|
|
184
|
+
| { type: "name"; equals: string }
|
|
185
|
+
| { type: "name"; contains: string };
|
|
186
|
+
|
|
187
|
+
// --- Card Type Filters ---
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Filter by card type (character, item, location, action)
|
|
191
|
+
*/
|
|
192
|
+
export interface CardTypeFilter {
|
|
193
|
+
type: "card-type";
|
|
194
|
+
value: LorcanaCardType;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// --- Combined Lorcana Filter Type ---
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* All Lorcana-specific filters
|
|
201
|
+
*/
|
|
202
|
+
export type LorcanaFilter =
|
|
203
|
+
// State filters
|
|
204
|
+
| DamagedFilter
|
|
205
|
+
| UndamagedFilter
|
|
206
|
+
| ExertedFilter
|
|
207
|
+
| ReadyFilter
|
|
208
|
+
| DryFilter
|
|
209
|
+
// Property filters
|
|
210
|
+
| HasKeywordFilter
|
|
211
|
+
| HasClassificationFilter
|
|
212
|
+
| InkableFilter
|
|
213
|
+
// Numeric filters
|
|
214
|
+
| StrengthFilter
|
|
215
|
+
| WillpowerFilter
|
|
216
|
+
| CostFilter
|
|
217
|
+
| LoreValueFilter
|
|
218
|
+
// Location filters
|
|
219
|
+
| AtLocationFilter
|
|
220
|
+
| MoveCostFilter
|
|
221
|
+
// Name filter
|
|
222
|
+
| NameFilter
|
|
223
|
+
// Card Type filter
|
|
224
|
+
| CardTypeFilter
|
|
225
|
+
// Composite filters
|
|
226
|
+
| { type: "and"; filters: LorcanaFilter[] }
|
|
227
|
+
| { type: "or"; filters: LorcanaFilter[] }
|
|
228
|
+
| { type: "not"; filter: LorcanaFilter };
|
|
229
|
+
|
|
230
|
+
// ============================================================================
|
|
231
|
+
// Lorcana Context References
|
|
232
|
+
// ============================================================================
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Context references for Lorcana abilities
|
|
236
|
+
*
|
|
237
|
+
* These allow effects to reference cards based on the current context
|
|
238
|
+
* rather than requiring explicit targeting.
|
|
239
|
+
*/
|
|
240
|
+
export interface LorcanaContext extends BaseContext {
|
|
241
|
+
/** Reference the source card itself */
|
|
242
|
+
self?: boolean;
|
|
243
|
+
|
|
244
|
+
/** Reference the card that triggered this ability */
|
|
245
|
+
triggerSource?: boolean;
|
|
246
|
+
|
|
247
|
+
/** Reference the attacker in a challenge */
|
|
248
|
+
attacker?: boolean;
|
|
249
|
+
|
|
250
|
+
/** Reference the defender in a challenge */
|
|
251
|
+
defender?: boolean;
|
|
252
|
+
|
|
253
|
+
/** Reference the previously selected target in an effect chain */
|
|
254
|
+
previousTarget?: boolean;
|
|
255
|
+
|
|
256
|
+
/** Reference the singer in a song */
|
|
257
|
+
singer?: boolean;
|
|
258
|
+
|
|
259
|
+
/** Reference the song being sung */
|
|
260
|
+
song?: boolean;
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Resolution context for resolution conditions
|
|
264
|
+
* Used for checking if we are currently resolving a specific mechanic (e.g. Bodyguard)
|
|
265
|
+
*/
|
|
266
|
+
resolutionContext?: "bodyguard" | "shift" | string;
|
|
267
|
+
|
|
268
|
+
/** Reference cards revealed by an effect (e.g. "Look at the top card of your deck") */
|
|
269
|
+
revealedCards?: string[];
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Recursion depth tracking for condition evaluation
|
|
273
|
+
* Prevents infinite loops in recursive condition checks
|
|
274
|
+
* @internal
|
|
275
|
+
*/
|
|
276
|
+
recursionDepth?: number;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ============================================================================
|
|
280
|
+
// Lorcana Card Types
|
|
281
|
+
// ============================================================================
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Lorcana card types for targeting
|
|
285
|
+
*/
|
|
286
|
+
export type LorcanaCardType = "character" | "item" | "location" | "action";
|
|
287
|
+
|
|
288
|
+
// ============================================================================
|
|
289
|
+
// Lorcana Target DSL
|
|
290
|
+
// ============================================================================
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Lorcana card target - extends core DSL with Lorcana-specific features
|
|
294
|
+
*
|
|
295
|
+
* @example Target a chosen opposing damaged character
|
|
296
|
+
* ```typescript
|
|
297
|
+
* const target: LorcanaCardTarget = {
|
|
298
|
+
* selector: "chosen",
|
|
299
|
+
* count: 1,
|
|
300
|
+
* owner: "opponent",
|
|
301
|
+
* cardType: "character",
|
|
302
|
+
* zones: ["play"],
|
|
303
|
+
* filters: [{ type: "damaged" }]
|
|
304
|
+
* };
|
|
305
|
+
* ```
|
|
306
|
+
*
|
|
307
|
+
* @example Target all your characters with Evasive
|
|
308
|
+
* ```typescript
|
|
309
|
+
* const target: LorcanaCardTarget = {
|
|
310
|
+
* selector: "all",
|
|
311
|
+
* owner: "you",
|
|
312
|
+
* cardType: "character",
|
|
313
|
+
* zones: ["play"],
|
|
314
|
+
* filters: [{ type: "has-keyword", keyword: "Evasive" }]
|
|
315
|
+
* };
|
|
316
|
+
* ```
|
|
317
|
+
*/
|
|
318
|
+
export interface LorcanaCardTarget
|
|
319
|
+
extends TargetDSL<LorcanaFilter, LorcanaContext> {
|
|
320
|
+
/** Lorcana card type constraint */
|
|
321
|
+
cardType?: LorcanaCardType;
|
|
322
|
+
|
|
323
|
+
/** Override zones with Lorcana-specific zone IDs */
|
|
324
|
+
zones?: LorcanaZoneId[];
|
|
325
|
+
|
|
326
|
+
/** Lorcana-specific filters */
|
|
327
|
+
filters?: LorcanaFilter[];
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ============================================================================
|
|
331
|
+
// Convenience Type Aliases
|
|
332
|
+
// ============================================================================
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Character target (card type constrained)
|
|
336
|
+
*/
|
|
337
|
+
export type CharacterTarget = LorcanaCardTarget & { cardType: "character" };
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Item target (card type constrained)
|
|
341
|
+
*/
|
|
342
|
+
export type ItemTarget = LorcanaCardTarget & { cardType: "item" };
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Location target (card type constrained)
|
|
346
|
+
*/
|
|
347
|
+
export type LocationTarget = LorcanaCardTarget & { cardType: "location" };
|
|
348
|
+
|
|
349
|
+
// ============================================================================
|
|
350
|
+
// Player Targeting (re-export with Lorcana context)
|
|
351
|
+
// ============================================================================
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Lorcana player target
|
|
355
|
+
*
|
|
356
|
+
* Uses the core PlayerTargetDSL with Lorcana terminology
|
|
357
|
+
*/
|
|
358
|
+
export type LorcanaPlayerTarget = PlayerTargetDSL;
|
|
359
|
+
|
|
360
|
+
// ============================================================================
|
|
361
|
+
// Type Guards
|
|
362
|
+
// ============================================================================
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Check if a target is a DSL object (vs enum string)
|
|
366
|
+
*/
|
|
367
|
+
export function isDSLTarget(
|
|
368
|
+
target: LorcanaTarget,
|
|
369
|
+
): target is LorcanaCardTarget {
|
|
370
|
+
return typeof target === "object" && target !== null;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Check if a filter is a state filter
|
|
375
|
+
*/
|
|
376
|
+
export function isStateFilter(
|
|
377
|
+
filter: LorcanaFilter,
|
|
378
|
+
): filter is
|
|
379
|
+
| DamagedFilter
|
|
380
|
+
| UndamagedFilter
|
|
381
|
+
| ExertedFilter
|
|
382
|
+
| ReadyFilter
|
|
383
|
+
| DryFilter {
|
|
384
|
+
return (
|
|
385
|
+
filter.type === "damaged" ||
|
|
386
|
+
filter.type === "undamaged" ||
|
|
387
|
+
filter.type === "exerted" ||
|
|
388
|
+
filter.type === "ready" ||
|
|
389
|
+
filter.type === "dry"
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Check if a filter is a numeric comparison filter
|
|
395
|
+
*/
|
|
396
|
+
export function isNumericFilter(
|
|
397
|
+
filter: LorcanaFilter,
|
|
398
|
+
): filter is
|
|
399
|
+
| StrengthFilter
|
|
400
|
+
| WillpowerFilter
|
|
401
|
+
| CostFilter
|
|
402
|
+
| LoreValueFilter
|
|
403
|
+
| MoveCostFilter {
|
|
404
|
+
return (
|
|
405
|
+
filter.type === "strength" ||
|
|
406
|
+
filter.type === "willpower" ||
|
|
407
|
+
filter.type === "cost" ||
|
|
408
|
+
filter.type === "lore-value" ||
|
|
409
|
+
filter.type === "move-cost"
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// ============================================================================
|
|
414
|
+
// Enum Shortcuts (defined here, expanded in enum-expansion.ts)
|
|
415
|
+
// ============================================================================
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Character target enum shortcuts
|
|
419
|
+
*
|
|
420
|
+
* These provide syntactic sugar for common targeting patterns.
|
|
421
|
+
* Use these for simple cases, use LorcanaCardTarget for complex cases.
|
|
422
|
+
*/
|
|
423
|
+
export type CharacterTargetEnum =
|
|
424
|
+
// Self-referential
|
|
425
|
+
| "SELF"
|
|
426
|
+
| "THIS_CHARACTER"
|
|
427
|
+
|
|
428
|
+
// Chosen (requires player choice)
|
|
429
|
+
| "CHOSEN_CHARACTER"
|
|
430
|
+
| "CHOSEN_OPPOSING_CHARACTER"
|
|
431
|
+
| "CHOSEN_CHARACTER_OF_YOURS"
|
|
432
|
+
| "ANOTHER_CHOSEN_CHARACTER"
|
|
433
|
+
| "ANOTHER_CHOSEN_CHARACTER_OF_YOURS"
|
|
434
|
+
|
|
435
|
+
// All/Each (affects multiple)
|
|
436
|
+
| "ALL_CHARACTERS"
|
|
437
|
+
| "ALL_OPPOSING_CHARACTERS"
|
|
438
|
+
| "YOUR_CHARACTERS"
|
|
439
|
+
| "YOUR_OTHER_CHARACTERS"
|
|
440
|
+
| "EACH_CHARACTER"
|
|
441
|
+
| "EACH_OPPOSING_CHARACTER"
|
|
442
|
+
|
|
443
|
+
// Damaged variants
|
|
444
|
+
| "CHOSEN_DAMAGED_CHARACTER"
|
|
445
|
+
| "CHOSEN_OPPOSING_DAMAGED_CHARACTER"
|
|
446
|
+
| "ALL_OPPOSING_DAMAGED_CHARACTERS";
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Item target enum shortcuts
|
|
450
|
+
*/
|
|
451
|
+
export type ItemTargetEnum =
|
|
452
|
+
| "CHOSEN_ITEM"
|
|
453
|
+
| "CHOSEN_OPPOSING_ITEM"
|
|
454
|
+
| "YOUR_ITEMS"
|
|
455
|
+
| "ALL_ITEMS"
|
|
456
|
+
| "ALL_OPPOSING_ITEMS"
|
|
457
|
+
| "THIS_ITEM";
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Location target enum shortcuts
|
|
461
|
+
*/
|
|
462
|
+
export type LocationTargetEnum =
|
|
463
|
+
| "CHOSEN_LOCATION"
|
|
464
|
+
| "CHOSEN_OPPOSING_LOCATION"
|
|
465
|
+
| "YOUR_LOCATIONS"
|
|
466
|
+
| "ALL_OPPOSING_LOCATIONS"
|
|
467
|
+
| "THIS_LOCATION";
|
|
468
|
+
|
|
469
|
+
// ============================================================================
|
|
470
|
+
// Union Types (enum OR DSL)
|
|
471
|
+
// ============================================================================
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Character target: either an enum shortcut or full DSL
|
|
475
|
+
*/
|
|
476
|
+
export type LorcanaCharacterTarget = CharacterTargetEnum | CharacterTarget;
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Item target: either an enum shortcut or full DSL
|
|
480
|
+
*/
|
|
481
|
+
export type LorcanaItemTarget = ItemTargetEnum | ItemTarget;
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Location target: either an enum shortcut or full DSL
|
|
485
|
+
*/
|
|
486
|
+
export type LorcanaLocationTarget = LocationTargetEnum | LocationTarget;
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Any card target
|
|
490
|
+
*/
|
|
491
|
+
export type LorcanaTarget =
|
|
492
|
+
| LorcanaCharacterTarget
|
|
493
|
+
| LorcanaItemTarget
|
|
494
|
+
| LorcanaLocationTarget
|
|
495
|
+
| LorcanaCardTarget;
|