Add showMatchScore setting and forfeit button

Added two new features for better game control:

1. Show Match Score Setting:
   - Added showMatchScore boolean to Settings type (default: true)
   - Added toggle in SettingsDialog to control score visibility
   - Players.tsx now respects the setting when passing matchScore
   - Users can hide score badges in 1v1 games if desired

2. Forfeit Button in Player Menu:
   - Added Skull icon import to PlayerMenu
   - Added onForfeit optional prop to PlayerMenu
   - Added red skull button in player menu buttons section
   - LifeCounter passes forfeit handler (toggleGameLost) to menu
   - Only shows when player can lose (life ≤ 0 or poison/commander damage)
   - Allows players to forfeit game from their individual menu

Both features improve user control over the match scoring system.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Viktor Rådberg
2025-11-16 18:33:38 +01:00
parent c71dbc2769
commit 309eb47f7a
5 changed files with 51 additions and 1 deletions

View File

@@ -242,6 +242,23 @@ export const SettingsDialog = ({
</ul>
</Description>
</SettingContainer>
<SettingContainer>
<ToggleContainer>
<label>Show Match Score</label>
<ToggleButton
checked={settings.showMatchScore}
onChange={() => {
setSettings({
...settings,
showMatchScore: !settings.showMatchScore,
});
}}
/>
</ToggleContainer>
<Description>
Shows a score badge on each player's card in 1v1 games to track wins across multiple games.
</Description>
</SettingContainer>
<Separator height="1px" />
<div className="flex w-full justify-center">
<button

View File

@@ -268,6 +268,7 @@ const LifeCounter = ({ player, opponents, matchScore }: LifeCounterProps) => {
isShown={showPlayerMenu}
player={player}
setShowPlayerMenu={setShowPlayerMenu}
onForfeit={playerCanLose(player) ? toggleGameLost : undefined}
/>
</LifeCounterWrapper>
</LifeCounterContentWrapper>

View File

@@ -16,6 +16,7 @@ import {
PartnerTax,
Poison,
ResetGame,
Skull,
} from '../../Icons/generated';
import { Player, Rotation } from '../../Types/Player';
import { PreStartMode } from '../../Types/Settings';
@@ -90,12 +91,14 @@ type PlayerMenuProps = {
player: Player;
setShowPlayerMenu: (showPlayerMenu: boolean) => void;
isShown: boolean;
onForfeit?: () => void;
};
const PlayerMenu = ({
player,
setShowPlayerMenu,
isShown,
onForfeit,
}: PlayerMenuProps) => {
const settingsContainerRef = useRef<HTMLDivElement | null>(null);
const resetGameDialogRef = useRef<HTMLDialogElement | null>(null);
@@ -479,6 +482,28 @@ const PlayerMenu = ({
>
<ResetGame size={iconSize} />
</button>
{onForfeit && (
<button
style={{
cursor: 'pointer',
userSelect: 'none',
fontSize: buttonFontSize,
padding: '2px',
}}
className="text-red-500"
onClick={() => {
analytics.trackEvent('forfeit_game', {
player: player.index,
});
onForfeit();
setShowPlayerMenu(false);
}}
aria-label="Forfeit Game"
>
<Skull size={iconSize} />
</button>
)}
</ButtonsSections>
</BetterRowContainer>

View File

@@ -48,7 +48,11 @@ export const Players = ({ gridLayout }: { gridLayout: GridLayout }) => {
opponents={players.filter(
(opponent) => opponent.index !== player.index
)}
matchScore={players.length === 2 ? gameScore[player.index] : undefined}
matchScore={
players.length === 2 && settings.showMatchScore
? gameScore[player.index]
: undefined
}
/>
{settings.preStartMode === PreStartMode.RandomKing &&

View File

@@ -27,6 +27,7 @@ export type Settings = {
preStartMode: PreStartMode;
showAnimations: boolean;
useMonarch: boolean;
showMatchScore: boolean;
};
export type InitialGameSettings = {
@@ -61,6 +62,7 @@ export const settingsSchema = z.object({
preStartMode: z.nativeEnum(PreStartMode),
showAnimations: z.boolean(),
useMonarch: z.boolean().default(false),
showMatchScore: z.boolean().default(true),
});
export const defaultSettings: Settings = {
@@ -71,4 +73,5 @@ export const defaultSettings: Settings = {
preStartMode: PreStartMode.None,
showAnimations: true,
useMonarch: false,
showMatchScore: true,
};