@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,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lorcana Game State Types
|
|
3
|
+
*
|
|
4
|
+
* Task 1.2: Define LorcanaState type extending base game state
|
|
5
|
+
*
|
|
6
|
+
* Complete type definition for Disney Lorcana game state following official rules:
|
|
7
|
+
* - Lore tracking for win condition (Rule 1.9.1.1 - win at 20 lore)
|
|
8
|
+
* - Ink management (total capacity and available per turn)
|
|
9
|
+
* - Character states (drying status, damage, exerted)
|
|
10
|
+
* - Turn metadata (cards played, characters questing, ink used)
|
|
11
|
+
* - Challenge state for combat resolution
|
|
12
|
+
* - Location and item states
|
|
13
|
+
*
|
|
14
|
+
* References:
|
|
15
|
+
* - Rule 3.1.4 (Lore starts at 0)
|
|
16
|
+
* - Rule 4.2.2.1 (Drying characters)
|
|
17
|
+
* - Rule 4.3.3 (Inkwell - once per turn)
|
|
18
|
+
* - Rule 4.3.6 (Challenge mechanics)
|
|
19
|
+
* - Rule 9 (Damage counters)
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type { CardId, PlayerId } from "./branded-types";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Lorcana Phase
|
|
26
|
+
*
|
|
27
|
+
* Three-phase turn structure (Rule 4.1.1):
|
|
28
|
+
* - beginning: Ready, Set, Draw steps
|
|
29
|
+
* - main: Player can take turn actions
|
|
30
|
+
* - end: End of turn cleanup
|
|
31
|
+
*/
|
|
32
|
+
export type LorcanaPhase = "beginning" | "main" | "end";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Character State
|
|
36
|
+
*
|
|
37
|
+
* Runtime state for a character card in play.
|
|
38
|
+
* Tracks damage, exerted status, and "drying" state.
|
|
39
|
+
*
|
|
40
|
+
* Rule 4.2.2.1: Characters are "drying" the turn they're played
|
|
41
|
+
* Rule 6.1.4: Must be dry to quest, challenge, or exert
|
|
42
|
+
* Rule 9: Damage represented by counters
|
|
43
|
+
*/
|
|
44
|
+
export type CharacterState = {
|
|
45
|
+
/**
|
|
46
|
+
* "Drying" status - true if played this turn
|
|
47
|
+
*
|
|
48
|
+
* Characters that are "drying" cannot:
|
|
49
|
+
* - Quest (Rule 4.3.5)
|
|
50
|
+
* - Challenge (Rule 4.3.6.6)
|
|
51
|
+
* - Be exerted to pay costs (Rule 6.1.4)
|
|
52
|
+
*
|
|
53
|
+
* Becomes false at Set step of next turn (Rule 4.2.2.1)
|
|
54
|
+
*/
|
|
55
|
+
playedThisTurn: boolean;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Damage counters on this character
|
|
59
|
+
*
|
|
60
|
+
* Rule 9.1: Each counter represents 1 damage
|
|
61
|
+
* Rule 1.9.1.3: Banished when damage >= Willpower
|
|
62
|
+
*/
|
|
63
|
+
damage: number;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Exerted status - true if turned sideways
|
|
67
|
+
*
|
|
68
|
+
* Rule 5.1.2: Exerted cards turned sideways
|
|
69
|
+
* Rule 4.2.1.1: Readied at start of turn
|
|
70
|
+
*/
|
|
71
|
+
exerted: boolean;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Permanent State
|
|
76
|
+
*
|
|
77
|
+
* Runtime state for locations and items in play.
|
|
78
|
+
* Currently only tracks damage (for locations).
|
|
79
|
+
*/
|
|
80
|
+
export type PermanentState = {
|
|
81
|
+
/**
|
|
82
|
+
* Damage counters (for locations)
|
|
83
|
+
*
|
|
84
|
+
* Rule 4.3.6.19-22: Locations can be challenged
|
|
85
|
+
* Rule 6.5: Locations have Willpower
|
|
86
|
+
*/
|
|
87
|
+
damage: number;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Challenge State
|
|
92
|
+
*
|
|
93
|
+
* Temporary state during challenge resolution.
|
|
94
|
+
*
|
|
95
|
+
* Rule 4.3.6: Challenge mechanics
|
|
96
|
+
* - Attacker declared and exerted
|
|
97
|
+
* - Defender chosen
|
|
98
|
+
* - Damage calculated and dealt
|
|
99
|
+
* - Challenge ends
|
|
100
|
+
*/
|
|
101
|
+
export type ChallengeState = {
|
|
102
|
+
/**
|
|
103
|
+
* Attacking character
|
|
104
|
+
*/
|
|
105
|
+
attacker: CardId;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Defending character or location
|
|
109
|
+
*/
|
|
110
|
+
defender: CardId;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Calculated damage attacker will deal
|
|
114
|
+
*
|
|
115
|
+
* Rule 4.3.6.14: Based on Strength with modifiers
|
|
116
|
+
* Rule 10.3: Challenger +N applies
|
|
117
|
+
*/
|
|
118
|
+
attackerDamage: number;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Calculated damage defender will deal
|
|
122
|
+
*
|
|
123
|
+
* Rule 4.3.6.14: Based on Strength with modifiers
|
|
124
|
+
* Rule 4.3.6.22: Locations deal no damage
|
|
125
|
+
*/
|
|
126
|
+
defenderDamage: number;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Turn Metadata
|
|
131
|
+
*
|
|
132
|
+
* Tracks actions taken this turn for validation and cleanup.
|
|
133
|
+
* Reset at start of each turn.
|
|
134
|
+
*/
|
|
135
|
+
export type TurnMetadata = {
|
|
136
|
+
/**
|
|
137
|
+
* Cards played this turn
|
|
138
|
+
*
|
|
139
|
+
* Tracked for effects that reference "cards played this turn"
|
|
140
|
+
*/
|
|
141
|
+
cardsPlayedThisTurn: CardId[];
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Characters that quested this turn
|
|
145
|
+
*
|
|
146
|
+
* Rule 4.3.5: Each character can quest once per turn
|
|
147
|
+
* Tracked for effects that reference "whenever quests"
|
|
148
|
+
*/
|
|
149
|
+
charactersQuesting: CardId[];
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Whether player has put a card into inkwell this turn
|
|
153
|
+
*
|
|
154
|
+
* Rule 4.3.3: Limited to once per turn
|
|
155
|
+
*/
|
|
156
|
+
inkedThisTurn: boolean;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Lorcana-specific Game State
|
|
161
|
+
*
|
|
162
|
+
* Complete game state for Disney Lorcana extending base game state structure.
|
|
163
|
+
* All Lorcana-specific data nested under `lorcana` property.
|
|
164
|
+
*/
|
|
165
|
+
export type LorcanaState = {
|
|
166
|
+
/**
|
|
167
|
+
* Players in the game
|
|
168
|
+
*
|
|
169
|
+
* Rule 2.1: Standard format is 2 players
|
|
170
|
+
*/
|
|
171
|
+
players: PlayerId[];
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Current player index (into players array)
|
|
175
|
+
*
|
|
176
|
+
* Rule 1.3: Active player takes their turn
|
|
177
|
+
*/
|
|
178
|
+
currentPlayerIndex: number;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Turn number (starts at 1)
|
|
182
|
+
*
|
|
183
|
+
* Rule 3.1: First turn determined randomly
|
|
184
|
+
* Rule 4.2.3.2: Starting player skips draw on turn 1
|
|
185
|
+
*/
|
|
186
|
+
turnNumber: number;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Current phase
|
|
190
|
+
*
|
|
191
|
+
* Rule 4.1: Three phases - Beginning, Main, End
|
|
192
|
+
*/
|
|
193
|
+
phase: LorcanaPhase;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Lorcana-specific game state
|
|
197
|
+
*/
|
|
198
|
+
lorcana: {
|
|
199
|
+
/**
|
|
200
|
+
* Lore totals for each player
|
|
201
|
+
*
|
|
202
|
+
* Rule 1.9.1.1: Win condition - first to 20 lore
|
|
203
|
+
* Rule 3.1.4: Starts at 0
|
|
204
|
+
* Rule 4.2.2.2: Gained from locations during Set step
|
|
205
|
+
* Rule 4.3.5.8: Gained from questing
|
|
206
|
+
*/
|
|
207
|
+
lore: Record<PlayerId, number>;
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Ink management
|
|
211
|
+
*
|
|
212
|
+
* Rule 4.3.3: Put card into inkwell once per turn
|
|
213
|
+
* Rule 8.5.1: Each ink card represents 1 ink
|
|
214
|
+
*/
|
|
215
|
+
ink: {
|
|
216
|
+
/**
|
|
217
|
+
* Available ink this turn (can be spent)
|
|
218
|
+
*
|
|
219
|
+
* Replenished when cards readied at start of turn
|
|
220
|
+
*/
|
|
221
|
+
available: Record<PlayerId, number>;
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Total ink capacity (total cards in inkwell)
|
|
225
|
+
*
|
|
226
|
+
* Never decreases, only increases when inking
|
|
227
|
+
*/
|
|
228
|
+
total: Record<PlayerId, number>;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Turn metadata - reset each turn
|
|
233
|
+
*
|
|
234
|
+
* Tracks what actions player has taken this turn
|
|
235
|
+
*/
|
|
236
|
+
turnMetadata: TurnMetadata;
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Character states
|
|
240
|
+
*
|
|
241
|
+
* Keyed by CardId, tracks runtime state of each character in play
|
|
242
|
+
*/
|
|
243
|
+
characterStates: Record<CardId, CharacterState>;
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Permanent states (locations and items)
|
|
247
|
+
*
|
|
248
|
+
* Keyed by CardId, tracks runtime state of non-character permanents
|
|
249
|
+
*/
|
|
250
|
+
permanentStates: Record<CardId, PermanentState>;
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Challenge state (only present during challenge)
|
|
254
|
+
*
|
|
255
|
+
* Rule 4.3.6: Challenge mechanics
|
|
256
|
+
* Optional - only set during challenge resolution
|
|
257
|
+
*/
|
|
258
|
+
challengeState?: ChallengeState;
|
|
259
|
+
};
|
|
260
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Move Enumeration Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the move enumeration system that allows AI agents
|
|
5
|
+
* and UI components to discover available moves and their parameters.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Parameter field schema
|
|
10
|
+
*
|
|
11
|
+
* Describes a single parameter field for a move, including its type,
|
|
12
|
+
* description, and valid values.
|
|
13
|
+
*/
|
|
14
|
+
export type ParamFieldSchema = {
|
|
15
|
+
/** Parameter name */
|
|
16
|
+
name: string;
|
|
17
|
+
|
|
18
|
+
/** Parameter type */
|
|
19
|
+
type: "cardId" | "playerId" | "number" | "boolean" | "object" | "string";
|
|
20
|
+
|
|
21
|
+
/** Human-readable description */
|
|
22
|
+
description: string;
|
|
23
|
+
|
|
24
|
+
/** For cardId/playerId type: valid IDs that can be used */
|
|
25
|
+
validValues?: string[];
|
|
26
|
+
|
|
27
|
+
/** For enum types: valid enum values */
|
|
28
|
+
enumValues?: string[];
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Move parameter schema
|
|
33
|
+
*
|
|
34
|
+
* Defines the structure of parameters a move accepts, including
|
|
35
|
+
* required and optional fields.
|
|
36
|
+
*/
|
|
37
|
+
export type MoveParamSchema = {
|
|
38
|
+
/** Required parameters */
|
|
39
|
+
required: ParamFieldSchema[];
|
|
40
|
+
|
|
41
|
+
/** Optional parameters */
|
|
42
|
+
optional?: ParamFieldSchema[];
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Available move information
|
|
47
|
+
*
|
|
48
|
+
* Metadata about a move that is available to execute, including
|
|
49
|
+
* display information and parameter schema.
|
|
50
|
+
*/
|
|
51
|
+
export type AvailableMoveInfo = {
|
|
52
|
+
/** Unique move identifier */
|
|
53
|
+
moveId: string;
|
|
54
|
+
|
|
55
|
+
/** Human-readable display name */
|
|
56
|
+
displayName: string;
|
|
57
|
+
|
|
58
|
+
/** Brief description of what the move does */
|
|
59
|
+
description: string;
|
|
60
|
+
|
|
61
|
+
/** Icon hint for UI (optional) */
|
|
62
|
+
icon?: string;
|
|
63
|
+
|
|
64
|
+
/** Parameter schema (undefined if move has no parameters) */
|
|
65
|
+
paramSchema?: MoveParamSchema;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Parameter information
|
|
70
|
+
*
|
|
71
|
+
* Detailed information about a specific parameter, including
|
|
72
|
+
* type, description, and constraints.
|
|
73
|
+
*/
|
|
74
|
+
export type ParameterInfo = {
|
|
75
|
+
/** Parameter type */
|
|
76
|
+
type: "cardId" | "playerId" | "number" | "boolean" | "object" | "string";
|
|
77
|
+
|
|
78
|
+
/** Human-readable description */
|
|
79
|
+
description: string;
|
|
80
|
+
|
|
81
|
+
/** Valid values for this parameter */
|
|
82
|
+
validValues?: any[];
|
|
83
|
+
|
|
84
|
+
/** Minimum value (for number types) */
|
|
85
|
+
min?: number;
|
|
86
|
+
|
|
87
|
+
/** Maximum value (for number types) */
|
|
88
|
+
max?: number;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Move parameter options
|
|
93
|
+
*
|
|
94
|
+
* All valid parameter combinations for a specific move, along with
|
|
95
|
+
* metadata about each parameter field.
|
|
96
|
+
*/
|
|
97
|
+
export type MoveParameterOptions = {
|
|
98
|
+
/** All valid parameter combinations for this move */
|
|
99
|
+
validCombinations: Record<string, any>[];
|
|
100
|
+
|
|
101
|
+
/** Information about each parameter */
|
|
102
|
+
parameterInfo: Record<string, ParameterInfo>;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Move validation error
|
|
107
|
+
*
|
|
108
|
+
* Detailed information about why a move cannot be executed,
|
|
109
|
+
* including error code, reason, context, and suggestions.
|
|
110
|
+
*/
|
|
111
|
+
export type MoveValidationError = {
|
|
112
|
+
/** Move that was attempted */
|
|
113
|
+
moveId: string;
|
|
114
|
+
|
|
115
|
+
/** Error code for programmatic handling */
|
|
116
|
+
errorCode: string;
|
|
117
|
+
|
|
118
|
+
/** Human-readable reason */
|
|
119
|
+
reason: string;
|
|
120
|
+
|
|
121
|
+
/** Additional error context */
|
|
122
|
+
context?: Record<string, any>;
|
|
123
|
+
|
|
124
|
+
/** Suggested actions to make the move valid */
|
|
125
|
+
suggestions?: string[];
|
|
126
|
+
};
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lorcana Move Parameter Types
|
|
3
|
+
*
|
|
4
|
+
* Type-safe move parameters using discriminated unions for better type safety.
|
|
5
|
+
* Each move has explicit parameter types that leverage TypeScript's type narrowing.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { CardId, PlayerId } from "./branded-types";
|
|
9
|
+
import type { LorcanaCardMeta } from "./game-state";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Play Card Cost Types - Discriminated Union
|
|
13
|
+
*
|
|
14
|
+
* Uses discriminated unions for type-safe alternative costs.
|
|
15
|
+
* TypeScript can narrow types based on the `cost` discriminator.
|
|
16
|
+
*/
|
|
17
|
+
export type PlayCardCost =
|
|
18
|
+
| {
|
|
19
|
+
/** Standard ink cost payment */
|
|
20
|
+
cost: "standard";
|
|
21
|
+
}
|
|
22
|
+
| {
|
|
23
|
+
/** Shift cost - play over another character */
|
|
24
|
+
cost: "shift";
|
|
25
|
+
/** Character to shift onto (must share name) */
|
|
26
|
+
shiftTarget: CardId;
|
|
27
|
+
}
|
|
28
|
+
| {
|
|
29
|
+
/** Sing cost - exert a character to play a song */
|
|
30
|
+
cost: "sing";
|
|
31
|
+
/** Character exerting to sing the song */
|
|
32
|
+
singer: CardId;
|
|
33
|
+
}
|
|
34
|
+
| {
|
|
35
|
+
/** Sing Together cost - exert multiple characters */
|
|
36
|
+
cost: "singTogether";
|
|
37
|
+
/** Characters exerting to sing together */
|
|
38
|
+
singers: CardId[];
|
|
39
|
+
}
|
|
40
|
+
| {
|
|
41
|
+
/** Free play (for effects that play cards) */
|
|
42
|
+
cost: "free";
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Lorcana Move Parameters
|
|
47
|
+
*
|
|
48
|
+
* Exhaustive type definition for all Lorcana moves.
|
|
49
|
+
* Each move has explicit, well-typed parameters.
|
|
50
|
+
*/
|
|
51
|
+
export type LorcanaMoveParams = {
|
|
52
|
+
// ===== Setup Moves =====
|
|
53
|
+
/**
|
|
54
|
+
* Choose who goes first
|
|
55
|
+
*
|
|
56
|
+
* Rule 3.1.1: First player determined randomly
|
|
57
|
+
*/
|
|
58
|
+
chooseWhoGoesFirstMove: { playerId: PlayerId };
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Alter hand (mulligan)
|
|
62
|
+
*
|
|
63
|
+
* Rule 3.1.6: Players may mulligan by putting cards on bottom of deck
|
|
64
|
+
*/
|
|
65
|
+
alterHand: {
|
|
66
|
+
playerId: PlayerId;
|
|
67
|
+
/** Cards to put on bottom of deck (empty array = keep all) */
|
|
68
|
+
cardsToMulligan: CardId[];
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Draw cards (utility move for testing/effects)
|
|
73
|
+
*/
|
|
74
|
+
drawCards: {
|
|
75
|
+
playerId: PlayerId;
|
|
76
|
+
count: number;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// ===== Resource Moves =====
|
|
80
|
+
/**
|
|
81
|
+
* Put a card into the inkwell
|
|
82
|
+
*
|
|
83
|
+
* Rule 4.3.3: Once per turn, put an inkable card into inkwell
|
|
84
|
+
*/
|
|
85
|
+
putACardIntoTheInkwell: {
|
|
86
|
+
cardId: CardId;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// ===== Core Game Moves =====
|
|
90
|
+
/**
|
|
91
|
+
* Play a card from hand
|
|
92
|
+
*
|
|
93
|
+
* Rule 4.3.4: Pay cost and put card into play (or discard for actions)
|
|
94
|
+
* Rule 6.3.3: Songs can be sung as alternative cost
|
|
95
|
+
* Rule 10.18: Shift allows playing over same-named characters
|
|
96
|
+
*/
|
|
97
|
+
playCard: {
|
|
98
|
+
cardId: CardId;
|
|
99
|
+
} & PlayCardCost;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Quest with a character
|
|
103
|
+
*
|
|
104
|
+
* Rule 4.3.5: Exert character to gain lore equal to its Lore value
|
|
105
|
+
*/
|
|
106
|
+
quest: {
|
|
107
|
+
cardId: CardId;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Challenge with a character
|
|
112
|
+
*
|
|
113
|
+
* Rule 4.3.6: Attack another character or location
|
|
114
|
+
*/
|
|
115
|
+
challenge: {
|
|
116
|
+
/** Attacking character */
|
|
117
|
+
attackerId: CardId;
|
|
118
|
+
/** Defending character or location */
|
|
119
|
+
defenderId: CardId;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Sing a song (legacy - prefer playCard with "sing" cost)
|
|
124
|
+
*
|
|
125
|
+
* Rule 6.3.3: Exert a character to play a song for free
|
|
126
|
+
*/
|
|
127
|
+
sing: {
|
|
128
|
+
/** Character doing the singing */
|
|
129
|
+
singerId: CardId;
|
|
130
|
+
/** Song being sung */
|
|
131
|
+
songId: CardId;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Sing Together (legacy - prefer playCard with "singTogether" cost)
|
|
136
|
+
*
|
|
137
|
+
* Rule 10.10: Multiple characters exert to sing a song together
|
|
138
|
+
*/
|
|
139
|
+
singTogether: {
|
|
140
|
+
/** Characters singing together */
|
|
141
|
+
singersIds: CardId[];
|
|
142
|
+
/** Song being sung */
|
|
143
|
+
songId: CardId;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Move a character to a location
|
|
148
|
+
*
|
|
149
|
+
* Rule 6.5: Characters can move to locations
|
|
150
|
+
*/
|
|
151
|
+
moveCharacterToLocation: {
|
|
152
|
+
characterId: CardId;
|
|
153
|
+
locationId: CardId;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Activate an ability
|
|
158
|
+
*
|
|
159
|
+
* Rule 7: Abilities with costs can be activated
|
|
160
|
+
*/
|
|
161
|
+
activateAbility: {
|
|
162
|
+
cardId: CardId;
|
|
163
|
+
/** Optional ability selection (if card has multiple) */
|
|
164
|
+
abilityIndex?: number;
|
|
165
|
+
/** Alternative identification by ability text */
|
|
166
|
+
abilityText?: string;
|
|
167
|
+
/** Alternative cost for ability (e.g., sing to activate) */
|
|
168
|
+
alternativeCost?: PlayCardCost;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// ===== Effect Resolution =====
|
|
172
|
+
/**
|
|
173
|
+
* Resolve an effect from the bag
|
|
174
|
+
*
|
|
175
|
+
* Rule 8.7: Bag system for triggered effects
|
|
176
|
+
*/
|
|
177
|
+
resolveBag: {
|
|
178
|
+
bagId: string;
|
|
179
|
+
/** Parameters for the effect */
|
|
180
|
+
params: unknown;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Resolve an effect
|
|
185
|
+
*/
|
|
186
|
+
resolveEffect: {
|
|
187
|
+
effectId: string;
|
|
188
|
+
/** Parameters for the effect */
|
|
189
|
+
params: unknown;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// ===== Manual Actions (Testing/Debug) =====
|
|
193
|
+
/**
|
|
194
|
+
* Manually exert a card (for testing)
|
|
195
|
+
*/
|
|
196
|
+
manualExert: {
|
|
197
|
+
cardId: CardId;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// ===== Standard Moves =====
|
|
201
|
+
/**
|
|
202
|
+
* Pass turn to next player
|
|
203
|
+
*
|
|
204
|
+
* Rule 4.1.2: Player completes their turn
|
|
205
|
+
*/
|
|
206
|
+
passTurn: Record<string, never>;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Concede the game
|
|
210
|
+
*
|
|
211
|
+
* Rule 1.9.1.2: Player can concede at any time
|
|
212
|
+
*/
|
|
213
|
+
concede: Record<string, never>;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
// LorcanaGameState is exported from game-state.ts - import from there
|