@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,405 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zone Operations
|
|
3
|
+
*
|
|
4
|
+
* Task 7.3, 7.4: Migrated to use @drmxrcy/tcg-core zone utilities
|
|
5
|
+
*
|
|
6
|
+
* This module provides zone operations for Lorcana. It includes:
|
|
7
|
+
* 1. Flat ZoneState helpers (Record<PlayerId, CardId[]>) - mutable, for simple cases
|
|
8
|
+
* 2. Core Zone re-exports - immutable Zone objects from @drmxrcy/tcg-core (recommended)
|
|
9
|
+
*
|
|
10
|
+
* For new code, prefer using core Zone objects for immutability and advanced features.
|
|
11
|
+
* The flat ZoneState pattern is maintained for backward compatibility.
|
|
12
|
+
*
|
|
13
|
+
* All operations follow Comprehensive Rules Section 8 (Zones)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { CardId, PlayerId } from "@drmxrcy/tcg-core";
|
|
17
|
+
|
|
18
|
+
// Re-export core zone utilities for direct use (recommended for new code)
|
|
19
|
+
// These work with immutable Zone objects from @drmxrcy/tcg-core
|
|
20
|
+
// Re-export with aliases to avoid conflicts with flat ZoneState helpers
|
|
21
|
+
export {
|
|
22
|
+
addCard,
|
|
23
|
+
addCardToBottom as addCardToBottomZone,
|
|
24
|
+
addCardToTop as addCardToTopZone,
|
|
25
|
+
type CardZoneConfig,
|
|
26
|
+
clearZone as clearZoneImmutable,
|
|
27
|
+
createPlayerZones,
|
|
28
|
+
createZone,
|
|
29
|
+
draw,
|
|
30
|
+
filterZoneByVisibility,
|
|
31
|
+
findCardInZones,
|
|
32
|
+
getBottomCard,
|
|
33
|
+
getCardsInZone as getCardsInZoneImmutable,
|
|
34
|
+
getTopCard as getTopCardFromZone,
|
|
35
|
+
getZoneSize as getZoneSizeImmutable,
|
|
36
|
+
isCardInZone as isCardInZoneImmutable,
|
|
37
|
+
mill,
|
|
38
|
+
moveCard,
|
|
39
|
+
peek,
|
|
40
|
+
removeCard,
|
|
41
|
+
reveal,
|
|
42
|
+
search,
|
|
43
|
+
shuffle,
|
|
44
|
+
type Zone,
|
|
45
|
+
type ZoneVisibility,
|
|
46
|
+
} from "@drmxrcy/tcg-core";
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Zone State
|
|
50
|
+
*
|
|
51
|
+
* Maps each player to their array of cards in a zone.
|
|
52
|
+
* Card order is significant for ordered zones (deck, discard).
|
|
53
|
+
*
|
|
54
|
+
* This is a simplified zone representation. For more advanced features,
|
|
55
|
+
* consider using @drmxrcy/tcg-core's Zone objects directly with zone-factory.
|
|
56
|
+
*/
|
|
57
|
+
export type ZoneState = Record<PlayerId, CardId[]>;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Create empty zone state for players
|
|
61
|
+
*
|
|
62
|
+
* Initializes a zone with empty arrays for each player.
|
|
63
|
+
*
|
|
64
|
+
* @param players - Array of player IDs
|
|
65
|
+
* @returns Zone state with empty arrays for each player
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* const players = [createPlayerId("p1"), createPlayerId("p2")];
|
|
70
|
+
* const handZone = createZoneState(players);
|
|
71
|
+
* // { p1: [], p2: [] }
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export const createZoneState = (players: PlayerId[]): ZoneState => {
|
|
75
|
+
const result: ZoneState = {};
|
|
76
|
+
for (const player of players) {
|
|
77
|
+
result[player] = [];
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Add card to player's zone
|
|
84
|
+
*
|
|
85
|
+
* Adds a card to the end of the player's zone array.
|
|
86
|
+
* For ordered zones (deck, discard), this maintains sequence.
|
|
87
|
+
*
|
|
88
|
+
* Note: This mutates the zone state. For immutable operations,
|
|
89
|
+
* consider using @drmxrcy/tcg-core's addCard with Zone objects.
|
|
90
|
+
*
|
|
91
|
+
* @param zoneState - The zone state to modify
|
|
92
|
+
* @param playerId - The player whose zone to add to
|
|
93
|
+
* @param cardId - The card to add
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* addCardToZone(handZone, playerId, cardId);
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export const addCardToZone = (
|
|
101
|
+
zoneState: ZoneState,
|
|
102
|
+
playerId: PlayerId,
|
|
103
|
+
cardId: CardId,
|
|
104
|
+
): void => {
|
|
105
|
+
if (!zoneState[playerId]) {
|
|
106
|
+
zoneState[playerId] = [];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
zoneState[playerId].push(cardId);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Remove card from player's zone
|
|
114
|
+
*
|
|
115
|
+
* Removes the first occurrence of a card from the player's zone.
|
|
116
|
+
* Maintains order for ordered zones.
|
|
117
|
+
*
|
|
118
|
+
* Note: This mutates the zone state. For immutable operations,
|
|
119
|
+
* consider using @drmxrcy/tcg-core's removeCard with Zone objects.
|
|
120
|
+
*
|
|
121
|
+
* @param zoneState - The zone state to modify
|
|
122
|
+
* @param playerId - The player whose zone to remove from
|
|
123
|
+
* @param cardId - The card to remove
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* removeCardFromZone(handZone, playerId, cardId);
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export const removeCardFromZone = (
|
|
131
|
+
zoneState: ZoneState,
|
|
132
|
+
playerId: PlayerId,
|
|
133
|
+
cardId: CardId,
|
|
134
|
+
): void => {
|
|
135
|
+
if (!zoneState[playerId]) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const index = zoneState[playerId].indexOf(cardId);
|
|
140
|
+
if (index !== -1) {
|
|
141
|
+
zoneState[playerId].splice(index, 1);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Move card between zones
|
|
147
|
+
*
|
|
148
|
+
* Removes card from source zone and adds to destination zone.
|
|
149
|
+
* This is the standard way to transition cards between zones.
|
|
150
|
+
*
|
|
151
|
+
* Rule 8.1: Zones are separate from one another
|
|
152
|
+
* Rule 8.1.5: Cards entering private zones lose all info
|
|
153
|
+
*
|
|
154
|
+
* Note: This mutates both zone states. For immutable operations,
|
|
155
|
+
* consider using @drmxrcy/tcg-core's moveCard with Zone objects.
|
|
156
|
+
*
|
|
157
|
+
* @param sourceZone - Zone to remove card from
|
|
158
|
+
* @param destZone - Zone to add card to
|
|
159
|
+
* @param playerId - The player whose zones to use
|
|
160
|
+
* @param cardId - The card to move
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```typescript
|
|
164
|
+
* // Draw card (deck -> hand)
|
|
165
|
+
* moveCardBetweenZones(deckZone, handZone, playerId, topCard);
|
|
166
|
+
*
|
|
167
|
+
* // Play card (hand -> play)
|
|
168
|
+
* moveCardBetweenZones(handZone, playZone, playerId, cardId);
|
|
169
|
+
*
|
|
170
|
+
* // Banish (play -> discard)
|
|
171
|
+
* moveCardBetweenZones(playZone, discardZone, playerId, cardId);
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
export const moveCardBetweenZones = (
|
|
175
|
+
sourceZone: ZoneState,
|
|
176
|
+
destZone: ZoneState,
|
|
177
|
+
playerId: PlayerId,
|
|
178
|
+
cardId: CardId,
|
|
179
|
+
): void => {
|
|
180
|
+
removeCardFromZone(sourceZone, playerId, cardId);
|
|
181
|
+
addCardToZone(destZone, playerId, cardId);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Check if card is in player's zone
|
|
186
|
+
*
|
|
187
|
+
* Uses array.includes which is optimized by JavaScript engines.
|
|
188
|
+
* For checking across multiple zones, see @drmxrcy/tcg-core's findCardInZones.
|
|
189
|
+
*
|
|
190
|
+
* @param zoneState - The zone state to check
|
|
191
|
+
* @param playerId - The player whose zone to check
|
|
192
|
+
* @param cardId - The card to look for
|
|
193
|
+
* @returns True if card is in the player's zone
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```typescript
|
|
197
|
+
* if (isCardInZone(handZone, playerId, cardId)) {
|
|
198
|
+
* // Card is in hand
|
|
199
|
+
* }
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
export const isCardInZone = (
|
|
203
|
+
zoneState: ZoneState,
|
|
204
|
+
playerId: PlayerId,
|
|
205
|
+
cardId: CardId,
|
|
206
|
+
): boolean => {
|
|
207
|
+
const playerZone = zoneState[playerId];
|
|
208
|
+
if (!playerZone) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return playerZone.includes(cardId);
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Get all cards in player's zone
|
|
217
|
+
*
|
|
218
|
+
* Returns a copy of the card array to prevent external modification.
|
|
219
|
+
* For ordered zones, maintains the card sequence.
|
|
220
|
+
*
|
|
221
|
+
* @param zoneState - The zone state to query
|
|
222
|
+
* @param playerId - The player whose cards to get
|
|
223
|
+
* @returns Array of card IDs (copy)
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* const handCards = getCardsInZone(handZone, playerId);
|
|
228
|
+
* const deckSize = getCardsInZone(deckZone, playerId).length;
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
export const getCardsInZone = (
|
|
232
|
+
zoneState: ZoneState,
|
|
233
|
+
playerId: PlayerId,
|
|
234
|
+
): CardId[] => {
|
|
235
|
+
const playerZone = zoneState[playerId];
|
|
236
|
+
if (!playerZone) {
|
|
237
|
+
return [];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Return copy to prevent external mutation
|
|
241
|
+
return [...playerZone];
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Get number of cards in player's zone
|
|
246
|
+
*
|
|
247
|
+
* Rule 8.1.2, 8.1.3: Players can count cards in any zone
|
|
248
|
+
*
|
|
249
|
+
* @param zoneState - The zone state to query
|
|
250
|
+
* @param playerId - The player whose cards to count
|
|
251
|
+
* @returns Number of cards in zone
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```typescript
|
|
255
|
+
* const handSize = getZoneSize(handZone, playerId);
|
|
256
|
+
* const deckSize = getZoneSize(deckZone, playerId);
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
259
|
+
export const getZoneSize = (
|
|
260
|
+
zoneState: ZoneState,
|
|
261
|
+
playerId: PlayerId,
|
|
262
|
+
): number => {
|
|
263
|
+
return getCardsInZone(zoneState, playerId).length;
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Get top card from ordered zone
|
|
268
|
+
*
|
|
269
|
+
* For zones like deck and discard where order matters.
|
|
270
|
+
* Returns undefined if zone is empty.
|
|
271
|
+
*
|
|
272
|
+
* Rule 8.2.3: Draw from top of deck
|
|
273
|
+
*
|
|
274
|
+
* @param zoneState - The zone state to query
|
|
275
|
+
* @param playerId - The player whose zone to check
|
|
276
|
+
* @returns Top card ID or undefined
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```typescript
|
|
280
|
+
* const topCard = getTopCard(deckZone, playerId);
|
|
281
|
+
* if (topCard) {
|
|
282
|
+
* // Draw the top card
|
|
283
|
+
* moveCardBetweenZones(deckZone, handZone, playerId, topCard);
|
|
284
|
+
* }
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
287
|
+
export const getTopCard = (
|
|
288
|
+
zoneState: ZoneState,
|
|
289
|
+
playerId: PlayerId,
|
|
290
|
+
): CardId | undefined => {
|
|
291
|
+
const cards = getCardsInZone(zoneState, playerId);
|
|
292
|
+
return cards[0]; // First card is top
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Clear all cards from player's zone
|
|
297
|
+
*
|
|
298
|
+
* Used for game cleanup or reset scenarios.
|
|
299
|
+
*
|
|
300
|
+
* Note: This mutates the zone state. For immutable operations,
|
|
301
|
+
* consider using @drmxrcy/tcg-core's clearZone with Zone objects.
|
|
302
|
+
*
|
|
303
|
+
* @param zoneState - The zone state to modify
|
|
304
|
+
* @param playerId - The player whose zone to clear
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* ```typescript
|
|
308
|
+
* clearZone(handZone, playerId);
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
export const clearZone = (zoneState: ZoneState, playerId: PlayerId): void => {
|
|
312
|
+
if (zoneState[playerId]) {
|
|
313
|
+
zoneState[playerId] = [];
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Add card to top of ordered zone
|
|
319
|
+
*
|
|
320
|
+
* For zones like deck where adding to top matters.
|
|
321
|
+
*
|
|
322
|
+
* Rule 8.2.4: Cards added to top/bottom in known order
|
|
323
|
+
*
|
|
324
|
+
* Note: This mutates the zone state. For immutable operations,
|
|
325
|
+
* consider using @drmxrcy/tcg-core's addCardToTop with Zone objects.
|
|
326
|
+
*
|
|
327
|
+
* @param zoneState - The zone state to modify
|
|
328
|
+
* @param playerId - The player whose zone to add to
|
|
329
|
+
* @param cardId - The card to add
|
|
330
|
+
*
|
|
331
|
+
* @example
|
|
332
|
+
* ```typescript
|
|
333
|
+
* // Put card on top of deck
|
|
334
|
+
* addCardToTop(deckZone, playerId, cardId);
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
337
|
+
export const addCardToTop = (
|
|
338
|
+
zoneState: ZoneState,
|
|
339
|
+
playerId: PlayerId,
|
|
340
|
+
cardId: CardId,
|
|
341
|
+
): void => {
|
|
342
|
+
if (!zoneState[playerId]) {
|
|
343
|
+
zoneState[playerId] = [];
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
zoneState[playerId].unshift(cardId); // Add to front
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Add card to bottom of ordered zone
|
|
351
|
+
*
|
|
352
|
+
* For zones like deck where adding to bottom matters.
|
|
353
|
+
*
|
|
354
|
+
* Rule 8.2.4: Cards added to top/bottom in known order
|
|
355
|
+
* Rule 3.1.6.1: Mulligan cards go to bottom
|
|
356
|
+
*
|
|
357
|
+
* Note: This mutates the zone state. For immutable operations,
|
|
358
|
+
* consider using @drmxrcy/tcg-core's addCardToBottom with Zone objects.
|
|
359
|
+
*
|
|
360
|
+
* @param zoneState - The zone state to modify
|
|
361
|
+
* @param playerId - The player whose zone to add to
|
|
362
|
+
* @param cardId - The card to add
|
|
363
|
+
*
|
|
364
|
+
* @example
|
|
365
|
+
* ```typescript
|
|
366
|
+
* // Put card on bottom of deck (mulligan)
|
|
367
|
+
* addCardToBottom(deckZone, playerId, cardId);
|
|
368
|
+
* ```
|
|
369
|
+
*/
|
|
370
|
+
export const addCardToBottom = (
|
|
371
|
+
zoneState: ZoneState,
|
|
372
|
+
playerId: PlayerId,
|
|
373
|
+
cardId: CardId,
|
|
374
|
+
): void => {
|
|
375
|
+
if (!zoneState[playerId]) {
|
|
376
|
+
zoneState[playerId] = [];
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
zoneState[playerId].push(cardId); // Add to end
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Migration Note
|
|
384
|
+
*
|
|
385
|
+
* The flat ZoneState helpers above (createZoneState, addCardToZone, etc.) are maintained
|
|
386
|
+
* for backward compatibility. They use mutable operations suitable for use with Immer.
|
|
387
|
+
*
|
|
388
|
+
* For new code, prefer the immutable Zone objects from @drmxrcy/tcg-core (re-exported above).
|
|
389
|
+
* Core Zone objects provide:
|
|
390
|
+
* - Immutability by default
|
|
391
|
+
* - Rich zone operations (shuffle, draw, mill, peek, search)
|
|
392
|
+
* - Visibility filtering
|
|
393
|
+
* - Better type safety
|
|
394
|
+
*
|
|
395
|
+
* Example migration:
|
|
396
|
+
* ```typescript
|
|
397
|
+
* // Old (flat ZoneState - mutable)
|
|
398
|
+
* const handZone: ZoneState = { [player1]: [], [player2]: [] };
|
|
399
|
+
* addCardToZone(handZone, player1, cardId);
|
|
400
|
+
*
|
|
401
|
+
* // New (core Zone - immutable)
|
|
402
|
+
* let handZone = createZone({ id: "hand", name: "Hand", visibility: "private", owner: player1 });
|
|
403
|
+
* handZone = addCard(handZone, cardId);
|
|
404
|
+
* ```
|
|
405
|
+
*/
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { CardZoneConfig, ZoneId } from "@drmxrcy/tcg-core";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lorcana Zone Configurations
|
|
5
|
+
*
|
|
6
|
+
* Defines all zones where cards can exist during the game:
|
|
7
|
+
* - deck: Player's library of cards
|
|
8
|
+
* - hand: Cards available to play
|
|
9
|
+
* - inkwell: Resource zone for paying costs
|
|
10
|
+
* - play: Active cards in play
|
|
11
|
+
* - discard: Graveyard for used/destroyed cards
|
|
12
|
+
*/
|
|
13
|
+
export const lorcanaZones: Record<string, CardZoneConfig> = {
|
|
14
|
+
deck: {
|
|
15
|
+
id: "deck" as ZoneId,
|
|
16
|
+
name: "zones.deck",
|
|
17
|
+
visibility: "secret",
|
|
18
|
+
ordered: true,
|
|
19
|
+
owner: undefined,
|
|
20
|
+
faceDown: true,
|
|
21
|
+
maxSize: 60,
|
|
22
|
+
},
|
|
23
|
+
hand: {
|
|
24
|
+
id: "hand" as ZoneId,
|
|
25
|
+
name: "zones.hand",
|
|
26
|
+
visibility: "private",
|
|
27
|
+
ordered: false,
|
|
28
|
+
owner: undefined,
|
|
29
|
+
faceDown: false,
|
|
30
|
+
maxSize: undefined,
|
|
31
|
+
},
|
|
32
|
+
inkwell: {
|
|
33
|
+
id: "inkwell" as ZoneId,
|
|
34
|
+
name: "zones.inkwell",
|
|
35
|
+
visibility: "public",
|
|
36
|
+
ordered: false,
|
|
37
|
+
owner: undefined,
|
|
38
|
+
faceDown: true,
|
|
39
|
+
maxSize: undefined,
|
|
40
|
+
},
|
|
41
|
+
play: {
|
|
42
|
+
id: "play" as ZoneId,
|
|
43
|
+
name: "zones.play",
|
|
44
|
+
visibility: "public",
|
|
45
|
+
ordered: false,
|
|
46
|
+
owner: undefined,
|
|
47
|
+
faceDown: false,
|
|
48
|
+
maxSize: undefined,
|
|
49
|
+
},
|
|
50
|
+
discard: {
|
|
51
|
+
id: "discard" as ZoneId,
|
|
52
|
+
name: "zones.discard",
|
|
53
|
+
visibility: "public",
|
|
54
|
+
ordered: false,
|
|
55
|
+
owner: undefined,
|
|
56
|
+
faceDown: false,
|
|
57
|
+
maxSize: undefined,
|
|
58
|
+
},
|
|
59
|
+
};
|