diff --git a/src/Components/LifeCounter/LifeCounter.tsx b/src/Components/LifeCounter/LifeCounter.tsx index a7fd80c..f41705d 100644 --- a/src/Components/LifeCounter/LifeCounter.tsx +++ b/src/Components/LifeCounter/LifeCounter.tsx @@ -1,7 +1,6 @@ import { useEffect, useRef, useState } from 'react'; import { useSwipeable } from 'react-swipeable'; import { twc } from 'react-twc'; -import { baseColors } from '../../../tailwind.config'; import { useAnalytics } from '../../Hooks/useAnalytics'; import { useGlobalSettings } from '../../Hooks/useGlobalSettings'; import { usePlayers } from '../../Hooks/usePlayers'; @@ -15,8 +14,8 @@ import { import { LoseGameButton } from '../Buttons/LoseButton'; import CommanderDamageBar from '../Counters/CommanderDamageBar'; import ExtraCountersBar from '../Counters/ExtraCountersBar'; -import { Paragraph } from '../Misc/TextComponents'; import PlayerMenu from '../Players/PlayerMenu'; +import { StartingPlayerCard } from '../PreStartGame/StartingPlayerCard'; import Health from './Health'; const SettingsButtonTwc = twc.button((props) => [ @@ -77,8 +76,6 @@ const PlayerLostWrapper = twc.div((props) => [ : '', ]); -const DynamicText = twc.div`text-[8vmin] whitespace-nowrap`; - const hasCommanderDamageReached21 = (player: Player) => { const commanderDamageTotals = player.commanderDamage.map( (commanderDamage) => commanderDamage.damageTotal @@ -116,9 +113,7 @@ const RECENT_DIFFERENCE_TTL = 3_000; const LifeCounter = ({ player, opponents }: LifeCounterProps) => { const { updatePlayer, updateLifeTotal } = usePlayers(); - const { settings, playing, setPlaying, stopPlayerRandomization } = - useGlobalSettings(); - const playingTimerRef = useRef(undefined); + const { settings, playing } = useGlobalSettings(); const recentDifferenceTimerRef = useRef( undefined ); @@ -185,28 +180,6 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [document.body.clientHeight, document.body.clientWidth]); - useEffect(() => { - if ( - player.isStartingPlayer && - ((!playing && - settings.useRandomStartingPlayerInterval && - stopPlayerRandomization) || - (!settings.useRandomStartingPlayerInterval && !playing)) - ) { - playingTimerRef.current = setTimeout(() => { - setPlaying(true); - }, 10_000); - } - - return () => clearTimeout(playingTimerRef.current); - }, [ - player.isStartingPlayer, - playing, - setPlaying, - settings.useRandomStartingPlayerInterval, - stopPlayerRandomization, - ]); - player.settings.rotation === Rotation.SideFlipped || player.settings.rotation === Rotation.Side; @@ -227,12 +200,6 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => { ? player.settings.rotation - 90 : player.settings.rotation; - const calcTextRotation = - player.settings.rotation === Rotation.SideFlipped || - player.settings.rotation === Rotation.Side - ? player.settings.rotation - 180 - : player.settings.rotation; - const amountOfPlayers = opponents.length + 1; return ( @@ -245,54 +212,12 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => { {amountOfPlayers > 1 && !playing && settings.showStartingPlayer && - player.isStartingPlayer && ( -
{ - clearTimeout(playingTimerRef.current); - setPlaying(true); - }} - > - -
- 👑 - {(stopPlayerRandomization || - !settings.useRandomStartingPlayerInterval) && ( - <> - You start! - (Press to hide) - - )} -
-
-
- )} + player.isStartingPlayer && } {player.hasLost && ( )} - {amountOfPlayers > 1 && - settings.showStartingPlayer && - settings.useRandomStartingPlayerInterval && - !stopPlayerRandomization && - !playing && ( -
- )} + {

⚙️ Settings ⚙️

- - - Show Start Player - { - setSettings({ - ...settings, - showStartingPlayer: !settings.showStartingPlayer, - }); - }} - /> - - - On start or reset of game, will pick a random player who will - start first if this is enabled. - + + {/* @ts-expect-error is defined in vite.config.ts*/} + Current version: {APP_VERSION}{' '} + {isLatestVersion && ( + (latest) + )} + + {!isLatestVersion && newVersion && ( + + New version ({newVersion}) is available!{' '} + + )} + {!isLatestVersion && newVersion && ( + + )} + + - Show Player Menu Cog + { @@ -123,27 +131,73 @@ export const SettingsModal = ({ isOpen, closeModal }: SettingsModalProps) => { - Randomize starting player with interval + { setSettings({ ...settings, - useRandomStartingPlayerInterval: - !settings.useRandomStartingPlayerInterval, + showStartingPlayer: !settings.showStartingPlayer, }); }} /> - Will randomize between all players at when starting a game, - pressing the screen aborts the interval and chooses the player - that has the crown. + On start or reset of game, will pick a random starting player, + according to the Pre-Start mode + +
+ + +
+
+ Different ways to determine the starting player before the game + starts. +
+ + {settings.preStartMode === PreStartMode.None && ( +
+ None: The starting + player will simply be shown. +
+ )} + {settings.preStartMode === PreStartMode.RandomKing && ( +
+ Random King:{' '} + Randomly pass a crown between all players, press the screen to + stop it. The player who has the crown when it stops gets to + start. +
+ )} + {settings.preStartMode === PreStartMode.FingerGame && ( +
+ Finger Game: All + players put a finger on the screen, one will be chosen at + random. +
+ )} +
- Keep Awake + { @@ -161,7 +215,10 @@ export const SettingsModal = ({ isOpen, closeModal }: SettingsModalProps) => { - Go fullscreen on start (Android only) + { @@ -197,31 +254,6 @@ export const SettingsModal = ({ isOpen, closeModal }: SettingsModalProps) => { )} - - - {/* @ts-expect-error is defined in vite.config.ts*/} - Current version: {APP_VERSION}{' '} - {isLatestVersion && ( - (latest) - )} - - {!isLatestVersion && newVersion && ( - - New version ({newVersion}) is available!{' '} - - )} - - {!isLatestVersion && newVersion && ( - - )} - + {touchPoints.length !== players.length && ( +
+ Waiting for fingers
+
+ {touchPoints.length}/{players.length} +
+
+ )} + + {touchPoints.map((point, index) => ( +
+ ))} +
+ ); +}; diff --git a/src/Components/PreStartGame/Games/RandomKing/RandomKingPlayers.tsx b/src/Components/PreStartGame/Games/RandomKing/RandomKingPlayers.tsx new file mode 100644 index 0000000..d5d0780 --- /dev/null +++ b/src/Components/PreStartGame/Games/RandomKing/RandomKingPlayers.tsx @@ -0,0 +1,49 @@ +import { usePlayers } from '../../../../Hooks/usePlayers'; +import { Player } from '../../../../Types/Player'; +import { GridLayout } from '../../../Views/Play'; +import { RoulettePlayerCard } from './RoulettePlayerCard'; + +const getGridArea = (player: Player) => { + 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'); + } +}; + +export const RandomKingPlayers = ({ + gridLayout, +}: { + gridLayout: GridLayout; +}) => { + const { players } = usePlayers(); + + return ( +
+
+ {players.map((player) => { + const gridArea = getGridArea(player); + return ( +
+ +
+ ); + })} +
+
+ ); +}; diff --git a/src/Components/PreStartGame/Games/RandomKing/RandomKingSelectWrapper.tsx b/src/Components/PreStartGame/Games/RandomKing/RandomKingSelectWrapper.tsx new file mode 100644 index 0000000..009054d --- /dev/null +++ b/src/Components/PreStartGame/Games/RandomKing/RandomKingSelectWrapper.tsx @@ -0,0 +1,94 @@ +import { useEffect, useRef } from 'react'; +import { useGlobalSettings } from '../../../../Hooks/useGlobalSettings'; +import { usePlayers } from '../../../../Hooks/usePlayers'; + +export const RandomKingSelectWrapper = () => { + const { setRandomizingPlayer } = useGlobalSettings(); + + const randomIntervalRef = useRef(null); + const prevRandomIndexRef = useRef(-1); + + const { settings, randomizingPlayer, setPreStartCompleted } = + useGlobalSettings(); + + const { players, setPlayers } = usePlayers(); + + useEffect(() => { + if ( + players.length > 1 && + settings.showStartingPlayer && + randomizingPlayer + ) { + randomIntervalRef.current = setInterval(() => { + let randomIndex: number; + + do { + randomIndex = Math.floor(Math.random() * players.length); + } while (randomIndex === prevRandomIndexRef.current); + + prevRandomIndexRef.current = randomIndex; + setPlayers( + players.map((p) => + p.index === prevRandomIndexRef.current + ? { + ...p, + isStartingPlayer: true, + } + : { + ...p, + isStartingPlayer: false, + } + ) + ); + }, 100); + } + + const randomPlayerIndex = Math.floor(Math.random() * players.length); + setPlayers( + players.map((p) => + p.index === randomPlayerIndex + ? { + ...p, + isStartingPlayer: true, + } + : { + ...p, + isStartingPlayer: false, + } + ) + ); + + return () => { + if (randomIntervalRef.current) { + clearInterval(randomIntervalRef.current); + } + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [players.length, setPlayers, randomizingPlayer]); + + const gradientColors = players.map((player) => player.color).join(', '); + + return ( +
{ + if (randomIntervalRef.current) { + clearInterval(randomIntervalRef.current); + randomIntervalRef.current = null; + } + setRandomizingPlayer(false); + setPreStartCompleted(true); + }} + > +
+
+

PRESS TO SELECT PLAYER

+
+
+ ); +}; diff --git a/src/Components/PreStartGame/Games/RandomKing/RoulettePlayerCard.tsx b/src/Components/PreStartGame/Games/RandomKing/RoulettePlayerCard.tsx new file mode 100644 index 0000000..76a9a0d --- /dev/null +++ b/src/Components/PreStartGame/Games/RandomKing/RoulettePlayerCard.tsx @@ -0,0 +1,58 @@ +import { useEffect, useRef } from 'react'; +import { useGlobalSettings } from '../../../../Hooks/useGlobalSettings'; +import { Player, Rotation } from '../../../../Types/Player'; +import { Paragraph } from '../../../Misc/TextComponents'; +import { DynamicText } from '../../StartingPlayerCard'; + +export const RoulettePlayerCard = ({ player }: { player: Player }) => { + const startPlayingTimerRef = useRef(undefined); + + const { settings, randomizingPlayer, playing, setPlaying } = + useGlobalSettings(); + + useEffect(() => { + if ( + player.isStartingPlayer && + ((!playing && randomizingPlayer) || !playing) + ) { + startPlayingTimerRef.current = setTimeout(() => { + setPlaying(true); + }, 10_000); + } + + return () => clearTimeout(startPlayingTimerRef.current); + }, [ + player.isStartingPlayer, + playing, + setPlaying, + settings.preStartMode, + randomizingPlayer, + ]); + + const calcTextRotation = + player.settings.rotation === Rotation.SideFlipped || + player.settings.rotation === Rotation.Side + ? player.settings.rotation - 180 + : player.settings.rotation; + + return ( +
+
+ {player.isStartingPlayer && ( + +
+ 👑 +
+
+ )} +
+
+ ); +}; diff --git a/src/Components/PreStartGame/PreStart.tsx b/src/Components/PreStartGame/PreStart.tsx new file mode 100644 index 0000000..a49f254 --- /dev/null +++ b/src/Components/PreStartGame/PreStart.tsx @@ -0,0 +1,30 @@ +import { useGlobalSettings } from '../../Hooks/useGlobalSettings'; +import { PreStartMode } from '../../Types/Settings'; +import { GridLayout } from '../Views/Play'; +import { FingerGame } from './Games/FingerGame'; +import { RandomKingPlayers } from './Games/RandomKing/RandomKingPlayers'; +import { RandomKingSelectWrapper } from './Games/RandomKing/RandomKingSelectWrapper'; + +export const PreStart = ({ gridLayout }: { gridLayout: GridLayout }) => { + const { settings, randomizingPlayer, goToStart } = useGlobalSettings(); + + if (settings.preStartMode === PreStartMode.RandomKing) { + if (!randomizingPlayer) { + return null; + } + + return ( + <> + + + + ); + } + + if (settings.preStartMode === PreStartMode.FingerGame) { + return ; + } + + goToStart(); + return null; +}; diff --git a/src/Components/PreStartGame/StartingPlayerCard.tsx b/src/Components/PreStartGame/StartingPlayerCard.tsx new file mode 100644 index 0000000..c23c813 --- /dev/null +++ b/src/Components/PreStartGame/StartingPlayerCard.tsx @@ -0,0 +1,60 @@ +import { twc } from 'react-twc'; +import { baseColors } from '../../../tailwind.config'; +import { useGlobalSettings } from '../../Hooks/useGlobalSettings'; +import { Player, Rotation } from '../../Types/Player'; +import { PreStartMode } from '../../Types/Settings'; +import { Paragraph } from '../Misc/TextComponents'; + +export const DynamicText = twc.div`text-[8vmin] whitespace-nowrap`; + +export const StartingPlayerCard = ({ player }: { player: Player }) => { + const { settings, setPlaying, randomizingPlayer } = useGlobalSettings(); + + const calcTextRotation = + player.settings.rotation === Rotation.SideFlipped || + player.settings.rotation === Rotation.Side + ? player.settings.rotation - 180 + : player.settings.rotation; + + const calcRotation = + player.settings.rotation === Rotation.SideFlipped || + player.settings.rotation === Rotation.Side + ? player.settings.rotation - 90 + : player.settings.rotation; + + return ( +
{ + setPlaying(true); + }} + > + +
+ 👑 + {(!randomizingPlayer || + (settings.preStartMode !== PreStartMode.None && + settings.preStartMode !== PreStartMode.FingerGame)) && ( + <> + You start! + (Press to hide) + + )} +
+
+
+ ); +}; diff --git a/src/Components/Views/Play.tsx b/src/Components/Views/Play.tsx index 17c6e1e..bb65b56 100644 --- a/src/Components/Views/Play.tsx +++ b/src/Components/Views/Play.tsx @@ -1,67 +1,115 @@ +import { useEffect } from 'react'; +import { twc } from 'react-twc'; +import { twGridTemplateAreas } from '../../../tailwind.config'; import { useGlobalSettings } from '../../Hooks/useGlobalSettings'; import { usePlayers } from '../../Hooks/usePlayers'; -import { Orientation } from '../../Types/Settings'; +import { Orientation, PreStartMode } from '../../Types/Settings'; import { Players } from '../Players/Players'; -import { twc } from 'react-twc'; +import { PreStart } from '../PreStartGame/PreStart'; -const MainWrapper = twc.div`w-[100dvmax] h-[100dvmin] overflow-hidden`; +const MainWrapper = twc.div`w-[100dvmax] h-[100dvmin] overflow-hidden, setPlayers`; + +type GridTemplateAreasKeys = keyof typeof twGridTemplateAreas; + +export type GridLayout = `grid-areas-${GridTemplateAreasKeys}`; export const Play = () => { - const { players } = usePlayers(); - const { initialGameSettings } = useGlobalSettings(); + const { players, setPlayers } = usePlayers(); + const { initialGameSettings, playing, settings, preStartCompleted } = + useGlobalSettings(); - let Layout: JSX.Element; + let gridLayout: GridLayout; switch (players.length) { case 1: if (initialGameSettings?.orientation === Orientation.Portrait) { - Layout = Players(players, 'grid-areas-onePlayerPortrait'); + gridLayout = 'grid-areas-onePlayerPortrait'; } - Layout = Players(players, 'grid-areas-onePlayerLandscape'); + gridLayout = 'grid-areas-onePlayerLandscape'; break; case 2: switch (initialGameSettings?.orientation) { case Orientation.Portrait: - Layout = Players(players, 'grid-areas-twoPlayersOppositePortrait'); + gridLayout = 'grid-areas-twoPlayersOppositePortrait'; break; default: case Orientation.Landscape: - Layout = Players(players, 'grid-areas-twoPlayersSameSideLandscape'); + gridLayout = 'grid-areas-twoPlayersSameSideLandscape'; break; case Orientation.OppositeLandscape: - Layout = Players(players, 'grid-areas-twoPlayersOppositeLandscape'); + gridLayout = 'grid-areas-twoPlayersOppositeLandscape'; break; } break; case 3: if (initialGameSettings?.orientation === Orientation.Portrait) { - Layout = Players(players, 'grid-areas-threePlayersSide'); + gridLayout = 'grid-areas-threePlayersSide'; break; } - Layout = Players(players, 'grid-areas-threePlayers'); + gridLayout = 'grid-areas-threePlayers'; break; default: case 4: if (initialGameSettings?.orientation === Orientation.Portrait) { - Layout = Players(players, 'grid-areas-fourPlayerPortrait'); + gridLayout = 'grid-areas-fourPlayerPortrait'; break; } - Layout = Players(players, 'grid-areas-fourPlayer'); + gridLayout = 'grid-areas-fourPlayer'; break; case 5: if (initialGameSettings?.orientation === Orientation.Portrait) { - Layout = Players(players, 'grid-areas-fivePlayersSide'); + gridLayout = 'grid-areas-fivePlayersSide'; break; } - Layout = Players(players, 'grid-areas-fivePlayers'); + gridLayout = 'grid-areas-fivePlayers'; break; case 6: if (initialGameSettings?.orientation === Orientation.Portrait) { - Layout = Players(players, 'grid-areas-sixPlayersSide'); + gridLayout = 'grid-areas-sixPlayersSide'; break; } - Layout = Players(players, 'grid-areas-sixPlayers'); + gridLayout = 'grid-areas-sixPlayers'; break; } - return {Layout}; + useEffect(() => { + if (settings.preStartMode !== PreStartMode.None) { + return; + } + + const randomIndex = Math.floor(Math.random() * players.length); + + setPlayers( + players.map((p) => + p.index === randomIndex + ? { + ...p, + isStartingPlayer: true, + } + : { + ...p, + isStartingPlayer: false, + } + ) + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + if ( + !preStartCompleted && + settings.preStartMode !== PreStartMode.None && + !playing && + settings.showStartingPlayer + ) { + return ( + + + + ); + } + + return ( + + + + ); }; diff --git a/src/Components/Views/StartMenu/StartMenu.tsx b/src/Components/Views/StartMenu/StartMenu.tsx index 3993bea..a034b6d 100644 --- a/src/Components/Views/StartMenu/StartMenu.tsx +++ b/src/Components/Views/StartMenu/StartMenu.tsx @@ -12,6 +12,7 @@ import { GameFormat, InitialGameSettings, Orientation, + PreStartMode, } from '../../../Types/Settings'; import { InfoModal } from '../../Misc/InfoModal'; import { SettingsModal } from '../../Misc/SettingsModal'; @@ -89,6 +90,7 @@ const Start = () => { setInitialGameSettings, settings, isPWA, + setRandomizingPlayer, } = useGlobalSettings(); const [openInfoModal, setOpenInfoModal] = useState(false); @@ -126,6 +128,7 @@ const Start = () => { setInitialGameSettings(initialGameSettings); setPlayers(createInitialPlayers(initialGameSettings)); setShowPlay(true); + setRandomizingPlayer(settings.preStartMode === PreStartMode.RandomKing); localStorage.setItem('playing', 'false'); localStorage.setItem('showPlay', 'true'); }; diff --git a/src/Contexts/GlobalSettingsContext.tsx b/src/Contexts/GlobalSettingsContext.tsx index fa06150..5a0263c 100644 --- a/src/Contexts/GlobalSettingsContext.tsx +++ b/src/Contexts/GlobalSettingsContext.tsx @@ -24,9 +24,11 @@ export type GlobalSettingsContextType = { setSettings: (settings: Settings) => void; playing: boolean; setPlaying: (playing: boolean) => void; - stopPlayerRandomization: boolean; - setStopPlayerRandomization: (stopRandom: boolean) => void; + randomizingPlayer: boolean; + setRandomizingPlayer: (stopRandom: boolean) => void; isPWA: boolean; + preStartCompleted: boolean; + setPreStartCompleted: (completed: boolean) => void; }; export const GlobalSettingsContext = diff --git a/src/Data/getInitialPlayers.ts b/src/Data/getInitialPlayers.ts index 73dd862..9c899db 100644 --- a/src/Data/getInitialPlayers.ts +++ b/src/Data/getInitialPlayers.ts @@ -1,7 +1,7 @@ import { Player, Rotation } from '../Types/Player'; import { InitialGameSettings, Orientation } from '../Types/Settings'; -const presetColors = [ +export const presetColors = [ '#F06292', // Light Pink '#4DB6AC', // Teal '#FFA726', // Orange diff --git a/src/Providers/GlobalSettingsProvider.tsx b/src/Providers/GlobalSettingsProvider.tsx index 4dba78e..b5ca954 100644 --- a/src/Providers/GlobalSettingsProvider.tsx +++ b/src/Providers/GlobalSettingsProvider.tsx @@ -7,8 +7,10 @@ import { import { useAnalytics } from '../Hooks/useAnalytics'; import { InitialGameSettings, - InitialGameSettingsSchema, + initialGameSettingsSchema, + PreStartMode, Settings, + settingsSchema, } from '../Types/Settings'; export const GlobalSettingsProvider = ({ @@ -22,6 +24,7 @@ export const GlobalSettingsProvider = ({ const savedGameSettings = localStorage.getItem('initialGameSettings'); const savedSettings = localStorage.getItem('settings'); const savedPlaying = localStorage.getItem('playing'); + const savedPreStartComplete = localStorage.getItem('preStartComplete'); const [playing, setPlaying] = useState( savedPlaying ? savedPlaying === 'true' : false @@ -31,38 +34,55 @@ export const GlobalSettingsProvider = ({ localStorage.setItem('playing', String(playing)); }; + const [preStartCompleted, setPreStartCompleted] = useState( + savedPreStartComplete ? savedPreStartComplete === 'true' : false + ); + const [showPlay, setShowPlay] = useState( savedShowPlay ? savedShowPlay === 'true' : false ); - const [stopPlayerRandomization, setStopPlayerRandomization] = - useState(false); + const [randomizingPlayer, setRandomizingPlayer] = useState( + savedSettings + ? Boolean(JSON.parse(savedSettings).preStartMode === 'random-king') + : true + ); const [initialGameSettings, setInitialGameSettings] = useState( savedGameSettings ? JSON.parse(savedGameSettings) : null ); + const parsedSettings = settingsSchema.safeParse( + JSON.parse(savedSettings ?? '') + ); const [settings, setSettings] = useState( - savedSettings - ? JSON.parse(savedSettings) + parsedSettings.success + ? parsedSettings.data : { goFullscreenOnStart: true, keepAwake: true, showStartingPlayer: true, showPlayerMenuCog: true, - useRandomStartingPlayerInterval: false, + preStartMode: PreStartMode.None, } ); + const setSettingsAndLocalStorage = (settings: Settings) => { + setSettings(settings); + localStorage.setItem('settings', JSON.stringify(settings)); + }; + const removeLocalStorage = async () => { localStorage.removeItem('initialGameSettings'); localStorage.removeItem('players'); localStorage.removeItem('playing'); localStorage.removeItem('showPlay'); + localStorage.removeItem('preStartComplete'); setPlaying(false); setShowPlay(false); + setPreStartCompleted(false); }; useEffect(() => { @@ -74,7 +94,7 @@ export const GlobalSettingsProvider = ({ //parse existing game settings with zod schema const parsedInitialGameSettings = - InitialGameSettingsSchema.safeParse(initialGameSettings); + initialGameSettingsSchema.safeParse(initialGameSettings); if (!parsedInitialGameSettings.success) { removeLocalStorage(); @@ -88,10 +108,6 @@ export const GlobalSettingsProvider = ({ ); }, [initialGameSettings, savedGameSettings]); - useEffect(() => { - localStorage.setItem('settings', JSON.stringify(settings)); - }, [settings]); - const [isFullscreen, setIsFullscreen] = useState(false); useEffect(() => { @@ -155,6 +171,11 @@ export const GlobalSettingsProvider = ({ } }; + const setPreStartCompletedAndLocalStorage = (preStartComplete: boolean) => { + setPreStartCompleted(preStartComplete); + localStorage.setItem('playing', String(playing)); + }; + return { fullscreen: { isFullscreen, enableFullscreen, disableFullscreen }, wakeLock: { @@ -173,24 +194,27 @@ export const GlobalSettingsProvider = ({ initialGameSettings, setInitialGameSettings, settings, - setSettings, - stopPlayerRandomization, - setStopPlayerRandomization, + setSettings: setSettingsAndLocalStorage, + randomizingPlayer, + setRandomizingPlayer, isPWA: window?.matchMedia('(display-mode: standalone)').matches, + preStartCompleted, + setPreStartCompleted: setPreStartCompletedAndLocalStorage, }; }, [ - active, - analytics, - initialGameSettings, isFullscreen, isSupported, - playing, release, + active, request, - settings, - showPlay, - stopPlayerRandomization, type, + showPlay, + playing, + initialGameSettings, + settings, + randomizingPlayer, + preStartCompleted, + analytics, ]); return ( diff --git a/src/Types/Settings.ts b/src/Types/Settings.ts index e697560..8399300 100644 --- a/src/Types/Settings.ts +++ b/src/Types/Settings.ts @@ -12,12 +12,18 @@ export enum GameFormat { TwoHeadedGiant = 'two-headed-giant', } +export enum PreStartMode { + None = 'none', + RandomKing = 'random-king', + FingerGame = 'finger-game', +} + export type Settings = { keepAwake: boolean; showStartingPlayer: boolean; showPlayerMenuCog: boolean; goFullscreenOnStart: boolean; - useRandomStartingPlayerInterval: boolean; + preStartMode: PreStartMode; }; export type InitialGameSettings = { @@ -28,10 +34,18 @@ export type InitialGameSettings = { orientation: Orientation; }; -export const InitialGameSettingsSchema = z.object({ +export const initialGameSettingsSchema = z.object({ startingLifeTotal: z.number().min(1).max(200).default(20), useCommanderDamage: z.boolean().default(false), gameFormat: z.nativeEnum(GameFormat).optional(), numberOfPlayers: z.number().min(1).max(6).default(2), orientation: z.nativeEnum(Orientation).default(Orientation.Landscape), }); + +export const settingsSchema = z.object({ + keepAwake: z.boolean().default(true), + showStartingPlayer: z.boolean().default(true), + showPlayerMenuCog: z.boolean().default(true), + goFullscreenOnStart: z.boolean().default(true), + preStartMode: z.nativeEnum(PreStartMode).default(PreStartMode.None), +}); diff --git a/tailwind.config.ts b/tailwind.config.ts index fae3780..6494c8b 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -46,6 +46,37 @@ export const baseColors = { }, }; +export const twGridTemplateAreas = { + onePlayerLandscape: ['player0 player0'], + onePlayerPortrait: ['player0', 'player0'], + twoPlayersOppositeLandscape: ['player0', 'player1'], + twoPlayersOppositePortrait: ['player0 player1', 'player0 player1'], + twoPlayersSameSideLandscape: ['player0 player1'], + threePlayers: ['player0 player0', 'player1 player2'], + threePlayersSide: [ + 'player0 player0 player0 player2', + 'player1 player1 player1 player2', + ], + fourPlayerPortrait: [ + 'player0 player1 player1 player1 player1 player3', + 'player0 player2 player2 player2 player2 player3', + ], + fourPlayer: ['player0 player1', 'player2 player3'], + fivePlayers: [ + 'player0 player0 player0 player1 player1 player1', + 'player2 player2 player3 player3 player4 player4', + ], + fivePlayersSide: [ + 'player0 player0 player0 player0 player0 player1 player1 player1 player1 player1 player2', + 'player3 player3 player3 player3 player3 player4 player4 player4 player4 player4 player2', + ], + sixPlayers: ['player0 player1 player2', 'player3 player4 player5'], + sixPlayersSide: [ + 'player0 player1 player1 player1 player1 player1 player1 player2 player2 player2 player2 player2 player2 player3', + 'player0 player4 player4 player4 player4 player4 player4 player5 player5 player5 player5 player5 player5 player3', + ], +}; + /** @type {import('tailwindcss').Config} */ export default { content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], @@ -54,36 +85,7 @@ export default { modalSm: '548px', }, extend: { - gridTemplateAreas: { - onePlayerLandscape: ['player0 player0'], - onePlayerPortrait: ['player0', 'player0'], - twoPlayersOppositeLandscape: ['player0', 'player1'], - twoPlayersOppositePortrait: ['player0 player1', 'player0 player1'], - twoPlayersSameSideLandscape: ['player0 player1'], - threePlayers: ['player0 player0', 'player1 player2'], - threePlayersSide: [ - 'player0 player0 player0 player2', - 'player1 player1 player1 player2', - ], - fourPlayerPortrait: [ - 'player0 player1 player1 player1 player1 player3', - 'player0 player2 player2 player2 player2 player3', - ], - fourPlayer: ['player0 player1', 'player2 player3'], - fivePlayers: [ - 'player0 player0 player0 player1 player1 player1', - 'player2 player2 player3 player3 player4 player4', - ], - fivePlayersSide: [ - 'player0 player0 player0 player0 player0 player1 player1 player1 player1 player1 player2', - 'player3 player3 player3 player3 player3 player4 player4 player4 player4 player4 player2', - ], - sixPlayers: ['player0 player1 player2', 'player3 player4 player5'], - sixPlayersSide: [ - 'player0 player1 player1 player1 player1 player1 player1 player2 player2 player2 player2 player2 player2 player3', - 'player0 player4 player4 player4 player4 player4 player4 player5 player5 player5 player5 player5 player5 player3', - ], - }, + gridTemplateAreas: twGridTemplateAreas, colors: baseColors, keyframes: { fadeOut: {