@masonf539/egyptian-war 1.0.0 → 1.0.1-beta.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.
- package/LICENSE +26 -26
- package/README.md +68 -66
- package/dist/definition.js +4 -4
- package/dist/engine.d.ts +4 -4
- package/dist/engine.js +4 -7
- package/dist/session.js +8 -26
- package/docs/design.md +126 -126
- package/package.json +52 -52
- package/public/client.js +1203 -1193
- package/public/register.js +28 -28
- package/public/style.css +1526 -1505
- package/public/template.html +152 -152
package/LICENSE
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
Egyptian War License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Mason Fong
|
|
4
|
-
|
|
5
|
-
Required Notice: Copyright (c) 2026 Mason Fong
|
|
6
|
-
Required Notice: Egyptian War source code is licensed under the PolyForm Noncommercial License 1.0.0.
|
|
7
|
-
|
|
8
|
-
The Egyptian War software, including its source code, browser code, styles,
|
|
9
|
-
templates, tests, documentation, rules engine, and networking implementation,
|
|
10
|
-
is licensed under the PolyForm Noncommercial License 1.0.0.
|
|
11
|
-
|
|
12
|
-
You may use, copy, modify, and distribute the Egyptian War software only for
|
|
13
|
-
purposes permitted by that license. Commercial use requires a separate written
|
|
14
|
-
license from the copyright holder.
|
|
15
|
-
|
|
16
|
-
The complete PolyForm Noncommercial License 1.0.0 is available at:
|
|
17
|
-
|
|
18
|
-
https://polyformproject.org/licenses/noncommercial/1.0.0
|
|
19
|
-
|
|
20
|
-
The creative assets located in public/assets are part of the Egyptian War software
|
|
21
|
-
and are licensed under the PolyForm Noncommercial License 1.0.0, like the rest of this package.
|
|
22
|
-
|
|
23
|
-
Third-party dependencies and separately identified third-party materials remain
|
|
24
|
-
governed by their respective licenses.
|
|
25
|
-
|
|
26
|
-
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OR CONDITION, TO THE EXTENT
|
|
1
|
+
Egyptian War License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mason Fong
|
|
4
|
+
|
|
5
|
+
Required Notice: Copyright (c) 2026 Mason Fong
|
|
6
|
+
Required Notice: Egyptian War source code is licensed under the PolyForm Noncommercial License 1.0.0.
|
|
7
|
+
|
|
8
|
+
The Egyptian War software, including its source code, browser code, styles,
|
|
9
|
+
templates, tests, documentation, rules engine, and networking implementation,
|
|
10
|
+
is licensed under the PolyForm Noncommercial License 1.0.0.
|
|
11
|
+
|
|
12
|
+
You may use, copy, modify, and distribute the Egyptian War software only for
|
|
13
|
+
purposes permitted by that license. Commercial use requires a separate written
|
|
14
|
+
license from the copyright holder.
|
|
15
|
+
|
|
16
|
+
The complete PolyForm Noncommercial License 1.0.0 is available at:
|
|
17
|
+
|
|
18
|
+
https://polyformproject.org/licenses/noncommercial/1.0.0
|
|
19
|
+
|
|
20
|
+
The creative assets located in public/assets are part of the Egyptian War software
|
|
21
|
+
and are licensed under the PolyForm Noncommercial License 1.0.0, like the rest of this package.
|
|
22
|
+
|
|
23
|
+
Third-party dependencies and separately identified third-party materials remain
|
|
24
|
+
governed by their respective licenses.
|
|
25
|
+
|
|
26
|
+
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OR CONDITION, TO THE EXTENT
|
|
27
27
|
PERMITTED BY LAW.
|
package/README.md
CHANGED
|
@@ -1,66 +1,68 @@
|
|
|
1
|
-
# Egyptian War for GameHub
|
|
2
|
-
|
|
3
|
-
This
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Egyptian War is designed to be installed and run by GameHub through the
|
|
7
|
-
`@masonf539/game-sdk` contracts. It is independently versioned, but it is not a
|
|
8
|
-
standalone web application and depends on GameHub for rooms, networking, the
|
|
9
|
-
lobby, shared settings, and browser hosting.
|
|
10
|
-
|
|
11
|
-
The package owns its rules, state engine, slap arbitration, tests, browser
|
|
12
|
-
presentation, game-specific audio, preview media, and design documentation.
|
|
13
|
-
GameHub supplies rooms, members, spectators, transport, and shared services.
|
|
14
|
-
|
|
15
|
-
`src/session.ts` owns the authoritative runtime lifecycle, including turn
|
|
16
|
-
timers, slap collection, pause/disconnect recovery, animations, member
|
|
17
|
-
removal, and completion. `public/client.js` owns the browser controller and
|
|
18
|
-
registers it with GameHub's generic client host.
|
|
19
|
-
|
|
20
|
-
Game-specific music, effects, and picker previews live under `public/assets`
|
|
21
|
-
and are served by GameHub from `/games/egyptian-war`.
|
|
22
|
-
The package-owned `public/register.js` registers those sounds with GameHub's
|
|
23
|
-
shared audio service.
|
|
24
|
-
The game board lives in `public/template.html`, and its presentation lives in
|
|
25
|
-
`public/style.css`. GameHub injects the fragment into a package-specific root;
|
|
26
|
-
the browser controller scopes its element lookups to that root.
|
|
27
|
-
During active play, the stylesheet fixes the board to the available viewport,
|
|
28
|
-
accounts for mobile safe areas, and keeps its compact Pause/connection row
|
|
29
|
-
below GameHub's Settings shortcut. Full Screen and confirmed Exit/Leave
|
|
30
|
-
actions remain platform-owned controls in GameHub's reusable Game tab. The
|
|
31
|
-
shortcut is mounted inside the active game viewport, while the Settings tabs
|
|
32
|
-
move into one horizontal row at medium and small widths.
|
|
33
|
-
Egyptian War declares `play-card` and `slap` input actions for GameHub's
|
|
34
|
-
package-aware Keybinds tab. Their defaults are E and Space respectively, and
|
|
35
|
-
the game subscribes through the shared input service so remapping remains a
|
|
36
|
-
personal GameHub preference.
|
|
37
|
-
The package's public state also states whether the current player may legally
|
|
38
|
-
play a card, keeping the action disabled during the final slap window after a
|
|
39
|
-
failed challenge. Its numeric countdown stays fixed at the right edge inside
|
|
40
|
-
the full-width timer track while the yellow fill passes beneath it, and the
|
|
41
|
-
complete timer scales with the action controls on narrow screens.
|
|
42
|
-
|
|
43
|
-
The package consumes an exact `@masonf539/game-sdk` release. Once both packages
|
|
44
|
-
are published, GameHub installs an exact Egyptian War version and enables it
|
|
45
|
-
through `gamehub.config.json`. GameHub's `npm run verify:packages` command
|
|
46
|
-
currently verifies that both packed artifacts work without monorepo source.
|
|
47
|
-
|
|
48
|
-
## Slap rules and penalties
|
|
49
|
-
|
|
50
|
-
Slap patterns are validated by the server and configured in the lobby. The
|
|
51
|
-
"Three in a Row" pattern checks the latest three cards for an ascending or
|
|
52
|
-
descending rank sequence, including Ace-low and Ace-high sequences. The
|
|
53
|
-
"Tens" pattern checks only two directly adjacent number cards that total ten;
|
|
54
|
-
an Ace counts as one. An active player who false-slaps forfeits the configured
|
|
55
|
-
number of cards, up to the number they hold. An eliminated player has no cards
|
|
56
|
-
to forfeit, so a false slap
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
1
|
+
# Egyptian War for GameHub
|
|
2
|
+
|
|
3
|
+
This repository contains the server-authoritative Egyptian War game plugin
|
|
4
|
+
for GameHub.
|
|
5
|
+
|
|
6
|
+
Egyptian War is designed to be installed and run by GameHub through the
|
|
7
|
+
`@masonf539/game-sdk` contracts. It is independently versioned, but it is not a
|
|
8
|
+
standalone web application and depends on GameHub for rooms, networking, the
|
|
9
|
+
lobby, shared settings, and browser hosting.
|
|
10
|
+
|
|
11
|
+
The package owns its rules, state engine, slap arbitration, tests, browser
|
|
12
|
+
presentation, game-specific audio, preview media, and design documentation.
|
|
13
|
+
GameHub supplies rooms, members, spectators, transport, and shared services.
|
|
14
|
+
|
|
15
|
+
`src/session.ts` owns the authoritative runtime lifecycle, including turn
|
|
16
|
+
timers, slap collection, pause/disconnect recovery, animations, member
|
|
17
|
+
removal, and completion. `public/client.js` owns the browser controller and
|
|
18
|
+
registers it with GameHub's generic client host.
|
|
19
|
+
|
|
20
|
+
Game-specific music, effects, and picker previews live under `public/assets`
|
|
21
|
+
and are served by GameHub from `/games/egyptian-war`.
|
|
22
|
+
The package-owned `public/register.js` registers those sounds with GameHub's
|
|
23
|
+
shared audio service.
|
|
24
|
+
The game board lives in `public/template.html`, and its presentation lives in
|
|
25
|
+
`public/style.css`. GameHub injects the fragment into a package-specific root;
|
|
26
|
+
the browser controller scopes its element lookups to that root.
|
|
27
|
+
During active play, the stylesheet fixes the board to the available viewport,
|
|
28
|
+
accounts for mobile safe areas, and keeps its compact Pause/connection row
|
|
29
|
+
below GameHub's Settings shortcut. Full Screen and confirmed Exit/Leave
|
|
30
|
+
actions remain platform-owned controls in GameHub's reusable Game tab. The
|
|
31
|
+
shortcut is mounted inside the active game viewport, while the Settings tabs
|
|
32
|
+
move into one horizontal row at medium and small widths.
|
|
33
|
+
Egyptian War declares `play-card` and `slap` input actions for GameHub's
|
|
34
|
+
package-aware Keybinds tab. Their defaults are E and Space respectively, and
|
|
35
|
+
the game subscribes through the shared input service so remapping remains a
|
|
36
|
+
personal GameHub preference.
|
|
37
|
+
The package's public state also states whether the current player may legally
|
|
38
|
+
play a card, keeping the action disabled during the final slap window after a
|
|
39
|
+
failed challenge. Its numeric countdown stays fixed at the right edge inside
|
|
40
|
+
the full-width timer track while the yellow fill passes beneath it, and the
|
|
41
|
+
complete timer scales with the action controls on narrow screens.
|
|
42
|
+
|
|
43
|
+
The package consumes an exact `@masonf539/game-sdk` release. Once both packages
|
|
44
|
+
are published, GameHub installs an exact Egyptian War version and enables it
|
|
45
|
+
through `gamehub.config.json`. GameHub's `npm run verify:packages` command
|
|
46
|
+
currently verifies that both packed artifacts work without monorepo source.
|
|
47
|
+
|
|
48
|
+
## Slap rules and penalties
|
|
49
|
+
|
|
50
|
+
Slap patterns are validated by the server and configured in the lobby. The
|
|
51
|
+
"Three in a Row" pattern checks the latest three cards for an ascending or
|
|
52
|
+
descending rank sequence, including Ace-low and Ace-high sequences. The
|
|
53
|
+
"Tens" pattern checks only two directly adjacent number cards that total ten;
|
|
54
|
+
an Ace counts as one. An active player who false-slaps forfeits the configured
|
|
55
|
+
number of cards, up to the number they hold. An eliminated player has no cards
|
|
56
|
+
to forfeit, so a false slap prevents them from slapping the unchanged pile
|
|
57
|
+
again. The cooldown clears when the next card is played, allowing that card to
|
|
58
|
+
be slapped and the eliminated player to re-enter immediately.
|
|
59
|
+
Invalid reentry attempts use the same visible one-hand slap animation as other
|
|
60
|
+
false slaps; additional attempts during the cooldown do not create extra hands.
|
|
61
|
+
|
|
62
|
+
## Installation preparation
|
|
63
|
+
|
|
64
|
+
The repository includes its own README, license, TypeScript settings, tests,
|
|
65
|
+
public assets, package metadata, and ignore rules. Its only GameHub code
|
|
66
|
+
dependency is the exact `@masonf539/game-sdk` version declared in `package.json`.
|
|
67
|
+
A lockfile will be generated after that SDK version is published so it records
|
|
68
|
+
the registry artifact rather than the former monorepo workspace link.
|
package/dist/definition.js
CHANGED
|
@@ -5,7 +5,7 @@ const game_sdk_1 = require("@masonf539/game-sdk");
|
|
|
5
5
|
exports.egyptianWar = {
|
|
6
6
|
id: "egyptian-war",
|
|
7
7
|
name: "Egyptian War",
|
|
8
|
-
description: "Play cards into a central pile, survive face-card challenges, and race to slap enabled patterns. False slaps carry penalties, and eliminated players must
|
|
8
|
+
description: "Play cards into a central pile, survive face-card challenges, and race to slap enabled patterns. False slaps carry penalties, and eliminated players must wait for another card before slapping again. The last player holding all the cards wins.",
|
|
9
9
|
isPlayable: true,
|
|
10
10
|
chatEnabled: true,
|
|
11
11
|
minPlayers: 2,
|
|
@@ -39,10 +39,10 @@ exports.egyptianWar = {
|
|
|
39
39
|
"If the challenged player uses every attempt without revealing a face card, Ace, or Joker, the player who most recently played a challenge card wins the pile unless the final card creates a valid slap combination.",
|
|
40
40
|
"When the final failed challenge card creates a valid slap combination, players get a short window of about four seconds to slap; if nobody slaps, the most recent challenge card player wins the pile.",
|
|
41
41
|
"If the challenged player runs out of cards before completing their attempts, the next player with cards inherits the challenge with the remaining attempts.",
|
|
42
|
-
"A player who runs out of cards is skipped until they reenter by winning a valid slap. An eliminated player who false-slaps
|
|
42
|
+
"A player who runs out of cards is skipped until they reenter by winning a valid slap. An eliminated player who false-slaps cannot slap the unchanged pile again, but may slap as soon as the next card is played.",
|
|
43
43
|
"A player who wins the pile places all of its cards at the bottom of their deck without shuffling and begins the next pile.",
|
|
44
44
|
"When a valid slap combination appears, the first player to slap wins the central pile.",
|
|
45
|
-
"Slapping without a valid combination forfeits the configured number of cards face down to the bottom of the pile, or all remaining cards if fewer remain. Penalty cards do not create slap combinations. A player with no cards forfeits no cards for a false slap
|
|
45
|
+
"Slapping without a valid combination forfeits the configured number of cards face down to the bottom of the pile, or all remaining cards if fewer remain. Penalty cards do not create slap combinations. A player with no cards forfeits no cards for a false slap and must wait for another card before trying again.",
|
|
46
46
|
"When only one player still has cards, that player wins unless the current face-up pile has a valid slap that allows an eliminated player to reenter.",
|
|
47
47
|
"If several players slap at nearly the same moment, the pile goes to one of them by weighted chance that favors the earlier slap. The others forfeit nothing.",
|
|
48
48
|
"The winner is the player who collects every card."
|
|
@@ -103,7 +103,7 @@ exports.egyptianWar = {
|
|
|
103
103
|
type: "number",
|
|
104
104
|
key: "falseSlapPenaltyCards",
|
|
105
105
|
label: "False Slap Penalty",
|
|
106
|
-
description: "Cards forfeited to the bottom of the pile for an invalid slap. An eliminated player forfeits no cards
|
|
106
|
+
description: "Cards forfeited to the bottom of the pile for an invalid slap. An eliminated player forfeits no cards and must wait for another card before slapping again.",
|
|
107
107
|
defaultValue: 2,
|
|
108
108
|
options: [1, 2, 3],
|
|
109
109
|
unit: "card"
|
package/dist/engine.d.ts
CHANGED
|
@@ -81,12 +81,12 @@ export type EgyptianWarDeparture = "removed" | "left";
|
|
|
81
81
|
export declare function removeEgyptianWarPlayer(state: EgyptianWarState, playerId: string, departure?: EgyptianWarDeparture): string;
|
|
82
82
|
export declare function isEgyptianWarPileSlappable(pile: readonly Card[], settings: EgyptianWarSettings): boolean;
|
|
83
83
|
export declare function isEgyptianWarStateSlappable(state: Pick<EgyptianWarState, "pile" | "penaltyPileCardCount" | "settings">): boolean;
|
|
84
|
-
export type EgyptianWarSlapCooldownStatus = "none" | "waiting"
|
|
85
|
-
/**
|
|
84
|
+
export type EgyptianWarSlapCooldownStatus = "none" | "waiting";
|
|
85
|
+
/** Prevent repeated free slaps until one more card has been played. */
|
|
86
86
|
export declare function startEgyptianWarSlapCooldown(state: EgyptianWarState, playerId: string): void;
|
|
87
87
|
/**
|
|
88
|
-
* Returns the status of an eliminated player's
|
|
89
|
-
*
|
|
88
|
+
* Returns the status of an eliminated player's slap cooldown. Once another
|
|
89
|
+
* card has been played, the cooldown clears and that new card may be slapped.
|
|
90
90
|
*/
|
|
91
91
|
export declare function checkEgyptianWarSlapCooldown(state: EgyptianWarState, playerId: string): EgyptianWarSlapCooldownStatus;
|
|
92
92
|
export declare function applyEgyptianWarAction(state: EgyptianWarState, playerId: string, action: EgyptianWarAction): string;
|
package/dist/engine.js
CHANGED
|
@@ -471,7 +471,7 @@ function trackPileVersion(state, run) {
|
|
|
471
471
|
}
|
|
472
472
|
}
|
|
473
473
|
}
|
|
474
|
-
/**
|
|
474
|
+
/** Prevent repeated free slaps until one more card has been played. */
|
|
475
475
|
function startEgyptianWarSlapCooldown(state, playerId) {
|
|
476
476
|
const player = state.players.find((candidate) => candidate.id === playerId);
|
|
477
477
|
if (!player)
|
|
@@ -479,8 +479,8 @@ function startEgyptianWarSlapCooldown(state, playerId) {
|
|
|
479
479
|
player.slapCooldownUntilCardPlayCount = (state.cardsPlayedCount ?? 0) + 1;
|
|
480
480
|
}
|
|
481
481
|
/**
|
|
482
|
-
* Returns the status of an eliminated player's
|
|
483
|
-
*
|
|
482
|
+
* Returns the status of an eliminated player's slap cooldown. Once another
|
|
483
|
+
* card has been played, the cooldown clears and that new card may be slapped.
|
|
484
484
|
*/
|
|
485
485
|
function checkEgyptianWarSlapCooldown(state, playerId) {
|
|
486
486
|
const player = state.players.find((candidate) => candidate.id === playerId);
|
|
@@ -491,7 +491,7 @@ function checkEgyptianWarSlapCooldown(state, playerId) {
|
|
|
491
491
|
if (cardsPlayedCount < cooldownTarget)
|
|
492
492
|
return "waiting";
|
|
493
493
|
player.slapCooldownUntilCardPlayCount = null;
|
|
494
|
-
return
|
|
494
|
+
return "none";
|
|
495
495
|
}
|
|
496
496
|
function applyEgyptianWarAction(state, playerId, action) {
|
|
497
497
|
return trackPileVersion(state, () => applyAction(state, playerId, action));
|
|
@@ -513,9 +513,6 @@ function applyAction(state, playerId, action) {
|
|
|
513
513
|
if (slapCooldown === "waiting") {
|
|
514
514
|
throw new EgyptianWarRuleError("You must wait until the next card is played before slapping again.");
|
|
515
515
|
}
|
|
516
|
-
if (slapCooldown === "blocked") {
|
|
517
|
-
throw new EgyptianWarRuleError("Your one-card slap cooldown blocked that slap.");
|
|
518
|
-
}
|
|
519
516
|
const faceUpPile = state.pile.slice(state.penaltyPileCardCount);
|
|
520
517
|
if (faceUpPile.length === 0) {
|
|
521
518
|
throw new EgyptianWarRuleError("There are no face-up cards to slap.");
|
package/dist/session.js
CHANGED
|
@@ -396,6 +396,14 @@ class EgyptianWarSession {
|
|
|
396
396
|
return;
|
|
397
397
|
}
|
|
398
398
|
const isLiveSlap = action === "slap" && !isTurnTimeout && !isResolvedSlap;
|
|
399
|
+
if (isLiveSlap &&
|
|
400
|
+
(0, engine_js_1.checkEgyptianWarSlapCooldown)(this.state, memberId) === "waiting") {
|
|
401
|
+
respond({
|
|
402
|
+
success: false,
|
|
403
|
+
message: "You must wait until the next card is played before slapping again."
|
|
404
|
+
});
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
399
407
|
if (this.isAnimating &&
|
|
400
408
|
!isResolvedSlap &&
|
|
401
409
|
!(isLiveSlap && this.animationAllowsSlaps)) {
|
|
@@ -403,22 +411,6 @@ class EgyptianWarSession {
|
|
|
403
411
|
return;
|
|
404
412
|
}
|
|
405
413
|
if (isLiveSlap) {
|
|
406
|
-
const slapCooldown = (0, engine_js_1.checkEgyptianWarSlapCooldown)(this.state, memberId);
|
|
407
|
-
if (slapCooldown === "waiting") {
|
|
408
|
-
respond({
|
|
409
|
-
success: false,
|
|
410
|
-
message: "You must wait until the next card is played before slapping again."
|
|
411
|
-
});
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
if (slapCooldown === "blocked") {
|
|
415
|
-
respond({
|
|
416
|
-
success: false,
|
|
417
|
-
message: "Your one-card slap cooldown blocked that slap."
|
|
418
|
-
});
|
|
419
|
-
this.context.broadcastState();
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
414
|
if ((0, engine_js_1.isEgyptianWarStateSlappable)(this.state)) {
|
|
423
415
|
this.collectSlap(memberId, respond);
|
|
424
416
|
return;
|
|
@@ -436,16 +428,6 @@ class EgyptianWarSession {
|
|
|
436
428
|
respond({ success: false, message: "Wait for the current action to finish." });
|
|
437
429
|
return;
|
|
438
430
|
}
|
|
439
|
-
const eliminatedPlayer = this.state.players.find((player) => player.id === memberId);
|
|
440
|
-
const faceUpCardCount = this.state.pile.length - this.state.penaltyPileCardCount;
|
|
441
|
-
if (faceUpCardCount > 0 && eliminatedPlayer?.cards.length === 0) {
|
|
442
|
-
(0, engine_js_1.startEgyptianWarSlapCooldown)(this.state, memberId);
|
|
443
|
-
respond({
|
|
444
|
-
success: false,
|
|
445
|
-
message: "You are out of cards and must wait until the next card is played before slapping again."
|
|
446
|
-
});
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
431
|
}
|
|
450
432
|
if (this.pendingSlaps !== null && !isResolvedSlap) {
|
|
451
433
|
respond({ success: false, message: "A slap is being resolved. Please wait." });
|
package/docs/design.md
CHANGED
|
@@ -1,126 +1,126 @@
|
|
|
1
|
-
# Egyptian War Design
|
|
2
|
-
|
|
3
|
-
Egyptian War is a versioned game plugin made for GameHub. The package owns the
|
|
4
|
-
game-specific server and browser behavior, while GameHub provides the room,
|
|
5
|
-
transport, lobby, spectator, and hosting services required to run it.
|
|
6
|
-
|
|
7
|
-
## Implementation status
|
|
8
|
-
|
|
9
|
-
This document distinguishes implemented behavior from planned gameplay. A checked item means the behavior exists in the application, not merely that its design has been selected.
|
|
10
|
-
Checked implementation items do not imply that every multiplayer interaction has completed manual gameplay testing; automated engine, arbitration, Socket.IO integration, and browser-animation tests run during the Docker build.
|
|
11
|
-
|
|
12
|
-
### Game availability
|
|
13
|
-
|
|
14
|
-
- [x] Keep Egyptian War visible so players can read its rules and configure its settings.
|
|
15
|
-
- [x] Represent playability in the shared game definition.
|
|
16
|
-
- [x] Disable the Start Game button and show a coming-soon status when the selected game is not playable.
|
|
17
|
-
- [x] Reject attempts to start an unplayable game on the server.
|
|
18
|
-
- [x] Mark Egyptian War playable after its server-authoritative game loop and client interface are implemented.
|
|
19
|
-
|
|
20
|
-
### Existing configuration
|
|
21
|
-
|
|
22
|
-
- [x] Support 2–6 players in the game definition.
|
|
23
|
-
- [x] Define standard cards and optional Jokers.
|
|
24
|
-
- [x] Configure one, two, or three complete decks, defaulting to one; include two Jokers per deck when Jokers are enabled.
|
|
25
|
-
- [x] Configure Jokers, doubles, sandwiches, three in a row, top-bottom, adjacent-card tens, and marriage as lobby settings.
|
|
26
|
-
- [x] Apply the saved settings to server-side slap validation.
|
|
27
|
-
- [x] Configure the false-slap penalty to 1, 2, or 3 cards, defaulting to 2.
|
|
28
|
-
- [x] Configure a 5–120 second player timer with a default of 15 seconds using synchronized slider and number controls.
|
|
29
|
-
- [x] Reset the active player's timer after they win a valid slap on their own turn.
|
|
30
|
-
|
|
31
|
-
### Server-authoritative gameplay
|
|
32
|
-
|
|
33
|
-
- [x] Implement server-side deck creation and cryptographic shuffling.
|
|
34
|
-
- [x] Implement face-down dealing and public state without hidden decks.
|
|
35
|
-
- [x] Implement random first-player selection.
|
|
36
|
-
- [x] Initialize the engine when a live room starts Egyptian War.
|
|
37
|
-
- [x] Enforce clockwise turns.
|
|
38
|
-
- [x] Implement Jack, Queen, King, Ace, and Joker challenge attempts (Jokers allow 5 attempts).
|
|
39
|
-
- [x] Award the pile to the most recent challenge-card player when a challenge fails, except while a valid final-attempt slap window is active.
|
|
40
|
-
- [x] Validate slaps on the server and resolve competing slaps fairly.
|
|
41
|
-
- [x] Use server receipt time for slap candidates so clients cannot improve their position by delaying timing acknowledgements. Collect requests for
|
|
42
|
-
- [x] Finish accepted slap arbitration before honoring a host pause, and allow candidates accepted before a disconnect-triggered pause to resolve instead of silently discarding them.
|
|
43
|
-
- [x] Skip players who run out of cards; continue the turn with the next eligible player.
|
|
44
|
-
- [x] Pass an unfinished challenge to the next eligible player with the remaining attempts unchanged when its responder runs out of cards.
|
|
45
|
-
- [x] Allow eliminated players to reenter through a valid slap.
|
|
46
|
-
- [x] Detect when one player owns every card and end the game.
|
|
47
|
-
- [x] Return the room to the lobby after the game ends.
|
|
48
|
-
- [x] Pause the active game when a player disconnects; allow the host to resume while the player is away, and restore the saved pause preference after they reconnect. Lobby-only disconnects still expire after two minutes.
|
|
49
|
-
- [x] Gray out disconnected players' table seats and show a yellow disconnected alert beneath each seat.
|
|
50
|
-
- [x] Provide a host-only Close Lobby control in the lobby and expose confirmed Exit Game or Leave Game actions through GameHub's reusable Game settings tab.
|
|
51
|
-
- [x] Hide the room code by default for non-host players while showing it by default to the host.
|
|
52
|
-
- [x] Preserve a reconnecting active player's remaining turn time and allow one short grace period per turn, capped at the configured turn duration, then continue normal automatic turn play if the timer expires.
|
|
53
|
-
- [x] Send recent face-up cards in reconnect snapshots so the board restores the visible pile after reconnecting.
|
|
54
|
-
- [x] Reject slap actions when there are no face-up cards available to slap, including immediately after a pile transfer or reconnect.
|
|
55
|
-
- [x] Let the host kick a disconnected player during a game; hold their cards face-down until the current pile is awarded, then add them to the next pile.
|
|
56
|
-
- [x] Tell a kicked player they were removed after disconnecting if they later try to reconnect, and notify the table when their cards are deferred.
|
|
57
|
-
- [x] Play the game-winner animation when kicking opponents leaves the host as the last player.
|
|
58
|
-
- [x] Keep slap actions available throughout active play, including for players out of cards, while rejecting actions during pile-transfer, penalty, and win animations and during pauses. Slaps are still collected during the short card-play animation, because the card is already on the pile.
|
|
59
|
-
- [x] Penalize an invalid slap by moving the configured number of the player's cards to the bottom of the pile; forfeit any remaining cards if fewer than the configured penalty remain.
|
|
60
|
-
- [x] Keep face-down false-slap penalty cards out of slap-pattern validation so repeated penalties cannot manufacture a valid slap.
|
|
61
|
-
- [x] End the game when only one player has cards and the face-up pile offers no valid slap for an eliminated player to reenter.
|
|
62
|
-
- [x] Never let a challenger answer their own challenge: when nobody but the challenger can continue, the challenger wins the pile (or the slap window opens).
|
|
63
|
-
- [x]
|
|
64
|
-
- [x] Keep slap-pattern information off the wire: the public state carries a `pileVersion` but no slappable flag, and messages about a pending pile are neutral.
|
|
65
|
-
- [x] Have slaps carry the `pileVersion` the player last saw. A slap that lost a short race with a card play (previous version, previous pile slappable, under 350 ms) is ignored without a penalty.
|
|
66
|
-
- [x] Allow a player to leave voluntarily; their cards are held face-down and added to the next pile, just like a host removal. If removing a player leaves nobody holding cards, the remaining cards go to the next player in turn order immediately.
|
|
67
|
-
- [x] Allow only the host to pause and resume the game; enforce pause state on the server.
|
|
68
|
-
- [x] Support per-game chat capability; validate room membership, message length, and send rate on the server.
|
|
69
|
-
- [x] Preserve a final played card and winner state long enough for the win animation before returning to the lobby.
|
|
70
|
-
- [x] Enforce the player timer on the server and pause it during host pauses and action animations; add a short reconnect grace period, then auto-play on timeout.
|
|
71
|
-
- [x] When the final failed challenge card creates a valid slap, allow slaps for a short fixed window (four seconds, not a whole turn) before awarding the pile to the challenge owner.
|
|
72
|
-
- [x] Let the host end an active game and return everyone to the lobby after confirming in the browser.
|
|
73
|
-
|
|
74
|
-
### Client gameplay
|
|
75
|
-
|
|
76
|
-
- [x] Keep active gameplay fixed to the usable mobile viewport so browser chrome and the on-screen keyboard cannot push the board off-screen.
|
|
77
|
-
- [x] Reserve space for GameHub's compact Settings shortcut above the package-owned Pause and connection-latency row on narrow screens.
|
|
78
|
-
- [x] Render each player's public state without revealing hidden cards.
|
|
79
|
-
- [x] Render the central pile and the most recently played card.
|
|
80
|
-
- [x] Provide server-validated play-card and slap actions.
|
|
81
|
-
- [x] Show turn, challenge, elimination, pile-winner, and game-winner updates.
|
|
82
|
-
- [x] Replace the separate player list with a responsive table arena showing each player's avatar, name, and card count around the table.
|
|
83
|
-
- [x] Render physical-looking cards and animate played cards into the central pile.
|
|
84
|
-
- [x] Place play-card and slap controls immediately below the arena.
|
|
85
|
-
- [x] Display action and challenge messages on the table rather than below it.
|
|
86
|
-
- [x] Show late joiners in a dedicated in-game spectator list, including connection status and host removal controls.
|
|
87
|
-
- [x] Preserve spectator-list updates that arrive while the game client mounts so each spectator sees themselves, allow spectators to return to the lobby through a dedicated confirmation, and offer lobby waiters a Spectate Game action while play remains active.
|
|
88
|
-
- [x] Lay out each player avatar beside their name and card count, with an underline beneath the player details.
|
|
89
|
-
- [x] Highlight the entire current player's seat.
|
|
90
|
-
- [x] Animate cards moving from the pile into the pile winner's area and outline the winner in gold.
|
|
91
|
-
- [x] Animate
|
|
92
|
-
- [x] Animate and retain the final played card while highlighting the game winner in gold.
|
|
93
|
-
- [x] Place a host-only pause/resume button at the upper-right of the gameplay panel.
|
|
94
|
-
- [x] Place a host-only Close Lobby control in the lobby and route the Game settings tab's Exit/Leave action through the appropriate in-app confirmation modal.
|
|
95
|
-
- [x] Put a host-only Kick button beside a disconnected player's table alert.
|
|
96
|
-
- [x] Make the slap-rules list collapsible.
|
|
97
|
-
- [x] On narrow screens, place the collapsed rules Show control directly beside the Slap Rules label without changing the expanded panel or Hide control.
|
|
98
|
-
- [x] Show enabled slap patterns and the false-slap penalty in the upper-left of the gameplay panel.
|
|
99
|
-
- [x] Keep turn-state messages neutral; do not announce when the current pile matches a slap pattern.
|
|
100
|
-
- [x] Show a chat panel only for games whose definitions enable chat.
|
|
101
|
-
- [x] Show the synchronized turn countdown below the table while reserving its layout space when hidden.
|
|
102
|
-
- [x] Keep the current whole seconds stationary at the right inside the timer track above its moving fill, align the timer to both action-button edges, and scale the complete control group together on narrow screens.
|
|
103
|
-
- [x] Decorate the countdown's uncovered track with a blue gradient and progressively revealed sleep marks, ending with a sleeping bear, without changing authoritative timer behavior.
|
|
104
|
-
- [x] Show each browser its latest display-only network round-trip time in the upper-right; never use that client-acknowledged measurement for slap arbitration.
|
|
105
|
-
- [x] Show server connection state as a colored dot with status text on the main menu, in the lobby, and beside the gameplay controls.
|
|
106
|
-
- [x] Fit active gameplay into the browser viewport without page scrolling; overlay Chat and Spectators in one wide bottom-left tab panel with an explicit viewport-relative Expand/Collapse control; place the timer and same-level action buttons in the open bottom-right area; and show turn text directly above the central pile count.
|
|
107
|
-
- [x] Let the Chat/Spectators panel collapse into a small round chat button: it opens at medium size by default, the arrow switches between medium and large, and a small diagonal-arrow button on the panel's corner collapses it to the circle. Reopening restores the size it had before it was collapsed, a new game starts at medium again, and the circle shows an unread count (capped at 9+) for messages from other players that arrived while it was collapsed.
|
|
108
|
-
- [x] Give the side-panel expand and minimize controls separate, lighter utility-button surfaces so they remain visually distinct from the Chat and Spectators tabs.
|
|
109
|
-
- [x] Keep the collapsed Slap Rules Show button in the top-right while shrinking the surrounding box around its title at every viewport size, then restore the full rules width when expanded.
|
|
110
|
-
- [x] Style "Play a Card" and "Slap" buttons as prominent game-action controls, with a secondary "Bring the pain" label and fire accents on Slap.
|
|
111
|
-
- [x] Keep slaps available during active play even when no pattern matches; disable them only when no face-up cards are present, the game is paused or finished, or a non-card-play animation is in progress.
|
|
112
|
-
- [x] Disable Play a Card for every player while a completed challenge is waiting through its final slap window, even though the next player remains identified in public state.
|
|
113
|
-
- [x] Restore active players from a complete reconnect snapshot.
|
|
114
|
-
- [x] Reuse the limited public game-state projection for spectators so they receive the visible pile, public player state, timers, and live animations without hidden decks.
|
|
115
|
-
- [x] Use game-specific background music and synchronize card, slap, pile-win, and game-win sounds with their corresponding presentation animations.
|
|
116
|
-
- [x] Expose persistent, independently adjustable music, lobby alert, and gameplay volume controls through the personal GameHub Settings Audio tab (0% disables a category), and wait for browser-approved user interaction before starting music.
|
|
117
|
-
|
|
118
|
-
### Table presentation
|
|
119
|
-
|
|
120
|
-
- [x] Draw the table with CSS until a project-owned table image is available.
|
|
121
|
-
- [x] Give the CSS table a layered casino-felt, wooden-rail, depth, and drop-shadow treatment while retaining its original oval shape.
|
|
122
|
-
- [x] Draw cards as accessible HTML/CSS playing-card faces; do not depend on external card image assets.
|
|
123
|
-
- [x] Render mirrored card corners, distinct face-card styling, and a Joker with vertical JOKER labels and a decorative center that is distinguishable from a Jack.
|
|
124
|
-
- [x]
|
|
125
|
-
- [x] Keep the table, player seats, card motion, spectator area, and controls usable on narrow screens and with reduced-motion preferences.
|
|
126
|
-
- Offer chat per game rather than requiring it in every game; games that rely on secrecy or fast rounds can opt out, while social multiplayer games can enable it.
|
|
1
|
+
# Egyptian War Design
|
|
2
|
+
|
|
3
|
+
Egyptian War is a versioned game plugin made for GameHub. The package owns the
|
|
4
|
+
game-specific server and browser behavior, while GameHub provides the room,
|
|
5
|
+
transport, lobby, spectator, and hosting services required to run it.
|
|
6
|
+
|
|
7
|
+
## Implementation status
|
|
8
|
+
|
|
9
|
+
This document distinguishes implemented behavior from planned gameplay. A checked item means the behavior exists in the application, not merely that its design has been selected.
|
|
10
|
+
Checked implementation items do not imply that every multiplayer interaction has completed manual gameplay testing; automated engine, arbitration, Socket.IO integration, and browser-animation tests run during the Docker build.
|
|
11
|
+
|
|
12
|
+
### Game availability
|
|
13
|
+
|
|
14
|
+
- [x] Keep Egyptian War visible so players can read its rules and configure its settings.
|
|
15
|
+
- [x] Represent playability in the shared game definition.
|
|
16
|
+
- [x] Disable the Start Game button and show a coming-soon status when the selected game is not playable.
|
|
17
|
+
- [x] Reject attempts to start an unplayable game on the server.
|
|
18
|
+
- [x] Mark Egyptian War playable after its server-authoritative game loop and client interface are implemented.
|
|
19
|
+
|
|
20
|
+
### Existing configuration
|
|
21
|
+
|
|
22
|
+
- [x] Support 2–6 players in the game definition.
|
|
23
|
+
- [x] Define standard cards and optional Jokers.
|
|
24
|
+
- [x] Configure one, two, or three complete decks, defaulting to one; include two Jokers per deck when Jokers are enabled.
|
|
25
|
+
- [x] Configure Jokers, doubles, sandwiches, three in a row, top-bottom, adjacent-card tens, and marriage as lobby settings.
|
|
26
|
+
- [x] Apply the saved settings to server-side slap validation.
|
|
27
|
+
- [x] Configure the false-slap penalty to 1, 2, or 3 cards, defaulting to 2.
|
|
28
|
+
- [x] Configure a 5–120 second player timer with a default of 15 seconds using synchronized slider and number controls.
|
|
29
|
+
- [x] Reset the active player's timer after they win a valid slap on their own turn.
|
|
30
|
+
|
|
31
|
+
### Server-authoritative gameplay
|
|
32
|
+
|
|
33
|
+
- [x] Implement server-side deck creation and cryptographic shuffling.
|
|
34
|
+
- [x] Implement face-down dealing and public state without hidden decks.
|
|
35
|
+
- [x] Implement random first-player selection.
|
|
36
|
+
- [x] Initialize the engine when a live room starts Egyptian War.
|
|
37
|
+
- [x] Enforce clockwise turns.
|
|
38
|
+
- [x] Implement Jack, Queen, King, Ace, and Joker challenge attempts (Jokers allow 5 attempts).
|
|
39
|
+
- [x] Award the pile to the most recent challenge-card player when a challenge fails, except while a valid final-attempt slap window is active.
|
|
40
|
+
- [x] Validate slaps on the server and resolve competing slaps fairly.
|
|
41
|
+
- [x] Use server receipt time for slap candidates so clients cannot improve their position by delaying timing acknowledgements. Collect requests for 300 ms, assign a fixed server-controlled 25 ms uncertainty to each candidate, compare candidates within the resulting 50 ms window, and select eligible candidates with smooth exponential weighting driven by Node's cryptographically secure random source.
|
|
42
|
+
- [x] Finish accepted slap arbitration before honoring a host pause, and allow candidates accepted before a disconnect-triggered pause to resolve instead of silently discarding them.
|
|
43
|
+
- [x] Skip players who run out of cards; continue the turn with the next eligible player.
|
|
44
|
+
- [x] Pass an unfinished challenge to the next eligible player with the remaining attempts unchanged when its responder runs out of cards.
|
|
45
|
+
- [x] Allow eliminated players to reenter through a valid slap.
|
|
46
|
+
- [x] Detect when one player owns every card and end the game.
|
|
47
|
+
- [x] Return the room to the lobby after the game ends.
|
|
48
|
+
- [x] Pause the active game when a player disconnects; allow the host to resume while the player is away, and restore the saved pause preference after they reconnect. Lobby-only disconnects still expire after two minutes.
|
|
49
|
+
- [x] Gray out disconnected players' table seats and show a yellow disconnected alert beneath each seat.
|
|
50
|
+
- [x] Provide a host-only Close Lobby control in the lobby and expose confirmed Exit Game or Leave Game actions through GameHub's reusable Game settings tab.
|
|
51
|
+
- [x] Hide the room code by default for non-host players while showing it by default to the host.
|
|
52
|
+
- [x] Preserve a reconnecting active player's remaining turn time and allow one short grace period per turn, capped at the configured turn duration, then continue normal automatic turn play if the timer expires.
|
|
53
|
+
- [x] Send recent face-up cards in reconnect snapshots so the board restores the visible pile after reconnecting.
|
|
54
|
+
- [x] Reject slap actions when there are no face-up cards available to slap, including immediately after a pile transfer or reconnect.
|
|
55
|
+
- [x] Let the host kick a disconnected player during a game; hold their cards face-down until the current pile is awarded, then add them to the next pile.
|
|
56
|
+
- [x] Tell a kicked player they were removed after disconnecting if they later try to reconnect, and notify the table when their cards are deferred.
|
|
57
|
+
- [x] Play the game-winner animation when kicking opponents leaves the host as the last player.
|
|
58
|
+
- [x] Keep slap actions available throughout active play, including for players out of cards, while rejecting actions during pile-transfer, penalty, and win animations and during pauses. Slaps are still collected during the short card-play animation, because the card is already on the pile.
|
|
59
|
+
- [x] Penalize an invalid slap by moving the configured number of the player's cards to the bottom of the pile; forfeit any remaining cards if fewer than the configured penalty remain.
|
|
60
|
+
- [x] Keep face-down false-slap penalty cards out of slap-pattern validation so repeated penalties cannot manufacture a valid slap.
|
|
61
|
+
- [x] End the game when only one player has cards and the face-up pile offers no valid slap for an eliminated player to reenter.
|
|
62
|
+
- [x] Never let a challenger answer their own challenge: when nobody but the challenger can continue, the challenger wins the pile (or the slap window opens).
|
|
63
|
+
- [x] Prevent an eliminated player who false-slaps from repeatedly slapping the unchanged pile; clear that cooldown when the next card is played so they can immediately slap back in.
|
|
64
|
+
- [x] Keep slap-pattern information off the wire: the public state carries a `pileVersion` but no slappable flag, and messages about a pending pile are neutral.
|
|
65
|
+
- [x] Have slaps carry the `pileVersion` the player last saw. A slap that lost a short race with a card play (previous version, previous pile slappable, under 350 ms) is ignored without a penalty.
|
|
66
|
+
- [x] Allow a player to leave voluntarily; their cards are held face-down and added to the next pile, just like a host removal. If removing a player leaves nobody holding cards, the remaining cards go to the next player in turn order immediately.
|
|
67
|
+
- [x] Allow only the host to pause and resume the game; enforce pause state on the server.
|
|
68
|
+
- [x] Support per-game chat capability; validate room membership, message length, and send rate on the server.
|
|
69
|
+
- [x] Preserve a final played card and winner state long enough for the win animation before returning to the lobby.
|
|
70
|
+
- [x] Enforce the player timer on the server and pause it during host pauses and action animations; add a short reconnect grace period, then auto-play on timeout.
|
|
71
|
+
- [x] When the final failed challenge card creates a valid slap, allow slaps for a short fixed window (four seconds, not a whole turn) before awarding the pile to the challenge owner.
|
|
72
|
+
- [x] Let the host end an active game and return everyone to the lobby after confirming in the browser.
|
|
73
|
+
|
|
74
|
+
### Client gameplay
|
|
75
|
+
|
|
76
|
+
- [x] Keep active gameplay fixed to the usable mobile viewport so browser chrome and the on-screen keyboard cannot push the board off-screen.
|
|
77
|
+
- [x] Reserve space for GameHub's compact Settings shortcut above the package-owned Pause and connection-latency row on narrow screens.
|
|
78
|
+
- [x] Render each player's public state without revealing hidden cards.
|
|
79
|
+
- [x] Render the central pile and the most recently played card.
|
|
80
|
+
- [x] Provide server-validated play-card and slap actions.
|
|
81
|
+
- [x] Show turn, challenge, elimination, pile-winner, and game-winner updates.
|
|
82
|
+
- [x] Replace the separate player list with a responsive table arena showing each player's avatar, name, and card count around the table.
|
|
83
|
+
- [x] Render physical-looking cards and animate played cards into the central pile.
|
|
84
|
+
- [x] Place play-card and slap controls immediately below the arena.
|
|
85
|
+
- [x] Display action and challenge messages on the table rather than below it.
|
|
86
|
+
- [x] Show late joiners in a dedicated in-game spectator list, including connection status and host removal controls.
|
|
87
|
+
- [x] Preserve spectator-list updates that arrive while the game client mounts so each spectator sees themselves, allow spectators to return to the lobby through a dedicated confirmation, and offer lobby waiters a Spectate Game action while play remains active.
|
|
88
|
+
- [x] Lay out each player avatar beside their name and card count, with an underline beneath the player details.
|
|
89
|
+
- [x] Highlight the entire current player's seat.
|
|
90
|
+
- [x] Animate cards moving from the pile into the pile winner's area and outline the winner in gold.
|
|
91
|
+
- [x] Animate one hand moving from the slapper's seat to the pile for valid and invalid slap attempts, including invalid reentry attempts by eliminated players; suppress extra hands while their cooldown is active.
|
|
92
|
+
- [x] Animate and retain the final played card while highlighting the game winner in gold.
|
|
93
|
+
- [x] Place a host-only pause/resume button at the upper-right of the gameplay panel.
|
|
94
|
+
- [x] Place a host-only Close Lobby control in the lobby and route the Game settings tab's Exit/Leave action through the appropriate in-app confirmation modal.
|
|
95
|
+
- [x] Put a host-only Kick button beside a disconnected player's table alert.
|
|
96
|
+
- [x] Make the slap-rules list collapsible.
|
|
97
|
+
- [x] On narrow screens, place the collapsed rules Show control directly beside the Slap Rules label without changing the expanded panel or Hide control.
|
|
98
|
+
- [x] Show enabled slap patterns and the false-slap penalty in the upper-left of the gameplay panel.
|
|
99
|
+
- [x] Keep turn-state messages neutral; do not announce when the current pile matches a slap pattern.
|
|
100
|
+
- [x] Show a chat panel only for games whose definitions enable chat.
|
|
101
|
+
- [x] Show the synchronized turn countdown below the table while reserving its layout space when hidden.
|
|
102
|
+
- [x] Keep the current whole seconds stationary at the right inside the timer track above its moving fill, align the timer to both action-button edges, and scale the complete control group together on narrow screens.
|
|
103
|
+
- [x] Decorate the countdown's uncovered track with a blue gradient and progressively revealed sleep marks, ending with a sleeping bear, without changing authoritative timer behavior.
|
|
104
|
+
- [x] Show each browser its latest display-only network round-trip time in the upper-right; never use that client-acknowledged measurement for slap arbitration.
|
|
105
|
+
- [x] Show server connection state as a colored dot with status text on the main menu, in the lobby, and beside the gameplay controls.
|
|
106
|
+
- [x] Fit active gameplay into the browser viewport without page scrolling; overlay Chat and Spectators in one wide bottom-left tab panel with an explicit viewport-relative Expand/Collapse control; place the timer and same-level action buttons in the open bottom-right area; and show turn text directly above the central pile count.
|
|
107
|
+
- [x] Let the Chat/Spectators panel collapse into a small round chat button: it opens at medium size by default, the arrow switches between medium and large, and a small diagonal-arrow button on the panel's corner collapses it to the circle. Reopening restores the size it had before it was collapsed, a new game starts at medium again, and the circle shows an unread count (capped at 9+) for messages from other players that arrived while it was collapsed.
|
|
108
|
+
- [x] Give the side-panel expand and minimize controls separate, lighter utility-button surfaces so they remain visually distinct from the Chat and Spectators tabs.
|
|
109
|
+
- [x] Keep the collapsed Slap Rules Show button in the top-right while shrinking the surrounding box around its title at every viewport size, then restore the full rules width when expanded.
|
|
110
|
+
- [x] Style "Play a Card" and "Slap" buttons as prominent game-action controls, with a secondary "Bring the pain" label and fire accents on Slap.
|
|
111
|
+
- [x] Keep slaps available during active play even when no pattern matches; disable them only when no face-up cards are present, the game is paused or finished, or a non-card-play animation is in progress.
|
|
112
|
+
- [x] Disable Play a Card for every player while a completed challenge is waiting through its final slap window, even though the next player remains identified in public state.
|
|
113
|
+
- [x] Restore active players from a complete reconnect snapshot.
|
|
114
|
+
- [x] Reuse the limited public game-state projection for spectators so they receive the visible pile, public player state, timers, and live animations without hidden decks.
|
|
115
|
+
- [x] Use game-specific background music and synchronize card, slap, pile-win, and game-win sounds with their corresponding presentation animations.
|
|
116
|
+
- [x] Expose persistent, independently adjustable music, lobby alert, and gameplay volume controls through the personal GameHub Settings Audio tab (0% disables a category), and wait for browser-approved user interaction before starting music.
|
|
117
|
+
|
|
118
|
+
### Table presentation
|
|
119
|
+
|
|
120
|
+
- [x] Draw the table with CSS until a project-owned table image is available.
|
|
121
|
+
- [x] Give the CSS table a layered casino-felt, wooden-rail, depth, and drop-shadow treatment while retaining its original oval shape.
|
|
122
|
+
- [x] Draw cards as accessible HTML/CSS playing-card faces; do not depend on external card image assets.
|
|
123
|
+
- [x] Render mirrored card corners, distinct face-card styling, and a Joker with vertical JOKER labels and a decorative center that is distinguishable from a Jack.
|
|
124
|
+
- [x] Render slap hands with a package-owned palm graphic for consistent mobile orientation, scale them and their stopping distance with the rendered card size, keep their shadows visually below the hand at every seat rotation, and layer later collected attempts above the official winner.
|
|
125
|
+
- [x] Keep the table, player seats, card motion, spectator area, and controls usable on narrow screens and with reduced-motion preferences.
|
|
126
|
+
- Offer chat per game rather than requiring it in every game; games that rely on secrecy or fast rounds can opt out, while social multiplayer games can enable it.
|