Forfeit = 2x coin loss + 0 XP (no kot); end-of-game roster + add friend
CI/CD / CI - API (dotnet build + engine sim) (push) Successful in 21s
CI/CD / CI - Web (tsc + next build) (push) Successful in 1m0s
CI/CD / Deploy - local stack (db + server + web) (push) Failing after 0s

Forfeit penalty reworked (client + server gamification, in sync):
- Surrendering team loses DOUBLE the entry coins; winner takes the stake.
- Forfeiter earns NO XP. No kot is applied or mentioned anymore.
- MatchSummary/Dto carry a `forfeit` flag; GameRoom.FinalizeForfeit →
  ApplyRewardsAsync(team) with Forfeit=true (dropped the kot path).
- Forfeit confirm dialogs now alert the real penalty (double coins, no XP).

End-of-game roster: SeatPlayerDto/ServerSeatPlayer + game-store SeatPlayer gain
userId/isBot. New <MatchPlayersList> lists everyone at the table on the final
screen (PostMatchRewardsModal + AI MatchOverlay) with a tactile "Add" button to
send a friend request to real (non-bot, non-self) players ("Sent" after).

Verified: tsc + sim + dotnet + next build clean; stack rebuilt :1500/:1505.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-05 10:40:14 +03:30
parent 6bbdbac23b
commit b739b503eb
13 changed files with 127 additions and 19 deletions
+4 -1
View File
@@ -23,6 +23,7 @@ import { getService } from "@/lib/online/service";
import { cn } from "@/lib/cn";
import { PlayingCard } from "./PlayingCard";
import { Sticker } from "./online/Sticker";
import { MatchPlayersList } from "./online/MatchPlayersList";
function useCountdown(deadline: number | null) {
const [now, setNow] = useState(() => Date.now());
@@ -887,8 +888,10 @@ function MatchOverlay({ onExit }: { onExit: () => void }) {
them: game.matchScore[1],
})}
</p>
<MatchPlayersList />
<div className="mt-7 flex gap-3">
<button onClick={onExit} className="btn-gold flex-1 rounded-xl py-3">
<button onClick={onExit} className="press-3d btn-gold flex-1 rounded-xl py-3">
{t("match.menu")}
</button>
</div>