@hellacardgames/speed 0.1.0

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.
Files changed (38) hide show
  1. package/README.md +9 -0
  2. package/dist/GameEvent-DDQ4jXxt.d.cts +111 -0
  3. package/dist/GameEvent-DDQ4jXxt.d.cts.map +1 -0
  4. package/dist/GameEvent-DDQ4jXxt.d.mts +111 -0
  5. package/dist/GameEvent-DDQ4jXxt.d.mts.map +1 -0
  6. package/dist/client/index.cjs +126 -0
  7. package/dist/client/index.cjs.map +1 -0
  8. package/dist/client/index.d.cts +38 -0
  9. package/dist/client/index.d.cts.map +1 -0
  10. package/dist/client/index.d.mts +38 -0
  11. package/dist/client/index.d.mts.map +1 -0
  12. package/dist/client/index.mjs +125 -0
  13. package/dist/client/index.mjs.map +1 -0
  14. package/dist/manager/index.cjs +33 -0
  15. package/dist/manager/index.cjs.map +1 -0
  16. package/dist/manager/index.d.cts +109 -0
  17. package/dist/manager/index.d.cts.map +1 -0
  18. package/dist/manager/index.d.mts +109 -0
  19. package/dist/manager/index.d.mts.map +1 -0
  20. package/dist/manager/index.mjs +22 -0
  21. package/dist/manager/index.mjs.map +1 -0
  22. package/dist/server/index.cjs +204 -0
  23. package/dist/server/index.cjs.map +1 -0
  24. package/dist/server/index.d.cts +40 -0
  25. package/dist/server/index.d.cts.map +1 -0
  26. package/dist/server/index.d.mts +40 -0
  27. package/dist/server/index.d.mts.map +1 -0
  28. package/dist/server/index.mjs +203 -0
  29. package/dist/server/index.mjs.map +1 -0
  30. package/dist/startGame-5InkAgEp.d.cts +108 -0
  31. package/dist/startGame-5InkAgEp.d.cts.map +1 -0
  32. package/dist/startGame-BC9XfFzG.mjs +815 -0
  33. package/dist/startGame-BC9XfFzG.mjs.map +1 -0
  34. package/dist/startGame-Bhk__XWk.cjs +898 -0
  35. package/dist/startGame-Bhk__XWk.cjs.map +1 -0
  36. package/dist/startGame-ayp8a-Cl.d.mts +108 -0
  37. package/dist/startGame-ayp8a-Cl.d.mts.map +1 -0
  38. package/package.json +92 -0
@@ -0,0 +1,815 @@
1
+ //#region src/manager/constants.ts
2
+ const GAME_KEY = "speed";
3
+ const WATCHDOG_INTERVAL_MS = 1e4;
4
+ const EXPIRY_EXTENSION_MS = 3e5;
5
+ const CAN_PLAY_AT_DELAY_MS = 3e3;
6
+ const CARDS = [
7
+ {
8
+ id: crypto.randomUUID(),
9
+ rank: 2,
10
+ suite: "clubs"
11
+ },
12
+ {
13
+ id: crypto.randomUUID(),
14
+ rank: 3,
15
+ suite: "clubs"
16
+ },
17
+ {
18
+ id: crypto.randomUUID(),
19
+ rank: 4,
20
+ suite: "clubs"
21
+ },
22
+ {
23
+ id: crypto.randomUUID(),
24
+ rank: 5,
25
+ suite: "clubs"
26
+ },
27
+ {
28
+ id: crypto.randomUUID(),
29
+ rank: 6,
30
+ suite: "clubs"
31
+ },
32
+ {
33
+ id: crypto.randomUUID(),
34
+ rank: 7,
35
+ suite: "clubs"
36
+ },
37
+ {
38
+ id: crypto.randomUUID(),
39
+ rank: 8,
40
+ suite: "clubs"
41
+ },
42
+ {
43
+ id: crypto.randomUUID(),
44
+ rank: 9,
45
+ suite: "clubs"
46
+ },
47
+ {
48
+ id: crypto.randomUUID(),
49
+ rank: 10,
50
+ suite: "clubs"
51
+ },
52
+ {
53
+ id: crypto.randomUUID(),
54
+ rank: 11,
55
+ suite: "clubs"
56
+ },
57
+ {
58
+ id: crypto.randomUUID(),
59
+ rank: 12,
60
+ suite: "clubs"
61
+ },
62
+ {
63
+ id: crypto.randomUUID(),
64
+ rank: 13,
65
+ suite: "clubs"
66
+ },
67
+ {
68
+ id: crypto.randomUUID(),
69
+ rank: 14,
70
+ suite: "clubs"
71
+ },
72
+ {
73
+ id: crypto.randomUUID(),
74
+ rank: 2,
75
+ suite: "diamonds"
76
+ },
77
+ {
78
+ id: crypto.randomUUID(),
79
+ rank: 3,
80
+ suite: "diamonds"
81
+ },
82
+ {
83
+ id: crypto.randomUUID(),
84
+ rank: 4,
85
+ suite: "diamonds"
86
+ },
87
+ {
88
+ id: crypto.randomUUID(),
89
+ rank: 5,
90
+ suite: "diamonds"
91
+ },
92
+ {
93
+ id: crypto.randomUUID(),
94
+ rank: 6,
95
+ suite: "diamonds"
96
+ },
97
+ {
98
+ id: crypto.randomUUID(),
99
+ rank: 7,
100
+ suite: "diamonds"
101
+ },
102
+ {
103
+ id: crypto.randomUUID(),
104
+ rank: 8,
105
+ suite: "diamonds"
106
+ },
107
+ {
108
+ id: crypto.randomUUID(),
109
+ rank: 9,
110
+ suite: "diamonds"
111
+ },
112
+ {
113
+ id: crypto.randomUUID(),
114
+ rank: 10,
115
+ suite: "diamonds"
116
+ },
117
+ {
118
+ id: crypto.randomUUID(),
119
+ rank: 11,
120
+ suite: "diamonds"
121
+ },
122
+ {
123
+ id: crypto.randomUUID(),
124
+ rank: 12,
125
+ suite: "diamonds"
126
+ },
127
+ {
128
+ id: crypto.randomUUID(),
129
+ rank: 13,
130
+ suite: "diamonds"
131
+ },
132
+ {
133
+ id: crypto.randomUUID(),
134
+ rank: 14,
135
+ suite: "diamonds"
136
+ },
137
+ {
138
+ id: crypto.randomUUID(),
139
+ rank: 2,
140
+ suite: "hearts"
141
+ },
142
+ {
143
+ id: crypto.randomUUID(),
144
+ rank: 3,
145
+ suite: "hearts"
146
+ },
147
+ {
148
+ id: crypto.randomUUID(),
149
+ rank: 4,
150
+ suite: "hearts"
151
+ },
152
+ {
153
+ id: crypto.randomUUID(),
154
+ rank: 5,
155
+ suite: "hearts"
156
+ },
157
+ {
158
+ id: crypto.randomUUID(),
159
+ rank: 6,
160
+ suite: "hearts"
161
+ },
162
+ {
163
+ id: crypto.randomUUID(),
164
+ rank: 7,
165
+ suite: "hearts"
166
+ },
167
+ {
168
+ id: crypto.randomUUID(),
169
+ rank: 8,
170
+ suite: "hearts"
171
+ },
172
+ {
173
+ id: crypto.randomUUID(),
174
+ rank: 9,
175
+ suite: "hearts"
176
+ },
177
+ {
178
+ id: crypto.randomUUID(),
179
+ rank: 10,
180
+ suite: "hearts"
181
+ },
182
+ {
183
+ id: crypto.randomUUID(),
184
+ rank: 11,
185
+ suite: "hearts"
186
+ },
187
+ {
188
+ id: crypto.randomUUID(),
189
+ rank: 12,
190
+ suite: "hearts"
191
+ },
192
+ {
193
+ id: crypto.randomUUID(),
194
+ rank: 13,
195
+ suite: "hearts"
196
+ },
197
+ {
198
+ id: crypto.randomUUID(),
199
+ rank: 14,
200
+ suite: "hearts"
201
+ },
202
+ {
203
+ id: crypto.randomUUID(),
204
+ rank: 2,
205
+ suite: "spades"
206
+ },
207
+ {
208
+ id: crypto.randomUUID(),
209
+ rank: 3,
210
+ suite: "spades"
211
+ },
212
+ {
213
+ id: crypto.randomUUID(),
214
+ rank: 4,
215
+ suite: "spades"
216
+ },
217
+ {
218
+ id: crypto.randomUUID(),
219
+ rank: 5,
220
+ suite: "spades"
221
+ },
222
+ {
223
+ id: crypto.randomUUID(),
224
+ rank: 6,
225
+ suite: "spades"
226
+ },
227
+ {
228
+ id: crypto.randomUUID(),
229
+ rank: 7,
230
+ suite: "spades"
231
+ },
232
+ {
233
+ id: crypto.randomUUID(),
234
+ rank: 8,
235
+ suite: "spades"
236
+ },
237
+ {
238
+ id: crypto.randomUUID(),
239
+ rank: 9,
240
+ suite: "spades"
241
+ },
242
+ {
243
+ id: crypto.randomUUID(),
244
+ rank: 10,
245
+ suite: "spades"
246
+ },
247
+ {
248
+ id: crypto.randomUUID(),
249
+ rank: 11,
250
+ suite: "spades"
251
+ },
252
+ {
253
+ id: crypto.randomUUID(),
254
+ rank: 12,
255
+ suite: "spades"
256
+ },
257
+ {
258
+ id: crypto.randomUUID(),
259
+ rank: 13,
260
+ suite: "spades"
261
+ },
262
+ {
263
+ id: crypto.randomUUID(),
264
+ rank: 14,
265
+ suite: "spades"
266
+ }
267
+ ];
268
+ //#endregion
269
+ //#region src/manager/games.ts
270
+ const games = /* @__PURE__ */ new Map();
271
+ //#endregion
272
+ //#region src/manager/actions/createGame.ts
273
+ function createGame(userId, username) {
274
+ if (games.size === 50) return {
275
+ success: false,
276
+ error: "maxGamesReached"
277
+ };
278
+ const player = {
279
+ id: crypto.randomUUID(),
280
+ userId,
281
+ username,
282
+ events: [],
283
+ hand: [],
284
+ drawPile: [],
285
+ sidePile: [],
286
+ centerPile: [],
287
+ hasNoPlayableCards: false
288
+ };
289
+ const createdAt = Date.now();
290
+ const game = {
291
+ status: "open",
292
+ id: crypto.randomUUID(),
293
+ createdAt,
294
+ expiresAt: createdAt + EXPIRY_EXTENSION_MS,
295
+ chatMessages: [],
296
+ players: [player]
297
+ };
298
+ games.set(game.id, game);
299
+ return {
300
+ success: true,
301
+ gameId: game.id,
302
+ playerId: player.id
303
+ };
304
+ }
305
+ //#endregion
306
+ //#region src/lib/emitEvent.ts
307
+ function emitEvent(game, data) {
308
+ const event = {
309
+ ...data,
310
+ id: crypto.randomUUID()
311
+ };
312
+ for (const p of game.players) p.events.push(event);
313
+ }
314
+ //#endregion
315
+ //#region src/lib/emitEventToPlayer.ts
316
+ function emitEventToPlayer(player, data) {
317
+ player.events.push({
318
+ ...data,
319
+ id: crypto.randomUUID()
320
+ });
321
+ }
322
+ //#endregion
323
+ //#region src/manager/actions/drawCard.ts
324
+ function drawCard(gameId, playerId) {
325
+ const game = games.get(gameId);
326
+ if (!game) return {
327
+ success: false,
328
+ error: "gameNotFound"
329
+ };
330
+ const player = game.players.find((p) => p.id === playerId);
331
+ if (!player) return {
332
+ success: false,
333
+ error: "playerNotFound"
334
+ };
335
+ if (game.status !== "started") return {
336
+ success: false,
337
+ error: "invalidStatus"
338
+ };
339
+ if (Date.now() < game.canPlayAt) return {
340
+ success: false,
341
+ error: "canPlayAtNotReached"
342
+ };
343
+ if (player.hand.length >= 5) return {
344
+ success: false,
345
+ error: "handFull"
346
+ };
347
+ if (player.drawPile.length === 0) return {
348
+ success: false,
349
+ error: "drawPileEmpty"
350
+ };
351
+ game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;
352
+ emitEvent(game, {
353
+ type: "expirationUpdated",
354
+ expiresAt: game.expiresAt
355
+ });
356
+ const card = player.drawPile.pop();
357
+ player.hand.push(card);
358
+ emitEventToPlayer(player, {
359
+ type: "drewCard",
360
+ card
361
+ });
362
+ emitEvent(game, {
363
+ type: "playerDrewCard",
364
+ username: player.username
365
+ });
366
+ return { success: true };
367
+ }
368
+ //#endregion
369
+ //#region src/manager/actions/getClientStateAndClearEvents.ts
370
+ function getClientStateAndClearEvents(gameId, playerId) {
371
+ const game = games.get(gameId);
372
+ if (!game) return {
373
+ success: false,
374
+ error: "gameNotFound"
375
+ };
376
+ const player = game.players.find((p) => p.id === playerId);
377
+ if (!player) return {
378
+ success: false,
379
+ error: "playerNotFound"
380
+ };
381
+ const state = {
382
+ status: game.status,
383
+ gameId,
384
+ playerId,
385
+ username: player.username,
386
+ players: game.players.map((p) => ({
387
+ username: p.username,
388
+ handSize: p.hand.length,
389
+ drawPileSize: p.drawPile.length,
390
+ sidePileSize: p.sidePile.length,
391
+ centerPileTopCard: p.centerPile.length ? p.centerPile[p.centerPile.length - 1] : null
392
+ })),
393
+ expiresAt: game.expiresAt,
394
+ chatMessages: game.chatMessages,
395
+ hand: player.hand,
396
+ canPlayAt: game.status === "started" ? game.canPlayAt : null
397
+ };
398
+ player.events.length = 0;
399
+ return {
400
+ success: true,
401
+ state
402
+ };
403
+ }
404
+ //#endregion
405
+ //#region src/manager/actions/getEventsAndClearAcknowledged.ts
406
+ function getEventsAndClearAcknowledged(gameId, playerId, lastReadId) {
407
+ const game = games.get(gameId);
408
+ if (!game) return {
409
+ success: false,
410
+ error: "gameNotFound"
411
+ };
412
+ const player = game.players.find((p) => p.id === playerId);
413
+ if (!player) return {
414
+ success: false,
415
+ error: "playerNotFound"
416
+ };
417
+ const lastReadEventIndex = player.events.findIndex((e) => e.id === lastReadId);
418
+ player.events.splice(0, lastReadEventIndex + 1);
419
+ return {
420
+ success: true,
421
+ events: player.events
422
+ };
423
+ }
424
+ //#endregion
425
+ //#region src/manager/actions/getJoinableGames.ts
426
+ function getJoinableGames() {
427
+ return {
428
+ success: true,
429
+ games: Array.from(games.values()).filter((g) => g.status === "open" && g.players.length < 2).map((g) => ({
430
+ id: g.id,
431
+ numPlayers: g.players.length
432
+ }))
433
+ };
434
+ }
435
+ //#endregion
436
+ //#region src/manager/actions/joinGame.ts
437
+ function joinGame(gameId, userId, username) {
438
+ const game = games.get(gameId);
439
+ if (!game) return {
440
+ success: false,
441
+ error: "gameNotFound"
442
+ };
443
+ if (game.status !== "open") return {
444
+ success: false,
445
+ error: "invalidStatus"
446
+ };
447
+ if (game.players.length === 2) return {
448
+ success: false,
449
+ error: "maxPlayersReached"
450
+ };
451
+ if (game.players.find((p) => p.userId === userId)) return {
452
+ success: false,
453
+ error: "alreadyInGame"
454
+ };
455
+ const player = {
456
+ id: crypto.randomUUID(),
457
+ userId,
458
+ username,
459
+ events: [],
460
+ hand: [],
461
+ drawPile: [],
462
+ sidePile: [],
463
+ centerPile: [],
464
+ hasNoPlayableCards: false
465
+ };
466
+ game.players.push(player);
467
+ emitEvent(game, {
468
+ type: "playerJoined",
469
+ username
470
+ });
471
+ return {
472
+ success: true,
473
+ playerId: player.id
474
+ };
475
+ }
476
+ //#endregion
477
+ //#region src/manager/actions/leaveGame.ts
478
+ function leaveGame(gameId, playerId) {
479
+ const game = games.get(gameId);
480
+ if (!game) return {
481
+ success: false,
482
+ error: "gameNotFound"
483
+ };
484
+ const playerIndex = game.players.findIndex((p) => p.id === playerId);
485
+ if (playerIndex === -1) return {
486
+ success: false,
487
+ error: "playerNotFound"
488
+ };
489
+ const player = game.players[playerIndex];
490
+ emitEvent(game, {
491
+ type: "playerLeft",
492
+ username: player.username
493
+ });
494
+ game.players.splice(playerIndex, 1);
495
+ if (game.status === "started" && game.players.length < 2) {
496
+ const forfeitedGame = {
497
+ ...game,
498
+ status: "forfeited",
499
+ expiresAt: Date.now() + EXPIRY_EXTENSION_MS
500
+ };
501
+ games.set(game.id, forfeitedGame);
502
+ emitEvent(forfeitedGame, { type: "gameForfeited" });
503
+ emitEvent(forfeitedGame, {
504
+ type: "expirationUpdated",
505
+ expiresAt: forfeitedGame.expiresAt
506
+ });
507
+ }
508
+ if (game.players.length === 0) games.delete(game.id);
509
+ return { success: true };
510
+ }
511
+ //#endregion
512
+ //#region src/manager/lib/isCardPlayable.ts
513
+ function isCardPlayable(card, targetPile) {
514
+ const targetCard = targetPile[targetPile.length - 1];
515
+ return Math.abs(card.rank - targetCard.rank) % 11 === 1;
516
+ }
517
+ //#endregion
518
+ //#region src/manager/lib/hasPlayableCard.ts
519
+ function hasPlayableCard(player, game) {
520
+ const otherPlayer = game.players.find((p) => p !== player);
521
+ const targetPile1 = player.centerPile;
522
+ const targetPile2 = otherPlayer.centerPile;
523
+ for (const card of player.hand) if (isCardPlayable(card, targetPile1) || isCardPlayable(card, targetPile2)) return true;
524
+ return false;
525
+ }
526
+ //#endregion
527
+ //#region src/manager/actions/playCard.ts
528
+ function playCard(gameId, playerId, cardId, isForOtherPlayerPile) {
529
+ const game = games.get(gameId);
530
+ if (!game) return {
531
+ success: false,
532
+ error: "gameNotFound"
533
+ };
534
+ const player = game.players.find((p) => p.id === playerId);
535
+ if (!player) return {
536
+ success: false,
537
+ error: "playerNotFound"
538
+ };
539
+ if (game.status !== "started") return {
540
+ success: false,
541
+ error: "invalidStatus"
542
+ };
543
+ if (Date.now() < game.canPlayAt) return {
544
+ success: false,
545
+ error: "canPlayAtNotReached"
546
+ };
547
+ const cardIndex = player.hand.findIndex((c) => c.id === cardId);
548
+ if (cardIndex === -1) return {
549
+ success: false,
550
+ error: "cardNotFound"
551
+ };
552
+ const otherPlayer = game.players.find((p) => p !== player);
553
+ const targetPile = isForOtherPlayerPile ? otherPlayer.centerPile : player.centerPile;
554
+ const card = player.hand[cardIndex];
555
+ if (!isCardPlayable(card, targetPile)) return {
556
+ success: false,
557
+ error: "cardNotPlayable"
558
+ };
559
+ game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;
560
+ emitEvent(game, {
561
+ type: "expirationUpdated",
562
+ expiresAt: game.expiresAt
563
+ });
564
+ player.hand.splice(cardIndex, 1);
565
+ targetPile.push(card);
566
+ emitEvent(game, {
567
+ type: "cardPlayed",
568
+ username: player.username,
569
+ card,
570
+ isForOtherPlayerPile
571
+ });
572
+ if (otherPlayer.hasNoPlayableCards && hasPlayableCard(otherPlayer, game)) otherPlayer.hasNoPlayableCards = false;
573
+ if (player.hand.length === 0 && player.drawPile.length === 0) {
574
+ emitEvent(game, { type: "gameCompleted" });
575
+ const completedGame = {
576
+ ...game,
577
+ status: "completed"
578
+ };
579
+ games.set(game.id, completedGame);
580
+ }
581
+ return { success: true };
582
+ }
583
+ //#endregion
584
+ //#region src/lib/shuffleCards.ts
585
+ function shuffleCards(cards) {
586
+ for (let i = cards.length - 1; i > 0; i--) {
587
+ const j = Math.floor(Math.random() * (i + 1));
588
+ [cards[i], cards[j]] = [cards[j], cards[i]];
589
+ }
590
+ }
591
+ //#endregion
592
+ //#region src/manager/actions/reportNoPlayableCards.ts
593
+ function reportNoPlayableCards(gameId, playerId) {
594
+ const game = games.get(gameId);
595
+ if (!game) return {
596
+ success: false,
597
+ error: "gameNotFound"
598
+ };
599
+ const player = game.players.find((p) => p.id === playerId);
600
+ if (!player) return {
601
+ success: false,
602
+ error: "playerNotFound"
603
+ };
604
+ if (game.status !== "started") return {
605
+ success: false,
606
+ error: "invalidStatus"
607
+ };
608
+ if (Date.now() < game.canPlayAt) return {
609
+ success: false,
610
+ error: "canPlayAtNotReached"
611
+ };
612
+ if (player.hasNoPlayableCards) return {
613
+ success: false,
614
+ error: "alreadyReported"
615
+ };
616
+ if (hasPlayableCard(player, game)) return {
617
+ success: false,
618
+ error: "hasPlayableCard"
619
+ };
620
+ if (player.drawPile.length > 0 && player.hand.length < 5) return {
621
+ success: false,
622
+ error: "canDraw"
623
+ };
624
+ game.expiresAt = Date.now() + EXPIRY_EXTENSION_MS;
625
+ emitEvent(game, {
626
+ type: "expirationUpdated",
627
+ expiresAt: game.expiresAt
628
+ });
629
+ player.hasNoPlayableCards = true;
630
+ const otherPlayer = game.players.find((p) => p !== player);
631
+ if (otherPlayer.hasNoPlayableCards) {
632
+ if (player.sidePile.length === 0 && otherPlayer.sidePile.length === 0) {
633
+ const cards = [...player.centerPile, ...otherPlayer.centerPile];
634
+ shuffleCards(cards);
635
+ player.sidePile.push(...cards.splice(0, 5));
636
+ emitEvent(game, {
637
+ type: "playerSidePileInitialized",
638
+ username: player.username,
639
+ numCards: player.sidePile.length
640
+ });
641
+ player.centerPile.push(...cards.splice(0, 1));
642
+ emitEvent(game, {
643
+ type: "playerCenterPileInitialized",
644
+ username: player.username,
645
+ card: player.centerPile[player.centerPile.length - 1]
646
+ });
647
+ otherPlayer.centerPile.push(...cards.splice(0, 1));
648
+ emitEvent(game, {
649
+ type: "playerCenterPileInitialized",
650
+ username: otherPlayer.username,
651
+ card: otherPlayer.centerPile[otherPlayer.centerPile.length - 1]
652
+ });
653
+ otherPlayer.sidePile.push(...cards.splice(0, 5));
654
+ emitEvent(game, {
655
+ type: "playerSidePileInitialized",
656
+ username: otherPlayer.username,
657
+ numCards: otherPlayer.sidePile.length
658
+ });
659
+ } else {
660
+ const playerSidePileCard = player.sidePile.pop();
661
+ player.centerPile.push(playerSidePileCard);
662
+ emitEvent(game, {
663
+ type: "cardDrawnFromSidePileToCenterPile",
664
+ username: player.username,
665
+ card: playerSidePileCard
666
+ });
667
+ const otherPlayerSidePileCard = otherPlayer.sidePile.pop();
668
+ otherPlayer.centerPile.push(otherPlayerSidePileCard);
669
+ emitEvent(game, {
670
+ type: "cardDrawnFromSidePileToCenterPile",
671
+ username: otherPlayer.username,
672
+ card: otherPlayerSidePileCard
673
+ });
674
+ }
675
+ player.hasNoPlayableCards = false;
676
+ otherPlayer.hasNoPlayableCards = false;
677
+ game.canPlayAt = Date.now() + CAN_PLAY_AT_DELAY_MS;
678
+ emitEvent(game, {
679
+ type: "canPlayAtUpdated",
680
+ canPlayAt: game.canPlayAt
681
+ });
682
+ }
683
+ return { success: true };
684
+ }
685
+ //#endregion
686
+ //#region src/manager/actions/sendChat.ts
687
+ function sendChat(gameId, playerId, text) {
688
+ const game = games.get(gameId);
689
+ if (!game) return {
690
+ success: false,
691
+ error: "gameNotFound"
692
+ };
693
+ const player = game.players.find((p) => p.id === playerId);
694
+ if (!player) return {
695
+ success: false,
696
+ error: "playerNotFound"
697
+ };
698
+ const message = {
699
+ id: crypto.randomUUID(),
700
+ username: player.username,
701
+ text
702
+ };
703
+ game.chatMessages.push(message);
704
+ emitEvent(game, {
705
+ type: "chat",
706
+ message
707
+ });
708
+ return { success: true };
709
+ }
710
+ //#endregion
711
+ //#region src/manager/actions/startGame.ts
712
+ function startGame(gameId, playerId) {
713
+ const game = games.get(gameId);
714
+ if (!game) return {
715
+ success: false,
716
+ error: "gameNotFound"
717
+ };
718
+ const player = game.players.find((p) => p.id === playerId);
719
+ if (!player) return {
720
+ success: false,
721
+ error: "playerNotFound"
722
+ };
723
+ if (game.status !== "open") return {
724
+ success: false,
725
+ error: "invalidStatus"
726
+ };
727
+ if (game.players.indexOf(player) !== 0) return {
728
+ success: false,
729
+ error: "playerNotAdmin"
730
+ };
731
+ if (game.players.length < 2) return {
732
+ success: false,
733
+ error: "minPlayersNotReached"
734
+ };
735
+ const otherPlayer = game.players[1];
736
+ const cards = [...CARDS];
737
+ shuffleCards(cards);
738
+ player.sidePile.push(...cards.splice(0, 5));
739
+ emitEvent(game, {
740
+ type: "playerSidePileInitialized",
741
+ username: player.username,
742
+ numCards: player.sidePile.length
743
+ });
744
+ player.centerPile.push(...cards.splice(0, 1));
745
+ emitEvent(game, {
746
+ type: "playerCenterPileInitialized",
747
+ username: player.username,
748
+ card: player.centerPile[player.centerPile.length - 1]
749
+ });
750
+ otherPlayer.centerPile.push(...cards.splice(0, 1));
751
+ emitEvent(game, {
752
+ type: "playerCenterPileInitialized",
753
+ username: otherPlayer.username,
754
+ card: otherPlayer.centerPile[otherPlayer.centerPile.length - 1]
755
+ });
756
+ otherPlayer.sidePile.push(...cards.splice(0, 5));
757
+ emitEvent(game, {
758
+ type: "playerSidePileInitialized",
759
+ username: otherPlayer.username,
760
+ numCards: otherPlayer.sidePile.length
761
+ });
762
+ player.drawPile.push(...cards.splice(0, 15));
763
+ emitEvent(game, {
764
+ type: "playerDrawPileInitialized",
765
+ username: player.username,
766
+ numCards: player.drawPile.length
767
+ });
768
+ player.hand.push(...cards.splice(0, 5));
769
+ emitEventToPlayer(player, {
770
+ type: "handInitialized",
771
+ cards: player.hand
772
+ });
773
+ emitEvent(game, {
774
+ type: "playerHandInitialized",
775
+ username: player.username,
776
+ numCards: player.hand.length
777
+ });
778
+ otherPlayer.drawPile.push(...cards.splice(0, 15));
779
+ emitEvent(game, {
780
+ type: "playerDrawPileInitialized",
781
+ username: otherPlayer.username,
782
+ numCards: otherPlayer.drawPile.length
783
+ });
784
+ otherPlayer.hand.push(...cards.splice(0, 5));
785
+ emitEventToPlayer(otherPlayer, {
786
+ type: "handInitialized",
787
+ cards: otherPlayer.hand
788
+ });
789
+ emitEvent(game, {
790
+ type: "playerHandInitialized",
791
+ username: otherPlayer.username,
792
+ numCards: otherPlayer.hand.length
793
+ });
794
+ const startedGame = {
795
+ ...game,
796
+ status: "started",
797
+ expiresAt: Date.now() + EXPIRY_EXTENSION_MS,
798
+ canPlayAt: Date.now() + CAN_PLAY_AT_DELAY_MS
799
+ };
800
+ games.set(game.id, startedGame);
801
+ emitEvent(startedGame, { type: "gameStarted" });
802
+ emitEvent(startedGame, {
803
+ type: "expirationUpdated",
804
+ expiresAt: startedGame.expiresAt
805
+ });
806
+ emitEvent(startedGame, {
807
+ type: "canPlayAtUpdated",
808
+ canPlayAt: startedGame.canPlayAt
809
+ });
810
+ return { success: true };
811
+ }
812
+ //#endregion
813
+ export { leaveGame as a, getEventsAndClearAcknowledged as c, createGame as d, games as f, playCard as i, getClientStateAndClearEvents as l, WATCHDOG_INTERVAL_MS as m, sendChat as n, joinGame as o, GAME_KEY as p, reportNoPlayableCards as r, getJoinableGames as s, startGame as t, drawCard as u };
814
+
815
+ //# sourceMappingURL=startGame-BC9XfFzG.mjs.map