This commit is contained in:
Viktor Rådberg
2024-03-16 14:40:18 +01:00
parent ef1310d674
commit a90dd7c9ea
11 changed files with 183 additions and 110 deletions

View File

@@ -13,7 +13,7 @@ import { LoseGameButton } from '../Buttons/LoseButton';
import CommanderDamageBar from '../Counters/CommanderDamageBar'; import CommanderDamageBar from '../Counters/CommanderDamageBar';
import ExtraCountersBar from '../Counters/ExtraCountersBar'; import ExtraCountersBar from '../Counters/ExtraCountersBar';
import { Paragraph } from '../Misc/TextComponents'; import { Paragraph } from '../Misc/TextComponents';
import PlayerMenu from '../Player/PlayerMenu'; import PlayerMenu from '../Players/PlayerMenu';
import Health from './Health'; import Health from './Health';
import { baseColors } from '../../../tailwind.config'; import { baseColors } from '../../../tailwind.config';
@@ -88,16 +88,24 @@ const playerCanLose = (player: Player) => {
}; };
type LifeCounterProps = { type LifeCounterProps = {
stopRandom: boolean;
player: Player; player: Player;
opponents: Player[]; opponents: Player[];
isStartingPlayer?: boolean;
}; };
const RECENT_DIFFERENCE_TTL = 3_000; const RECENT_DIFFERENCE_TTL = 3_000;
const LifeCounter = ({ player, opponents }: LifeCounterProps) => { const LifeCounter = ({
stopRandom,
player,
opponents,
isStartingPlayer,
}: LifeCounterProps) => {
const { updatePlayer, updateLifeTotal } = usePlayers(); const { updatePlayer, updateLifeTotal } = usePlayers();
const { settings } = useGlobalSettings(); const { settings } = useGlobalSettings();
const playingTimerRef = useRef<NodeJS.Timeout | undefined>(undefined); const playingTimerRef = useRef<NodeJS.Timeout | undefined>(undefined);
const [playing, setPlaying] = useState(false);
const [showPlayerMenu, setShowPlayerMenu] = useState(false); const [showPlayerMenu, setShowPlayerMenu] = useState(false);
const [recentDifference, setRecentDifference] = useState(0); const [recentDifference, setRecentDifference] = useState(0);
@@ -127,6 +135,7 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
onSwiping: (e) => e.event.stopPropagation(), onSwiping: (e) => e.event.stopPropagation(),
rotationAngle, rotationAngle,
}); });
console.log('stopRandom', stopRandom);
useEffect(() => { useEffect(() => {
const timer = setTimeout(() => { const timer = setTimeout(() => {
@@ -153,6 +162,7 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
useEffect(() => { useEffect(() => {
playingTimerRef.current = setTimeout(() => { playingTimerRef.current = setTimeout(() => {
localStorage.setItem('playing', 'true'); localStorage.setItem('playing', 'true');
setPlaying(true);
}, 10_000); }, 10_000);
return () => clearTimeout(playingTimerRef.current); return () => clearTimeout(playingTimerRef.current);
@@ -191,20 +201,20 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
style={{ rotate: `${calcRotation}deg` }} style={{ rotate: `${calcRotation}deg` }}
{...handlers} {...handlers}
> >
{settings.showStartingPlayer && {!playing && settings.showStartingPlayer && isStartingPlayer && (
player.isStartingPlayer &&
player.showStartingPlayer && (
<div <div
className="z-10 flex absolute w-full h-full justify-center items-center select-none cursor-pointer webkit-user-select-none" className="z-20 flex absolute w-full h-full justify-center items-center select-none cursor-pointer webkit-user-select-none"
style={{ style={{
rotate: `${calcRotation}deg`, rotate: `${calcRotation}deg`,
backgroundImage: `radial-gradient(circle at center, ${player.color}, ${baseColors.primary.main})`, backgroundImage:
stopRandom || !settings.useRandomStartingPlayerInterval
? `radial-gradient(circle at center, ${player.color}, ${baseColors.primary.main})`
: 'none',
}} }}
onClick={() => { onClick={() => {
clearTimeout(playingTimerRef.current); clearTimeout(playingTimerRef.current);
localStorage.setItem('playing', 'true'); localStorage.setItem('playing', 'true');
player.showStartingPlayer = false; setPlaying(true);
updatePlayer(player);
}} }}
> >
<DynamicText <DynamicText
@@ -214,8 +224,12 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
> >
<div className="flex flex-col justify-center items-center"> <div className="flex flex-col justify-center items-center">
<Paragraph>👑</Paragraph> <Paragraph>👑</Paragraph>
{(stopRandom || !settings.useRandomStartingPlayerInterval) && (
<>
<Paragraph>You start!</Paragraph> <Paragraph>You start!</Paragraph>
<Paragraph className="text-xl">(Press to hide)</Paragraph> <Paragraph className="text-xl">(Press to hide)</Paragraph>
</>
)}
</div> </div>
</DynamicText> </DynamicText>
</div> </div>
@@ -224,6 +238,12 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
{player.hasLost && ( {player.hasLost && (
<PlayerLostWrapper $rotation={player.settings.rotation} /> <PlayerLostWrapper $rotation={player.settings.rotation} />
)} )}
{settings.useRandomStartingPlayerInterval && !stopRandom && (
<div
className="flex absolute w-full h-full justify-center items-center pointer-events-none select-none webkit-user-select-none z-10"
style={{ backgroundColor: player.color }}
/>
)}
<CommanderDamageBar <CommanderDamageBar
opponents={opponents} opponents={opponents}
player={player} player={player}

View File

@@ -121,6 +121,26 @@ export const SettingsModal = ({ isOpen, closeModal }: SettingsModalProps) => {
this is enabled. this is enabled.
</Description> </Description>
</SettingContainer> </SettingContainer>
<SettingContainer>
<ToggleContainer>
<FormLabel>Randomize starting player with interval</FormLabel>
<Switch
checked={settings.useRandomStartingPlayerInterval}
onChange={() => {
setSettings({
...settings,
useRandomStartingPlayerInterval:
!settings.useRandomStartingPlayerInterval,
});
}}
/>
</ToggleContainer>
<Description>
Will randomize between all players at when starting a game,
pressing the screen aborts the interval and chooses the player
that has the crown.
</Description>
</SettingContainer>
<SettingContainer> <SettingContainer>
<ToggleContainer> <ToggleContainer>
<FormLabel>Keep Awake</FormLabel> <FormLabel>Keep Awake</FormLabel>

View File

@@ -1,50 +0,0 @@
import LifeCounter from '../LifeCounter/LifeCounter';
import { Player as PlayerType } from '../../Types/Player';
import { twc } from 'react-twc';
const getGridArea = (player: PlayerType) => {
switch (player.index) {
case 0:
return 'grid-in-player0';
case 1:
return 'grid-in-player1';
case 2:
return 'grid-in-player2';
case 3:
return 'grid-in-player3';
case 4:
return 'grid-in-player4';
case 5:
return 'grid-in-player5';
default:
throw new Error('Invalid player index');
}
};
const PlayerWrapper = twc.div`w-full h-full bg-black`;
export const Player = (players: PlayerType[], gridClasses: string) => {
return (
<PlayerWrapper>
<div className={`grid w-full h-full gap-1 box-border ${gridClasses} `}>
{players.map((player) => {
const gridArea = getGridArea(player);
return (
<div
key={player.index}
className={`flex justify-center items-center align-middle ${gridArea}`}
>
<LifeCounter
player={player}
opponents={players.filter(
(opponent) => opponent.index !== player.index
)}
/>
</div>
);
})}
</div>
</PlayerWrapper>
);
};

View File

@@ -0,0 +1,99 @@
import LifeCounter from '../LifeCounter/LifeCounter';
import { Player as PlayerType } from '../../Types/Player';
import { twc } from 'react-twc';
import { useEffect, useRef, useState } from 'react';
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
const getGridArea = (player: PlayerType) => {
switch (player.index) {
case 0:
return 'grid-in-player0';
case 1:
return 'grid-in-player1';
case 2:
return 'grid-in-player2';
case 3:
return 'grid-in-player3';
case 4:
return 'grid-in-player4';
case 5:
return 'grid-in-player5';
default:
throw new Error('Invalid player index');
}
};
const PlayersWrapper = twc.div`w-full h-full bg-black`;
export const Players = (players: PlayerType[], gridClasses: string) => {
const randomIntervalRef = useRef<NodeJS.Timeout | null>(null);
const [randomPlayerIndex, setRandomPlayerIndex] = useState<number>(
Math.floor(Math.random() * players.length)
);
const [stopRandom, setStopRandom] = useState<boolean>(false);
const { settings } = useGlobalSettings();
useEffect(() => {
if (settings.useRandomStartingPlayerInterval) {
randomIntervalRef.current = setInterval(() => {
let randomIndex: number;
do {
randomIndex = Math.floor(Math.random() * players.length);
} while (randomIndex === randomPlayerIndex);
setRandomPlayerIndex(randomIndex);
}, 100);
}
return () => {
if (randomIntervalRef.current) {
clearInterval(randomIntervalRef.current);
}
};
}, [
players.length,
randomPlayerIndex,
settings.useRandomStartingPlayerInterval,
]);
return (
<PlayersWrapper>
{settings.useRandomStartingPlayerInterval && (
<div
data-stopRandom={stopRandom}
className="absolute flex justify-center items-center bg-black bg-opacity-50 size-full z-50 cursor-pointer text-5xl data-[stopRandom=true]:hidden"
onClick={() => {
if (randomIntervalRef.current) {
clearInterval(randomIntervalRef.current);
randomIntervalRef.current = null;
setStopRandom(true);
}
}}
>
CHOOSE A PLAYER
</div>
)}
<div className={`grid w-full h-full gap-1 box-border ${gridClasses} `}>
{players.map((player) => {
const gridArea = getGridArea(player);
return (
<div
key={player.index}
className={`flex justify-center items-center align-middle ${gridArea}`}
>
<LifeCounter
stopRandom={stopRandom}
player={player}
opponents={players.filter(
(opponent) => opponent.index !== player.index
)}
isStartingPlayer={randomPlayerIndex === player.index}
/>
</div>
);
})}
</div>
</PlayersWrapper>
);
};

View File

@@ -1,7 +1,7 @@
import { useGlobalSettings } from '../../Hooks/useGlobalSettings'; import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
import { usePlayers } from '../../Hooks/usePlayers'; import { usePlayers } from '../../Hooks/usePlayers';
import { Orientation } from '../../Types/Settings'; import { Orientation } from '../../Types/Settings';
import { Player } from '../Player/Player'; import { Players } from '../Players/Players';
import { twc } from 'react-twc'; import { twc } from 'react-twc';
const MainWrapper = twc.div`w-[100dvmax] h-[100dvmin] overflow-hidden`; const MainWrapper = twc.div`w-[100dvmax] h-[100dvmin] overflow-hidden`;
@@ -14,52 +14,52 @@ export const Play = () => {
switch (players.length) { switch (players.length) {
case 1: case 1:
if (initialGameSettings?.orientation === Orientation.Portrait) { if (initialGameSettings?.orientation === Orientation.Portrait) {
Layout = Player(players, 'grid-areas-onePlayerPortrait'); Layout = Players(players, 'grid-areas-onePlayerPortrait');
} }
Layout = Player(players, 'grid-areas-onePlayerLandscape'); Layout = Players(players, 'grid-areas-onePlayerLandscape');
break; break;
case 2: case 2:
switch (initialGameSettings?.orientation) { switch (initialGameSettings?.orientation) {
case Orientation.Portrait: case Orientation.Portrait:
Layout = Player(players, 'grid-areas-twoPlayersOppositePortrait'); Layout = Players(players, 'grid-areas-twoPlayersOppositePortrait');
break; break;
default: default:
case Orientation.Landscape: case Orientation.Landscape:
Layout = Player(players, 'grid-areas-twoPlayersSameSideLandscape'); Layout = Players(players, 'grid-areas-twoPlayersSameSideLandscape');
break; break;
case Orientation.OppositeLandscape: case Orientation.OppositeLandscape:
Layout = Player(players, 'grid-areas-twoPlayersOppositeLandscape'); Layout = Players(players, 'grid-areas-twoPlayersOppositeLandscape');
break; break;
} }
break; break;
case 3: case 3:
if (initialGameSettings?.orientation === Orientation.Portrait) { if (initialGameSettings?.orientation === Orientation.Portrait) {
Layout = Player(players, 'grid-areas-threePlayersSide'); Layout = Players(players, 'grid-areas-threePlayersSide');
break; break;
} }
Layout = Player(players, 'grid-areas-threePlayers'); Layout = Players(players, 'grid-areas-threePlayers');
break; break;
default: default:
case 4: case 4:
if (initialGameSettings?.orientation === Orientation.Portrait) { if (initialGameSettings?.orientation === Orientation.Portrait) {
Layout = Player(players, 'grid-areas-fourPlayerPortrait'); Layout = Players(players, 'grid-areas-fourPlayerPortrait');
break; break;
} }
Layout = Player(players, 'grid-areas-fourPlayer'); Layout = Players(players, 'grid-areas-fourPlayer');
break; break;
case 5: case 5:
if (initialGameSettings?.orientation === Orientation.Portrait) { if (initialGameSettings?.orientation === Orientation.Portrait) {
Layout = Player(players, 'grid-areas-fivePlayersSide'); Layout = Players(players, 'grid-areas-fivePlayersSide');
break; break;
} }
Layout = Player(players, 'grid-areas-fivePlayers'); Layout = Players(players, 'grid-areas-fivePlayers');
break; break;
case 6: case 6:
if (initialGameSettings?.orientation === Orientation.Portrait) { if (initialGameSettings?.orientation === Orientation.Portrait) {
Layout = Player(players, 'grid-areas-sixPlayersSide'); Layout = Players(players, 'grid-areas-sixPlayersSide');
break; break;
} }
Layout = Player(players, 'grid-areas-sixPlayers'); Layout = Players(players, 'grid-areas-sixPlayers');
break; break;
} }

View File

@@ -191,10 +191,8 @@ export const createInitialPlayers = ({
}: InitialGameSettings): Player[] => { }: InitialGameSettings): Player[] => {
const players: Player[] = []; const players: Player[] = [];
const availableColors = [...presetColors]; // Create a copy of the colors array const availableColors = [...presetColors]; // Create a copy of the colors array
const firstPlayerIndex = Math.floor(Math.random() * numberOfPlayers);
for (let i = 0; i <= numberOfPlayers - 1; i++) { for (let i = 0; i <= numberOfPlayers - 1; i++) {
const isStartingPlayer = i === firstPlayerIndex;
const colorIndex = Math.floor(Math.random() * availableColors.length); const colorIndex = Math.floor(Math.random() * availableColors.length);
const color = availableColors[colorIndex]; const color = availableColors[colorIndex];
@@ -224,8 +222,6 @@ export const createInitialPlayers = ({
usePoison: false, usePoison: false,
rotation, rotation,
}, },
isStartingPlayer,
showStartingPlayer: isStartingPlayer,
extraCounters: [], extraCounters: [],
commanderDamage, commanderDamage,
hasLost: false, hasLost: false,

View File

@@ -39,6 +39,7 @@ export const GlobalSettingsProvider = ({
keepAwake: true, keepAwake: true,
showStartingPlayer: true, showStartingPlayer: true,
showPlayerMenuCog: true, showPlayerMenuCog: true,
useRandomStartingPlayerInterval: false,
} }
); );

View File

@@ -50,10 +50,6 @@ export const PlayersProvider = ({ children }: { children: ReactNode }) => {
return; return;
} }
const startingPlayerIndex = Math.floor(
Math.random() * initialGameSettings.numberOfPlayers
);
players.forEach((player: Player) => { players.forEach((player: Player) => {
player.commanderDamage.map((damage) => { player.commanderDamage.map((damage) => {
damage.damageTotal = 0; damage.damageTotal = 0;
@@ -68,14 +64,6 @@ export const PlayersProvider = ({ children }: { children: ReactNode }) => {
player.hasLost = false; player.hasLost = false;
const isStartingPlayer = player.index === startingPlayerIndex;
player.isStartingPlayer = isStartingPlayer;
if (player.isStartingPlayer) {
player.showStartingPlayer = true;
}
updatePlayer(player); updatePlayer(player);
}); });
localStorage.setItem('playing', 'false'); localStorage.setItem('playing', 'false');

View File

@@ -5,8 +5,6 @@ export type Player = {
settings: PlayerSettings; settings: PlayerSettings;
commanderDamage: CommanderDamage[]; commanderDamage: CommanderDamage[];
extraCounters: ExtraCounter[]; extraCounters: ExtraCounter[];
isStartingPlayer: boolean;
showStartingPlayer: boolean;
hasLost: boolean; hasLost: boolean;
isSide: boolean; isSide: boolean;
}; };

View File

@@ -17,6 +17,7 @@ export type Settings = {
showStartingPlayer: boolean; showStartingPlayer: boolean;
showPlayerMenuCog: boolean; showPlayerMenuCog: boolean;
goFullscreenOnStart: boolean; goFullscreenOnStart: boolean;
useRandomStartingPlayerInterval: boolean;
}; };
export type InitialGameSettings = { export type InitialGameSettings = {