feat(games): refactor blackjack for continuous play, split/double, and table UI
Some checks failed
Deploy to Production / test (push) Failing after 32s

Transform blackjack from single-round to continuous-play table sessions with
round lifecycle (betting → playing → resolved → betting), split/double down
actions, per-hand bet tracking, leave/join table mid-session, and a responsive
felt-style table UI with arc-positioned player seats.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
syntaxbullet
2026-04-06 12:41:49 +02:00
parent ef78a85b9c
commit a36c05994c
12 changed files with 1907 additions and 554 deletions

View File

@@ -7,6 +7,10 @@ interface PlayerInfo {
username: string;
}
interface RoundResult {
payouts: Record<string, { net: number }>;
}
interface GameRoomState {
gameState: unknown;
players: PlayerInfo[];
@@ -14,6 +18,7 @@ interface GameRoomState {
roomStatus: "connecting" | "waiting" | "playing" | "finished" | "not_found";
isSpectator: boolean;
gameOver: { winner: string | null; reason: string; payout?: { amount: number; refunded?: boolean } } | null;
roundResult: RoundResult | null;
error: string | null;
sessionReplaced: boolean;
roomOptions: { betAmount?: number };
@@ -36,6 +41,7 @@ export function useGameRoom(roomId: string, userId: string, role?: string, prefe
roomStatus: "connecting",
isSpectator: false,
gameOver: null,
roundResult: null,
error: null,
sessionReplaced: false,
roomOptions: {},
@@ -64,11 +70,17 @@ export function useGameRoom(roomId: string, userId: string, role?: string, prefe
case "GAME_STATE":
// Authoritative player view — sent directly to this player
setState(prev => ({
...prev,
gameState: msg.state,
roomStatus: prev.roomStatus === "finished" ? "finished" : "playing",
}));
setState(prev => {
// Clear round result when a new betting phase starts
const phase = (msg.state as any)?.phase;
const roundResult = phase === "betting" ? null : prev.roundResult;
return {
...prev,
gameState: msg.state,
roundResult,
roomStatus: prev.roomStatus === "finished" ? "finished" : "playing",
};
});
break;
case "GAME_STARTED":
@@ -116,6 +128,13 @@ export function useGameRoom(roomId: string, userId: string, role?: string, prefe
}));
break;
case "ROUND_SETTLED":
setState(prev => ({
...prev,
roundResult: { payouts: msg.payouts },
}));
break;
case "GAME_ENDED":
setState(prev => ({
...prev,