mirror of
https://github.com/Vikeo/LifeTrinket.git
synced 2025-11-19 09:18:02 +00:00
score badge
This commit is contained in:
@@ -256,7 +256,7 @@ export const SettingsDialog = ({
|
|||||||
/>
|
/>
|
||||||
</ToggleContainer>
|
</ToggleContainer>
|
||||||
<Description>
|
<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>
|
</Description>
|
||||||
</SettingContainer>
|
</SettingContainer>
|
||||||
<Separator height="1px" />
|
<Separator height="1px" />
|
||||||
|
|||||||
@@ -32,23 +32,39 @@ const WinnerName = twc.div`
|
|||||||
mb-2
|
mb-2
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StartButton = twc.button`
|
const PrimaryButton = twc.button`
|
||||||
py-4 px-6 rounded-xl
|
py-4 px-6 rounded-xl
|
||||||
text-xl font-semibold
|
text-xl font-semibold
|
||||||
bg-interface-primary
|
bg-interface-primary
|
||||||
text-white
|
text-white
|
||||||
transition-all duration-200
|
transition-all duration-200
|
||||||
hover:scale-105 active:scale-95
|
hover:scale-105 active:scale-95
|
||||||
border-2 border-transparent
|
border-3 border-white/50
|
||||||
hover:border-white/30
|
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 = {
|
type GameOverProps = {
|
||||||
winner: Player;
|
winner: Player;
|
||||||
onStartNextGame: () => void;
|
onStartNextGame: () => void;
|
||||||
|
onStay: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GameOver = ({ winner, onStartNextGame }: GameOverProps) => {
|
export const GameOver = ({
|
||||||
|
winner,
|
||||||
|
onStartNextGame,
|
||||||
|
onStay,
|
||||||
|
}: GameOverProps) => {
|
||||||
return (
|
return (
|
||||||
<Overlay>
|
<Overlay>
|
||||||
<Modal>
|
<Modal>
|
||||||
@@ -60,9 +76,10 @@ export const GameOver = ({ winner, onStartNextGame }: GameOverProps) => {
|
|||||||
Won the game!
|
Won the game!
|
||||||
</p>
|
</p>
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<StartButton onClick={onStartNextGame}>
|
<PrimaryButton onClick={onStartNextGame}>
|
||||||
Start Next Game
|
Start Next Game
|
||||||
</StartButton>
|
</PrimaryButton>
|
||||||
|
<SecondaryButton onClick={onStay}>Close</SecondaryButton>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
</Modal>
|
</Modal>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
|
|||||||
@@ -226,11 +226,8 @@ const LifeCounter = ({ player, opponents, matchScore }: LifeCounterProps) => {
|
|||||||
<PlayerLostWrapper $rotation={player.settings.rotation} />
|
<PlayerLostWrapper $rotation={player.settings.rotation} />
|
||||||
)}
|
)}
|
||||||
{matchScore !== undefined && matchScore > 0 && (
|
{matchScore !== undefined && matchScore > 0 && (
|
||||||
<MatchScoreBadge
|
<MatchScoreBadge $rotation={player.settings.rotation}>
|
||||||
$rotation={player.settings.rotation}
|
<div style={{ rotate: `${-calcRotation}deg` }}>{matchScore}</div>
|
||||||
style={{ rotate: `${calcRotation}deg` }}
|
|
||||||
>
|
|
||||||
{matchScore}
|
|
||||||
</MatchScoreBadge>
|
</MatchScoreBadge>
|
||||||
)}
|
)}
|
||||||
<CommanderDamageBar
|
<CommanderDamageBar
|
||||||
@@ -269,6 +266,7 @@ const LifeCounter = ({ player, opponents, matchScore }: LifeCounterProps) => {
|
|||||||
player={player}
|
player={player}
|
||||||
setShowPlayerMenu={setShowPlayerMenu}
|
setShowPlayerMenu={setShowPlayerMenu}
|
||||||
onForfeit={toggleGameLost}
|
onForfeit={toggleGameLost}
|
||||||
|
totalPlayers={opponents.length + 1}
|
||||||
/>
|
/>
|
||||||
</LifeCounterWrapper>
|
</LifeCounterWrapper>
|
||||||
</LifeCounterContentWrapper>
|
</LifeCounterContentWrapper>
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ type PlayerMenuProps = {
|
|||||||
setShowPlayerMenu: (showPlayerMenu: boolean) => void;
|
setShowPlayerMenu: (showPlayerMenu: boolean) => void;
|
||||||
isShown: boolean;
|
isShown: boolean;
|
||||||
onForfeit?: () => void;
|
onForfeit?: () => void;
|
||||||
|
totalPlayers: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PlayerMenu = ({
|
const PlayerMenu = ({
|
||||||
@@ -99,10 +100,12 @@ const PlayerMenu = ({
|
|||||||
setShowPlayerMenu,
|
setShowPlayerMenu,
|
||||||
isShown,
|
isShown,
|
||||||
onForfeit,
|
onForfeit,
|
||||||
|
totalPlayers,
|
||||||
}: PlayerMenuProps) => {
|
}: PlayerMenuProps) => {
|
||||||
const settingsContainerRef = useRef<HTMLDivElement | null>(null);
|
const settingsContainerRef = useRef<HTMLDivElement | null>(null);
|
||||||
const resetGameDialogRef = useRef<HTMLDialogElement | null>(null);
|
const resetGameDialogRef = useRef<HTMLDialogElement | null>(null);
|
||||||
const endGameDialogRef = useRef<HTMLDialogElement | null>(null);
|
const endGameDialogRef = useRef<HTMLDialogElement | null>(null);
|
||||||
|
const forfeitGameDialogRef = useRef<HTMLDialogElement | null>(null);
|
||||||
|
|
||||||
const { isSide } = useSafeRotate({
|
const { isSide } = useSafeRotate({
|
||||||
rotation: player.settings.rotation,
|
rotation: player.settings.rotation,
|
||||||
@@ -492,12 +495,16 @@ const PlayerMenu = ({
|
|||||||
}}
|
}}
|
||||||
className="text-red-500"
|
className="text-red-500"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (onForfeit) {
|
if (totalPlayers === 2) {
|
||||||
analytics.trackEvent('forfeit_game', {
|
forfeitGameDialogRef.current?.show();
|
||||||
player: player.index,
|
} else {
|
||||||
});
|
if (onForfeit) {
|
||||||
onForfeit();
|
analytics.trackEvent('forfeit_game', {
|
||||||
setShowPlayerMenu(false);
|
player: player.index,
|
||||||
|
});
|
||||||
|
onForfeit();
|
||||||
|
setShowPlayerMenu(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
aria-label="Forfeit Game"
|
aria-label="Forfeit Game"
|
||||||
@@ -584,6 +591,48 @@ const PlayerMenu = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
</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>
|
</SettingsContainer>
|
||||||
</PlayerMenuWrapper>
|
</PlayerMenuWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export const Players = ({ gridLayout }: { gridLayout: GridLayout }) => {
|
|||||||
(opponent) => opponent.index !== player.index
|
(opponent) => opponent.index !== player.index
|
||||||
)}
|
)}
|
||||||
matchScore={
|
matchScore={
|
||||||
players.length === 2 && settings.showMatchScore
|
settings.showMatchScore
|
||||||
? gameScore[player.index]
|
? gameScore[player.index]
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,9 +96,9 @@ export const Play = () => {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// 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(() => {
|
useEffect(() => {
|
||||||
if (players.length !== 2 || winner !== null) {
|
if (players.length < 2 || winner !== null || !settings.showMatchScore) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ export const Play = () => {
|
|||||||
if (activePlayers.length === 1) {
|
if (activePlayers.length === 1) {
|
||||||
setWinner(activePlayers[0].index);
|
setWinner(activePlayers[0].index);
|
||||||
}
|
}
|
||||||
}, [players, winner]);
|
}, [players, winner, settings.showMatchScore]);
|
||||||
|
|
||||||
const handleStartNextGame = () => {
|
const handleStartNextGame = () => {
|
||||||
if (winner === null) return;
|
if (winner === null) return;
|
||||||
@@ -127,6 +127,26 @@ export const Play = () => {
|
|||||||
setWinner(null);
|
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 (
|
return (
|
||||||
<MainWrapper>
|
<MainWrapper>
|
||||||
{players.length > 1 &&
|
{players.length > 1 &&
|
||||||
@@ -141,6 +161,7 @@ export const Play = () => {
|
|||||||
<GameOver
|
<GameOver
|
||||||
winner={players[winner]}
|
winner={players[winner]}
|
||||||
onStartNextGame={handleStartNextGame}
|
onStartNextGame={handleStartNextGame}
|
||||||
|
onStay={handleStay}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</MainWrapper>
|
</MainWrapper>
|
||||||
|
|||||||
Reference in New Issue
Block a user