fix: correct blackjack PnL calculation and enhance UI
Some checks failed
Deploy to Production / test (push) Failing after 35s

- Fix incorrect PnL calculations where multipliers were treated as amounts
- Add proper net profit calculation: (multiplier × bet) - bet
- Update UI with gradient backgrounds, icons, and improved animations
- Add slide-in and pulse-slow keyframe animations
- Enhance dealer area with status-based styling
- Improve action buttons with colored gradients and hover effects
- Revise round result banner with better visual hierarchy
This commit is contained in:
syntaxbullet
2026-04-06 15:11:47 +02:00
parent 06c3891045
commit 034f2ead1c
4 changed files with 178 additions and 86 deletions

View File

@@ -95,13 +95,14 @@ export class GameServer {
const gameName = gameRegistry.get(room.gameSlug)?.name ?? "Game"; const gameName = gameRegistry.get(room.gameSlug)?.name ?? "Game";
const payoutDetails: Record<string, { net: number }> = {}; const payoutDetails: Record<string, { net: number }> = {};
for (const [playerId, grossPayout] of Object.entries(roundPayouts)) { for (const [playerId, multiplier] of Object.entries(roundPayouts)) {
// roundPayout is already the gross payout amount (not a multiplier) // roundPayout contains multipliers (e.g., win=2, blackjack=2.5, push=1)
const netProfit = Math.floor(grossPayout) - betAmount; // Calculate net profit (gross payout minus original bet) const grossAmount = Math.floor(betAmount * multiplier);
const netProfit = grossAmount - betAmount;
try { try {
await economyService.modifyUserBalance( await economyService.modifyUserBalance(
playerId, playerId,
BigInt(grossPayout), BigInt(grossAmount),
TransactionType.GAME_WIN, TransactionType.GAME_WIN,
`${gameName} round payout (room ${roomId.slice(0, 8)})`, `${gameName} round payout (room ${roomId.slice(0, 8)})`,
); );

View File

@@ -1,6 +1,6 @@
import { useMemo, useState, useEffect, useCallback } from "react"; import { useMemo, useState, useEffect, useCallback } from "react";
import type { GameUIProps } from "../registry"; import type { GameUIProps } from "../registry";
import { Hand, Square, ArrowLeftRight, ChevronsUp, LogOut, Check, Coins } from "lucide-react"; import { Hand, Square, ArrowLeftRight, ChevronsUp, LogOut, Check, Coins, Trophy, AlertCircle } from "lucide-react";
// ── Types matching server views ── // ── Types matching server views ──
@@ -127,18 +127,18 @@ function CardFan({ cards, faceDown, size = "normal" }: {
); );
} }
// ── Value badge ── // ── Value badge ---
function ValueBadge({ value, status }: { value: number; status?: string }) { function ValueBadge({ value, status }: { value: number; status?: string }) {
const colorClass = const colorClass =
value === 21 || status === "blackjack" value === 21 || status === "blackjack"
? "bg-emerald-500/20 text-emerald-400 font-bold" ? "bg-gradient-to-r from-emerald-500 to-emerald-600 text-white font-bold shadow-sm"
: value > 21 || status === "bust" : value > 21 || status === "bust"
? "bg-red-500/20 text-red-400 font-bold" ? "bg-gradient-to-r from-red-500 to-red-600 text-white font-bold shadow-sm"
: "bg-black/30 text-white/80"; : "bg-white/10 backdrop-blur-sm text-white border border-white/20";
return ( return (
<span className={`text-[10px] font-mono px-1.5 py-0.5 rounded ${colorClass}`}> <span className={`text-[10px] font-mono px-2 py-0.5 rounded-full ${colorClass}`}>
{value} {value}
</span> </span>
); );
@@ -147,35 +147,52 @@ function ValueBadge({ value, status }: { value: number; status?: string }) {
// ── Result badge ── // ── Result badge ──
function ResultBadge({ result, reason }: { result: string; reason: string | null }) { function ResultBadge({ result, reason }: { result: string; reason: string | null }) {
const config: Record<string, { label: string; color: string }> = { const config: Record<string, { label: string; color: string; icon?: React.ReactNode }> = {
blackjack: { label: "Blackjack!", color: "bg-yellow-500/20 text-yellow-300" }, blackjack: {
win: { label: "Win", color: "bg-emerald-500/20 text-emerald-400" }, label: "Blackjack!",
push: { label: "Push", color: "bg-blue-500/20 text-blue-400" }, color: "bg-gradient-to-r from-yellow-400 to-amber-500 text-white font-bold shadow-sm",
lose: { label: "Lose", color: "bg-red-500/20 text-red-400" }, icon: <Trophy className="w-3 h-3" />
},
win: {
label: "Win",
color: "bg-gradient-to-r from-emerald-500 to-emerald-600 text-white font-bold shadow-sm",
icon: <ChevronsUp className="w-3 h-3" />
},
push: {
label: "Push",
color: "bg-gradient-to-r from-blue-500 to-blue-600 text-white font-bold shadow-sm",
icon: <Check className="w-3 h-3" />
},
lose: {
label: "Lose",
color: "bg-gradient-to-r from-red-500 to-red-600 text-white font-bold shadow-sm",
icon: <AlertCircle className="w-3 h-3" />
},
}; };
const c = config[result] ?? { label: "-", color: "bg-black/20 text-white/50" }; const c = config[result] ?? { label: "-", color: "bg-white/10 text-white/50" };
return ( return (
<span className={`rounded px-1.5 py-0.5 text-[10px] font-semibold ${c.color}`} title={reason ?? undefined}> <span className={`flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] ${c.color}`} title={reason ?? undefined}>
{c.label} {c.icon}
<span>{c.label}</span>
</span> </span>
); );
} }
// ── Bet chip indicator ── // ── Bet chip indicator ---
function BetChip({ bet, betAmount }: { bet: number; betAmount: number }) { function BetChip({ bet, betAmount }: { bet: number; betAmount: number }) {
if (betAmount <= 0) return null; if (betAmount <= 0) return null;
const total = bet * betAmount; const total = bet * betAmount;
return ( return (
<div className="flex items-center gap-0.5 bg-yellow-500/20 text-yellow-300 rounded px-1.5 py-0.5"> <div className="flex items-center gap-1 bg-gradient-to-r from-yellow-500 to-yellow-600 text-white rounded-full px-2 py-0.5 shadow-md border border-yellow-400/30 animate-pulse-slow">
<Coins className="w-2.5 h-2.5" /> <Coins className="w-2.5 h-2.5" />
<span className="text-[9px] font-bold">{total}</span> <span className="text-[9px] font-bold">{total}</span>
</div> </div>
); );
} }
// ── Single hand display ── // ── Single hand display ---
function HandDisplay({ hand, isActive, betAmount, size = "normal" }: { function HandDisplay({ hand, isActive, betAmount, size = "normal" }: {
hand: PlayerHandView; hand: PlayerHandView;
@@ -184,11 +201,11 @@ function HandDisplay({ hand, isActive, betAmount, size = "normal" }: {
size?: "small" | "normal"; size?: "small" | "normal";
}) { }) {
return ( return (
<div className={`flex flex-col items-center gap-1 rounded-lg px-1.5 py-1 transition-all ${ <div className={`flex flex-col items-center gap-2 rounded-xl px-1.5 py-2 transition-all duration-300 ${
isActive ? "ring-1 ring-primary/40 bg-primary/5" : "" isActive ? "ring-2 ring-primary/50 bg-white/5 scale-[1.02] shadow-lg" : "ring-1 ring-white/10 bg-white/[0.02]"
}`}> }`}>
<CardFan cards={hand.cards} size={size} /> <CardFan cards={hand.cards} size={size} />
<div className="flex items-center gap-1 flex-wrap justify-center"> <div className="flex items-center gap-2 flex-wrap justify-center">
<ValueBadge value={hand.value} status={hand.status} /> <ValueBadge value={hand.value} status={hand.status} />
{hand.bet > 1 && <BetChip bet={hand.bet} betAmount={betAmount} />} {hand.bet > 1 && <BetChip bet={hand.bet} betAmount={betAmount} />}
{hand.result && <ResultBadge result={hand.result} reason={hand.resultReason} />} {hand.result && <ResultBadge result={hand.result} reason={hand.resultReason} />}
@@ -221,21 +238,34 @@ function Seat({ seat, playerName, isMe, isActivePlayer, activeHandIdx, betAmount
}, 0); }, 0);
const totalPnl = seat.cumulativePnl + currentRoundPnl; const totalPnl = seat.cumulativePnl + currentRoundPnl;
// Determine PnL color theme
const getPnlTheme = () => {
if (totalPnl > 0) return "emerald";
if (totalPnl < 0) return "red";
return "blue";
};
const pnlColor = getPnlTheme();
const isMeStyle = isActivePlayer && isMe ? "ring-2 ring-primary/50 bg-gradient-to-br from-white/10 to-white/5" : isActivePlayer ? "ring-1 ring-primary/30 bg-primary/5" : "ring-1 ring-white/10 bg-white/[0.02]";
return ( return (
<div className={`flex flex-col items-center gap-1.5 transition-all ${ <div className={`flex flex-col items-center gap-2 transition-all duration-300 ${
isActivePlayer ? "scale-105" : "" isActivePlayer ? "scale-105" : ""
}`}> }`}>
{/* Player label */} {/* Player label with avatar and active indicator */}
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-2">
<div className={`w-6 h-6 rounded-full flex items-center justify-center text-[10px] font-bold ${ <div className={`relative w-8 h-8 rounded-full flex items-center justify-center text-xs font-bold ${
isActivePlayer ? "bg-primary/30 text-primary ring-2 ring-primary/50" : "bg-white/10 text-white/70" isActivePlayer
? "bg-gradient-to-br from-primary/40 to-primary/60 ring-2 ring-primary/70 shadow-lg"
: "bg-white/10 text-white/70 ring-1 ring-white/20"
}`}> }`}>
{playerName[0]?.toUpperCase() ?? "?"} {playerName[0]?.toUpperCase() ?? "?"}
{isMe && <span className="absolute -top-1 -right-1 w-2 h-2 bg-primary rounded-full animate-pulse" />}
</div> </div>
<span className={`text-xs font-medium truncate max-w-20 ${ <span className={`text-xs font-medium truncate max-w-24 ${
isActivePlayer ? "text-primary" : "text-white/70" isActivePlayer ? "text-primary font-semibold" : isMe ? "text-white font-medium" : "text-white/60"
}`}> }`}>
{playerName}{isMe ? "" : ""} {playerName}{isMe ? " (You)" : ""}
</span> </span>
{isActivePlayer && ( {isActivePlayer && (
<span className="w-1.5 h-1.5 rounded-full bg-primary animate-pulse" /> <span className="w-1.5 h-1.5 rounded-full bg-primary animate-pulse" />
@@ -244,8 +274,10 @@ function Seat({ seat, playerName, isMe, isActivePlayer, activeHandIdx, betAmount
{/* Bet status (during betting phase, no hands yet) */} {/* Bet status (during betting phase, no hands yet) */}
{!hasHands && ( {!hasHands && (
<div className={`flex items-center gap-1 text-[10px] rounded-full px-2 py-0.5 ${ <div className={`flex items-center gap-1 text-[9px] rounded-full px-2 py-0.5 ${
hasBet ? "bg-emerald-500/20 text-emerald-400" : "bg-white/5 text-white/40" hasBet
? "bg-emerald-500/20 text-emerald-400 border border-emerald-500/30"
: "bg-white/5 text-white/40 border border-white/10"
}`}> }`}>
{hasBet ? <><Check className="w-2.5 h-2.5" /> Ready</> : "Waiting..."} {hasBet ? <><Check className="w-2.5 h-2.5" /> Ready</> : "Waiting..."}
</div> </div>
@@ -273,16 +305,18 @@ function Seat({ seat, playerName, isMe, isActivePlayer, activeHandIdx, betAmount
{/* Cumulative PnL indicator */} {/* Cumulative PnL indicator */}
{(seat.cumulativePnl !== 0 || hasHands) && ( {(seat.cumulativePnl !== 0 || hasHands) && (
<div className={`flex items-center gap-1 text-xs font-bold rounded px-2 py-0.5 ${ <div className={`flex items-center gap-1.5 text-xs font-bold rounded-full px-3 py-1 border ${
totalPnl > 0 totalPnl > 0
? "bg-emerald-500/20 text-emerald-400" ? `bg-emerald-500/10 border-emerald-500/30 text-emerald-400`
: totalPnl < 0 : totalPnl < 0
? "bg-red-500/20 text-red-400" ? `bg-red-500/10 border-red-500/30 text-red-400`
: "bg-blue-500/20 text-blue-300" : `bg-blue-500/10 border-blue-500/30 text-blue-400`
}`}> }`}>
{totalPnl > 0 && <ChevronsUp className="w-3.5 h-3.5" />} {totalPnl > 0 && <ChevronsUp className="w-3.5 h-3.5" />}
{totalPnl < 0 && <ChevronsUp className="w-3.5 h-3.5 rotate-180" />} {totalPnl < 0 && <ChevronsUp className="w-3.5 h-3.5 rotate-180" />}
<span>{totalPnl > 0 ? "+" : ""}{totalPnl} AU</span> <span className={`font-mono ${totalPnl > 0 ? "text-emerald-400" : totalPnl < 0 ? "text-red-400" : "text-blue-300"}`}>
{totalPnl > 0 ? "+" : ""}{totalPnl} AU
</span>
</div> </div>
)} )}
</div> </div>
@@ -320,24 +354,28 @@ function DealerArea({ dealerHand, visibleValue, fullValue }: {
visibleValue: number; visibleValue: number;
fullValue: number | null; fullValue: number | null;
}) { }) {
if (dealerHand.length === 0) {
return (
<div className="flex flex-col items-center gap-1.5">
<span className="text-xs font-semibold text-white/50">Dealer</span>
<div className="w-12 h-[4.25rem] sm:w-14 sm:h-[5rem] rounded-md border border-dashed border-white/10" />
</div>
);
}
const displayValue = fullValue ?? visibleValue;
const isBust = fullValue !== null && fullValue > 21; const isBust = fullValue !== null && fullValue > 21;
return ( return (
<div className="flex flex-col items-center gap-1.5"> <div className="flex flex-col items-center gap-2">
<div className="flex items-center gap-1.5"> <div className={`flex items-center gap-2 px-3 py-1 rounded-full ${
<span className="text-xs font-semibold text-white/60">Dealer</span> fullValue === 21
{displayValue > 0 && ( ? "bg-gradient-to-r from-yellow-500 to-amber-600 text-white shadow-lg"
<ValueBadge value={displayValue} status={isBust ? "bust" : undefined} /> : fullValue !== null && fullValue >= 17
? "bg-white/20 backdrop-blur-sm text-white"
: "bg-white/10 backdrop-blur-sm text-white/80"
}`}>
<span className="text-xs font-semibold">Dealer</span>
{fullValue !== null && (
<span className={`text-xs font-bold px-2 py-0.5 rounded-full ${
fullValue === 21
? "bg-white text-black font-bold"
: isBust
? "bg-red-500 text-white animate-bounce"
: "bg-black/30 text-white"
}`}>
{fullValue}
</span>
)} )}
</div> </div>
<CardFan cards={dealerHand} /> <CardFan cards={dealerHand} />
@@ -345,7 +383,7 @@ function DealerArea({ dealerHand, visibleValue, fullValue }: {
); );
} }
// ── Round result banner ── // ── Round result banner ---
function RoundResultBanner({ roundNumber, roundResult, myPlayerId, betAmount, myCumulativePnl }: { function RoundResultBanner({ roundNumber, roundResult, myPlayerId, betAmount, myCumulativePnl }: {
roundNumber: number; roundNumber: number;
@@ -361,22 +399,45 @@ function RoundResultBanner({ roundNumber, roundResult, myPlayerId, betAmount, my
const currentBalance = myCumulativePnl; const currentBalance = myCumulativePnl;
return ( return (
<div className="flex items-center justify-center gap-3 bg-black/30 rounded-xl px-4 py-2"> <div className="flex items-center justify-between gap-3 bg-gradient-to-r from-black/50 to-black/40 rounded-xl px-4 py-3 shadow-xl border border-white/10 animate-in fade-in slide-in-from-bottom-4 duration-300">
<span className="text-xs text-white/50">Round {roundNumber}</span>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-xs font-semibold text-white/50">Round {roundNumber}</span>
<div className="h-4 w-px bg-white/20" />
{betAmount > 0 && ( {betAmount > 0 && (
<span className={`text-xs font-semibold ${ <div className={`flex items-center gap-1.5 text-xs font-semibold px-2 py-0.5 rounded-full ${
roundNet > 0 ? "text-emerald-400" : roundNet < 0 ? "text-red-400" : "text-blue-300" roundNet > 0
? "bg-emerald-500/20 text-emerald-400 border border-emerald-500/30"
: roundNet < 0
? "bg-red-500/20 text-red-400 border border-red-500/30"
: "bg-blue-500/20 text-blue-400 border border-blue-500/30"
}`}> }`}>
Round: {roundNet > 0 ? "+" : ""}{roundNet} AU <span className="font-mono">
</span> {roundNet > 0 ? "+" : ""}{roundNet} AU
</span>
</div>
)} )}
<div className="w-px h-4 bg-white/20" /> </div>
<span className={`text-xs font-bold ${ <div className="flex items-center gap-2">
currentBalance > 0 ? "text-emerald-400" : currentBalance < 0 ? "text-red-400" : "text-blue-300" <span className="text-xs text-white/40">Total:</span>
<div className={`flex items-center gap-1.5 px-3 py-1 rounded-full border ${
currentBalance > 0
? "bg-emerald-500/10 border-emerald-500/30"
: currentBalance < 0
? "bg-red-500/10 border-red-500/30"
: "bg-blue-500/10 border-blue-500/30"
}`}> }`}>
Total: {currentBalance > 0 ? "+" : ""}{currentBalance} AU {currentBalance > 0 && <ChevronsUp className="w-4 h-4 text-emerald-500" />}
</span> {currentBalance < 0 && <ChevronsUp className="w-4 h-4 text-red-500 rotate-180" />}
<span className={`font-bold font-mono ${
currentBalance > 0
? "text-emerald-400"
: currentBalance < 0
? "text-red-400"
: "text-blue-300"
}`}>
{currentBalance > 0 ? "+" : ""}{currentBalance} AU
</span>
</div>
</div> </div>
</div> </div>
); );
@@ -504,25 +565,27 @@ export function BlackjackGame({ state, myPlayerId, isSpectator, onAction, player
</div> </div>
{/* ── Mobile layout (<md): vertical stack ── */} {/* ── Mobile layout (<md): vertical stack ── */}
<div className="md:hidden flex flex-col gap-4 p-4"> <div className="md:hidden flex flex-col gap-4 p-3 md:p-4">
{/* Dealer */} {/* Dealer */}
<DealerArea <div className="bg-white/5 rounded-xl p-3">
dealerHand={view.dealerHand} <DealerArea
visibleValue={view.dealerVisibleValue} dealerHand={view.dealerHand}
fullValue={view.dealerFullValue} visibleValue={view.dealerVisibleValue}
/> fullValue={view.dealerFullValue}
/>
</div>
{/* Divider */} {/* Divider */}
<div className="h-px bg-white/10" /> <div className="h-px bg-white/10 mx-3" />
{/* Phase label */} {/* Phase label */}
{isBetting && ( {isBetting && (
<div className="text-center text-white/30 text-xs"> <div className="text-center text-white/40 text-xs font-medium">
Round {view.roundNumber} Place your bets Round {view.roundNumber} Place your bets
</div> </div>
)} )}
{isResolved && ( {isResolved && (
<div className="text-center text-white/30 text-xs"> <div className="text-center text-white/40 text-xs font-medium">
Round {view.roundNumber} Results Round {view.roundNumber} Results
</div> </div>
)} )}
@@ -543,9 +606,9 @@ export function BlackjackGame({ state, myPlayerId, isSpectator, onAction, player
return ( return (
<div <div
key={slot.playerId} key={slot.playerId}
className={`rounded-xl px-3 py-2 transition-colors ${ className={`rounded-xl px-3 py-3 transition-all duration-200 ${
view.activePlayerId === slot.playerId view.activePlayerId === slot.playerId
? "bg-white/5 ring-1 ring-primary/20" ? "ring-1 ring-primary/30 bg-white/[0.05]"
: "bg-white/[0.02]" : "bg-white/[0.02]"
}`} }`}
> >
@@ -598,23 +661,23 @@ export function BlackjackGame({ state, myPlayerId, isSpectator, onAction, player
{/* Playing phase: action buttons */} {/* Playing phase: action buttons */}
{isPlaying && playerView?.canAct && ( {isPlaying && playerView?.canAct && (
<div className="flex gap-2"> <div className="flex gap-2 flex-wrap">
<button <button
onClick={handleHit} onClick={handleHit}
className="flex-1 flex items-center justify-center gap-2 rounded-xl bg-primary text-on-primary px-4 py-3 text-sm font-label font-semibold hover:opacity-90 transition-colors" className="flex-1 min-w-[80px] flex items-center justify-center gap-2 rounded-xl bg-gradient-to-r from-blue-600 to-blue-700 text-white px-4 py-3.5 text-sm font-label font-semibold shadow-lg hover:shadow-blue-500/20 transition-all active:scale-[0.98]"
> >
<Hand className="w-4 h-4" /> Hit <Hand className="w-4 h-4" /> Hit
</button> </button>
<button <button
onClick={handleStand} onClick={handleStand}
className="flex-1 flex items-center justify-center gap-2 rounded-xl bg-raised text-foreground px-4 py-3 text-sm font-label font-semibold hover:bg-surface-container-high transition-colors" className="flex-1 min-w-[80px] flex items-center justify-center gap-2 rounded-xl bg-gradient-to-r from-gray-600 to-gray-700 text-white px-4 py-3.5 text-sm font-label font-semibold shadow-lg hover:shadow-gray-500/20 transition-all active:scale-[0.98]"
> >
<Square className="w-3.5 h-3.5" /> Stand <Square className="w-3.5 h-3.5" /> Stand
</button> </button>
{playerView.canSplit && ( {playerView.canSplit && (
<button <button
onClick={handleSplit} onClick={handleSplit}
className="flex items-center justify-center gap-2 rounded-xl bg-raised text-foreground px-4 py-3 text-sm font-label font-semibold hover:bg-surface-container-high transition-colors" className="flex items-center justify-center gap-2 rounded-xl bg-gradient-to-r from-purple-600 to-indigo-700 text-white px-4 py-3.5 text-sm font-label font-semibold shadow-lg hover:shadow-purple-500/20 transition-all active:scale-[0.98]"
> >
<ArrowLeftRight className="w-4 h-4" /> Split <ArrowLeftRight className="w-4 h-4" /> Split
</button> </button>
@@ -622,7 +685,7 @@ export function BlackjackGame({ state, myPlayerId, isSpectator, onAction, player
{playerView.canDoubleDown && ( {playerView.canDoubleDown && (
<button <button
onClick={handleDouble} onClick={handleDouble}
className="flex items-center justify-center gap-2 rounded-xl bg-raised text-foreground px-4 py-3 text-sm font-label font-semibold hover:bg-surface-container-high transition-colors" className="flex items-center justify-center gap-2 rounded-xl bg-gradient-to-r from-orange-500 to-red-600 text-white px-4 py-3.5 text-sm font-label font-semibold shadow-lg hover:shadow-orange-500/20 transition-all active:scale-[0.98]"
> >
<ChevronsUp className="w-4 h-4" /> Double <ChevronsUp className="w-4 h-4" /> Double
</button> </button>

View File

@@ -77,3 +77,29 @@ body {
* { * {
border-color: var(--color-border); border-color: var(--color-border);
} }
/* Custom Animations */
@keyframes pulse-slow {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
.animate-pulse-slow {
animation: pulse-slow 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
/* Slide-in animation for result banner */
@keyframes slideInFromBottom {
from {
transform: translateY(1rem);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.animate-in.slide-in-from-bottom-4 {
animation: slideInFromBottom 0.3s ease-out forwards;
}

View File

@@ -194,12 +194,14 @@ function finishPlayerTurns(state: BlackjackState): BlackjackState {
// First resolve the hands // First resolve the hands
const resolvedHands = seat.hands.map(h => resolveHand(h, dealerHand)); const resolvedHands = seat.hands.map(h => resolveHand(h, dealerHand));
// Then calculate PnL based on resolved hands // Calculate round payouts as multipliers
const roundPayout = calculateRoundPayouts({ [id]: { ...seat, hands: resolvedHands } }); const roundPayout = calculateRoundPayouts({ [id]: { ...seat, hands: resolvedHands } });
const roundPayoutMoney = roundPayout[id] ?? 0; const multiplier = roundPayout[id] ?? 0;
// Subtract the total bet amount to get net profit/loss // Calculate total bet amount for this player
const roundBetTotal = seat.hands.reduce((sum, h) => sum + (h.bet * state.betAmount), 0); const roundBetTotal = seat.hands.reduce((sum, h) => sum + (h.bet * state.betAmount), 0);
const roundNetPnl = roundPayoutMoney ? roundPayoutMoney - roundBetTotal : -roundBetTotal; // Calculate actual payout amount and net profit
const roundPayoutAmount = Math.round(multiplier * state.betAmount);
const roundNetPnl = roundBetTotal > 0 ? roundPayoutAmount - roundBetTotal : 0;
resolvedSeats[id] = { resolvedSeats[id] = {
...seat, ...seat,