feat(games): implement blackjack game plugin with manual start and custom payouts
Some checks failed
Deploy to Production / test (push) Failing after 39s
Some checks failed
Deploy to Production / test (push) Failing after 39s
Adds a full blackjack game with dealer AI, hit/stand/double-down actions, and per-player payout multipliers (house-edge model). Extends the game framework with manualStart support and a START_GAME WebSocket message so hosts can begin when ready. Generalizes bet settlement transaction descriptions from chess-specific to game-agnostic. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,7 +42,7 @@ export function GameRoom({ userId, role }: { userId: string; role?: string }) {
|
||||
|
||||
const {
|
||||
gameState, players, spectators, roomStatus,
|
||||
isSpectator, gameOver, error, sendAction, leaveRoom, sessionReplaced, rejoin, fillRoom, roomOptions,
|
||||
isSpectator, gameOver, error, sendAction, leaveRoom, sessionReplaced, rejoin, fillRoom, startGame, roomOptions,
|
||||
} = useGameRoom(roomId!, userId, role, preferAs);
|
||||
|
||||
const betAmount = roomOptions.betAmount ?? 0;
|
||||
@@ -104,7 +104,7 @@ export function GameRoom({ userId, role }: { userId: string; role?: string }) {
|
||||
{isSpectator && <span className="text-text-disabled">Spectating</span>}
|
||||
{betAmount > 0 && (
|
||||
<span className="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-semibold bg-warning/15 text-warning">
|
||||
{betAmount * 2} AU pot
|
||||
{betAmount} AU{players.length > 1 ? `/player` : ""}
|
||||
</span>
|
||||
)}
|
||||
<span>👁 {spectators.length}</span>
|
||||
@@ -173,8 +173,9 @@ export function GameRoom({ userId, role }: { userId: string; role?: string }) {
|
||||
<div className="text-sm font-semibold mb-4 text-center">
|
||||
Waiting for players ({players.length}/{plugin.maxPlayers})
|
||||
</div>
|
||||
<div className="flex gap-3 justify-center mb-6">
|
||||
{Array.from({ length: plugin.maxPlayers }).map((_, i) => {
|
||||
<div className="flex gap-3 justify-center mb-6 flex-wrap">
|
||||
{Array.from({ length: Math.max(players.length + 1, plugin.minPlayers ?? 1, Math.min(plugin.maxPlayers, 4)) }).map((_, i) => {
|
||||
if (i >= plugin.maxPlayers) return null;
|
||||
const player = players[i];
|
||||
return (
|
||||
<div key={i} className={`flex flex-col items-center gap-2 px-4 py-3 rounded-xl ${player ? "bg-primary/10" : "bg-surface"}`}>
|
||||
@@ -192,6 +193,14 @@ export function GameRoom({ userId, role }: { userId: string; role?: string }) {
|
||||
})}
|
||||
</div>
|
||||
<CopyInviteLink url={window.location.href} />
|
||||
{plugin.manualStart && players.length >= (plugin.minPlayers ?? 1) && players[0]?.discordId === userId && (
|
||||
<button
|
||||
onClick={startGame}
|
||||
className="mt-4 w-full max-w-sm mx-auto block rounded-xl bg-primary text-on-primary px-4 py-2.5 text-sm font-label font-semibold hover:opacity-90 transition-colors"
|
||||
>
|
||||
Start Game ({players.length} player{players.length !== 1 ? "s" : ""})
|
||||
</button>
|
||||
)}
|
||||
{role === "admin" && players.length < plugin.maxPlayers && (
|
||||
<button
|
||||
onClick={fillRoom}
|
||||
|
||||
Reference in New Issue
Block a user