mirror of
https://github.com/Vikeo/LifeTrinket.git
synced 2025-11-18 16:58:01 +00:00
score badge
This commit is contained in:
@@ -256,7 +256,7 @@ export const SettingsDialog = ({
|
||||
/>
|
||||
</ToggleContainer>
|
||||
<Description>
|
||||
Shows a score badge on each player's card in 1v1 games to track wins across multiple games.
|
||||
Shows a score badge on each player's card to track wins across multiple games.
|
||||
</Description>
|
||||
</SettingContainer>
|
||||
<Separator height="1px" />
|
||||
|
||||
@@ -32,23 +32,39 @@ const WinnerName = twc.div`
|
||||
mb-2
|
||||
`;
|
||||
|
||||
const StartButton = twc.button`
|
||||
const PrimaryButton = twc.button`
|
||||
py-4 px-6 rounded-xl
|
||||
text-xl font-semibold
|
||||
bg-interface-primary
|
||||
text-white
|
||||
transition-all duration-200
|
||||
hover:scale-105 active:scale-95
|
||||
border-2 border-transparent
|
||||
hover:border-white/30
|
||||
border-3 border-white/50
|
||||
shadow-lg shadow-interface-primary/50
|
||||
`;
|
||||
|
||||
const SecondaryButton = twc.button`
|
||||
py-4 px-6 rounded-xl
|
||||
text-xl font-semibold
|
||||
bg-secondary-main
|
||||
text-text-primary
|
||||
transition-all duration-200
|
||||
hover:scale-105 active:scale-95
|
||||
border-3 border-primary-main
|
||||
shadow-lg shadow-secondary-main/50
|
||||
`;
|
||||
|
||||
type GameOverProps = {
|
||||
winner: Player;
|
||||
onStartNextGame: () => void;
|
||||
onStay: () => void;
|
||||
};
|
||||
|
||||
export const GameOver = ({ winner, onStartNextGame }: GameOverProps) => {
|
||||
export const GameOver = ({
|
||||
winner,
|
||||
onStartNextGame,
|
||||
onStay,
|
||||
}: GameOverProps) => {
|
||||
return (
|
||||
<Overlay>
|
||||
<Modal>
|
||||
@@ -60,9 +76,10 @@ export const GameOver = ({ winner, onStartNextGame }: GameOverProps) => {
|
||||
Won the game!
|
||||
</p>
|
||||
<ButtonContainer>
|
||||
<StartButton onClick={onStartNextGame}>
|
||||
<PrimaryButton onClick={onStartNextGame}>
|
||||
Start Next Game
|
||||
</StartButton>
|
||||
</PrimaryButton>
|
||||
<SecondaryButton onClick={onStay}>Close</SecondaryButton>
|
||||
</ButtonContainer>
|
||||
</Modal>
|
||||
</Overlay>
|
||||
|
||||
@@ -226,11 +226,8 @@ const LifeCounter = ({ player, opponents, matchScore }: LifeCounterProps) => {
|
||||
<PlayerLostWrapper $rotation={player.settings.rotation} />
|
||||
)}
|
||||
{matchScore !== undefined && matchScore > 0 && (
|
||||
<MatchScoreBadge
|
||||
$rotation={player.settings.rotation}
|
||||
style={{ rotate: `${calcRotation}deg` }}
|
||||
>
|
||||
{matchScore}
|
||||
<MatchScoreBadge $rotation={player.settings.rotation}>
|
||||
<div style={{ rotate: `${-calcRotation}deg` }}>{matchScore}</div>
|
||||
</MatchScoreBadge>
|
||||
)}
|
||||
<CommanderDamageBar
|
||||
@@ -269,6 +266,7 @@ const LifeCounter = ({ player, opponents, matchScore }: LifeCounterProps) => {
|
||||
player={player}
|
||||
setShowPlayerMenu={setShowPlayerMenu}
|
||||
onForfeit={toggleGameLost}
|
||||
totalPlayers={opponents.length + 1}
|
||||
/>
|
||||
</LifeCounterWrapper>
|
||||
</LifeCounterContentWrapper>
|
||||
|
||||
@@ -92,6 +92,7 @@ type PlayerMenuProps = {
|
||||
setShowPlayerMenu: (showPlayerMenu: boolean) => void;
|
||||
isShown: boolean;
|
||||
onForfeit?: () => void;
|
||||
totalPlayers: number;
|
||||
};
|
||||
|
||||
const PlayerMenu = ({
|
||||
@@ -99,10 +100,12 @@ const PlayerMenu = ({
|
||||
setShowPlayerMenu,
|
||||
isShown,
|
||||
onForfeit,
|
||||
totalPlayers,
|
||||
}: PlayerMenuProps) => {
|
||||
const settingsContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
const resetGameDialogRef = useRef<HTMLDialogElement | null>(null);
|
||||
const endGameDialogRef = useRef<HTMLDialogElement | null>(null);
|
||||
const forfeitGameDialogRef = useRef<HTMLDialogElement | null>(null);
|
||||
|
||||
const { isSide } = useSafeRotate({
|
||||
rotation: player.settings.rotation,
|
||||
@@ -492,6 +495,9 @@ const PlayerMenu = ({
|
||||
}}
|
||||
className="text-red-500"
|
||||
onClick={() => {
|
||||
if (totalPlayers === 2) {
|
||||
forfeitGameDialogRef.current?.show();
|
||||
} else {
|
||||
if (onForfeit) {
|
||||
analytics.trackEvent('forfeit_game', {
|
||||
player: player.index,
|
||||
@@ -499,6 +505,7 @@ const PlayerMenu = ({
|
||||
onForfeit();
|
||||
setShowPlayerMenu(false);
|
||||
}
|
||||
}
|
||||
}}
|
||||
aria-label="Forfeit Game"
|
||||
>
|
||||
@@ -584,6 +591,48 @@ const PlayerMenu = ({
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
ref={forfeitGameDialogRef}
|
||||
className="z-[999] size-full bg-background-settings overflow-y-scroll"
|
||||
onClick={() => forfeitGameDialogRef.current?.close()}
|
||||
>
|
||||
<div className="flex size-full items-center justify-center">
|
||||
<div className="flex flex-col justify-center p-4 gap-2 bg-background-default rounded-xl border-none">
|
||||
<h1
|
||||
className="text-center text-text-primary"
|
||||
style={{ fontSize: extraCountersSize }}
|
||||
>
|
||||
Forfeit Game?
|
||||
</h1>
|
||||
<div className="flex justify-evenly gap-2">
|
||||
<button
|
||||
className="bg-primary-main border border-primary-dark text-text-primary rounded-lg flex-grow"
|
||||
style={{ fontSize: iconSize }}
|
||||
onClick={() => forfeitGameDialogRef.current?.close()}
|
||||
>
|
||||
No
|
||||
</button>
|
||||
<button
|
||||
className="bg-primary-main border border-primary-dark text-text-primary rounded-lg flex-grow"
|
||||
onClick={() => {
|
||||
if (onForfeit) {
|
||||
analytics.trackEvent('forfeit_game', {
|
||||
player: player.index,
|
||||
});
|
||||
onForfeit();
|
||||
setShowPlayerMenu(false);
|
||||
forfeitGameDialogRef.current?.close();
|
||||
}
|
||||
}}
|
||||
style={{ fontSize: iconSize }}
|
||||
>
|
||||
Yes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
</SettingsContainer>
|
||||
</PlayerMenuWrapper>
|
||||
);
|
||||
|
||||
@@ -49,7 +49,7 @@ export const Players = ({ gridLayout }: { gridLayout: GridLayout }) => {
|
||||
(opponent) => opponent.index !== player.index
|
||||
)}
|
||||
matchScore={
|
||||
players.length === 2 && settings.showMatchScore
|
||||
settings.showMatchScore
|
||||
? gameScore[player.index]
|
||||
: undefined
|
||||
}
|
||||
|
||||
@@ -96,9 +96,9 @@ export const Play = () => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// Check for game over in 1v1 games
|
||||
// Check for game over when only one player remains
|
||||
useEffect(() => {
|
||||
if (players.length !== 2 || winner !== null) {
|
||||
if (players.length < 2 || winner !== null || !settings.showMatchScore) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ export const Play = () => {
|
||||
if (activePlayers.length === 1) {
|
||||
setWinner(activePlayers[0].index);
|
||||
}
|
||||
}, [players, winner]);
|
||||
}, [players, winner, settings.showMatchScore]);
|
||||
|
||||
const handleStartNextGame = () => {
|
||||
if (winner === null) return;
|
||||
@@ -127,6 +127,26 @@ export const Play = () => {
|
||||
setWinner(null);
|
||||
};
|
||||
|
||||
const handleStay = () => {
|
||||
if (winner === null) return;
|
||||
|
||||
// Update score
|
||||
const newScore = { ...gameScore };
|
||||
newScore[winner] = (newScore[winner] || 0) + 1;
|
||||
setGameScore(newScore);
|
||||
|
||||
// Reset hasLost state for all players
|
||||
setPlayers(
|
||||
players.map((p) => ({
|
||||
...p,
|
||||
hasLost: false,
|
||||
}))
|
||||
);
|
||||
|
||||
// Clear winner to allow new game over detection
|
||||
setWinner(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<MainWrapper>
|
||||
{players.length > 1 &&
|
||||
@@ -141,6 +161,7 @@ export const Play = () => {
|
||||
<GameOver
|
||||
winner={players[winner]}
|
||||
onStartNextGame={handleStartNextGame}
|
||||
onStay={handleStay}
|
||||
/>
|
||||
)}
|
||||
</MainWrapper>
|
||||
|
||||
Reference in New Issue
Block a user