Compare commits

...

6 Commits

Author SHA1 Message Date
Vikeo
ba9ca354fc fix crown being visible after reset 2024-04-01 18:48:06 +02:00
Viktor Rådberg
e79c728e6a Save game (#35)
* save

* hide scrollbar on desktop

* start menu styling

* bump
2024-04-01 00:28:59 +02:00
Viktor Rådberg
97f9bb75e5 bump 2024-03-31 19:58:00 +02:00
Viktor Rådberg
341cb3cde0 revert new api key 2024-03-31 19:57:16 +02:00
Viktor Rådberg
ce9c9ca997 default api key 2024-03-31 19:31:16 +02:00
Viktor Rådberg
ad485f059d debug release 2024-03-31 19:25:44 +02:00
10 changed files with 194 additions and 93 deletions

View File

@@ -1,12 +1,12 @@
index.html,1711905081687,5707f310c48bfbf4b0777999bec2b3216159b24efaac7d4ef5b3f774031a5bd2 index.html,1711905710499,4b604b23faec8d63a58e07b96d724a1aea56a7c86d57c9af791832ce87a552e7
manifest.webmanifest,1711905081687,f2bf253209f6e292a6b0dbfa06fb4ac188eb5f2dba568c3ad5511b9ed52c1f51 manifest.webmanifest,1711905710499,f2bf253209f6e292a6b0dbfa06fb4ac188eb5f2dba568c3ad5511b9ed52c1f51
manifest.json,1711905081475,7ff5111aa04a42adff3b38924ec467b6f345ed0309dba1486dc9b783b60c2a9d manifest.json,1711905710294,7ff5111aa04a42adff3b38924ec467b6f345ed0309dba1486dc9b783b60c2a9d
registerSW.js,1711905081687,5b6445b5215737c53ef0d379c151d57de165a056de2d1c5812ed614f158ebcbd sw.js,1711905711506,1ef2f4f40ec8dca15cc42d547462ade1e84314ea9722276f5994ccee7bbdf80f
sw.js,1711905082939,e3333155a3ccec8e315325341364c6fb441239fe35e70c1912b660bfdfbe5714 robots.txt,1711905710294,391d14b3c2f8c9143a27a28c7399585142228d4d1bdbe2c87ac946de411fa9a2
robots.txt,1711905081475,391d14b3c2f8c9143a27a28c7399585142228d4d1bdbe2c87ac946de411fa9a2 registerSW.js,1711905710499,5b6445b5215737c53ef0d379c151d57de165a056de2d1c5812ed614f158ebcbd
assets/index-7m_Zw4yH.css,1711905081687,37997d06b32b3d0c724c054913e3c0583b86f786358121cb1615e6646ff46b56 workbox-3e911b1d.js,1711905711507,d5dbc868a5c07af633d29de7ba3ffe37542aaaabdf33713b4298df31f92f11ff
workbox-3e911b1d.js,1711905082939,d5dbc868a5c07af633d29de7ba3ffe37542aaaabdf33713b4298df31f92f11ff assets/index-7m_Zw4yH.css,1711905710499,37997d06b32b3d0c724c054913e3c0583b86f786358121cb1615e6646ff46b56
logo192.png,1711905081474,3d1e2e6f064d4fd325828f21bb6493ff0dbf2390b0e7d2aba9f2b6def4829799 favicon.ico,1711905710294,8cefe5adbf00d337d8633fb744b9f2c4914f769b319be4bb7e184b7a4aa17160
favicon.ico,1711905081474,8cefe5adbf00d337d8633fb744b9f2c4914f769b319be4bb7e184b7a4aa17160 logo192.png,1711905710294,3d1e2e6f064d4fd325828f21bb6493ff0dbf2390b0e7d2aba9f2b6def4829799
logo512.png,1711905081475,892a4da1cc5434929a83a71fcbcb0d0d80aa82f44e3c21e9b20ffe9267197133 logo512.png,1711905710294,892a4da1cc5434929a83a71fcbcb0d0d80aa82f44e3c21e9b20ffe9267197133
assets/index-B28HjoRb.js,1711905081687,7475dc6e6963e1027f4ccaa29b43412d71fbe80a2dd42baccf9c058e69485a2f assets/index-CLJVONOc.js,1711905710499,22f3835412f82bb3f8a62e74a7f4602a9c43b447224790365dbcc6cbffb4e665

View File

@@ -1,7 +1,7 @@
{ {
"name": "life-trinket", "name": "life-trinket",
"private": true, "private": true,
"version": "0.9.42", "version": "0.9.6",
"type": "commonjs", "type": "commonjs",
"engines": { "engines": {
"node": ">=18", "node": ">=18",

View File

@@ -6,7 +6,7 @@ import { Cross } from '../../Icons/generated';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useAnalytics } from '../../Hooks/useAnalytics'; import { useAnalytics } from '../../Hooks/useAnalytics';
export const ModalWrapper = twc.div`absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 h-[85vh] bg-background-default p-4 overflow-scroll rounded-2xl border-none text-text-primary w-[95vw] max-w-[548px]`; export const ModalWrapper = twc.div`absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-[47.5%] h-[95%] bg-background-default p-4 overflow-scroll rounded-2xl border-none text-text-primary w-[95vw] max-w-[548px]`;
type InfoModalProps = { type InfoModalProps = {
isOpen: boolean; isOpen: boolean;

View File

@@ -2,6 +2,7 @@ import { Checkbox } from '@mui/material';
import { useRef } from 'react'; import { useRef } from 'react';
import { twc } from 'react-twc'; import { twc } from 'react-twc';
import { theme } from '../../Data/theme'; import { theme } from '../../Data/theme';
import { useAnalytics } from '../../Hooks/useAnalytics';
import { useGlobalSettings } from '../../Hooks/useGlobalSettings'; import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
import { usePlayers } from '../../Hooks/usePlayers'; import { usePlayers } from '../../Hooks/usePlayers';
import { useSafeRotate } from '../../Hooks/useSafeRotate'; import { useSafeRotate } from '../../Hooks/useSafeRotate';
@@ -17,8 +18,8 @@ import {
ResetGame, ResetGame,
} from '../../Icons/generated'; } from '../../Icons/generated';
import { Player, Rotation } from '../../Types/Player'; import { Player, Rotation } from '../../Types/Player';
import { PreStartMode } from '../../Types/Settings';
import { RotationDivProps } from '../Buttons/CommanderDamage'; import { RotationDivProps } from '../Buttons/CommanderDamage';
import { useAnalytics } from '../../Hooks/useAnalytics';
const PlayerMenuWrapper = twc.div` const PlayerMenuWrapper = twc.div`
flex flex
@@ -110,11 +111,14 @@ const PlayerMenu = ({
settings, settings,
setPlaying, setPlaying,
setRandomizingPlayer, setRandomizingPlayer,
saveCurrentGame,
initialGameSettings,
setPreStartCompleted,
} = useGlobalSettings(); } = useGlobalSettings();
const analytics = useAnalytics(); const analytics = useAnalytics();
const { updatePlayer, resetCurrentGame } = usePlayers(); const { updatePlayer, resetCurrentGame, players } = usePlayers();
const handleColorChange = (event: React.ChangeEvent<HTMLInputElement>) => { const handleColorChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const updatedPlayer = { ...player, color: event.target.value }; const updatedPlayer = { ...player, color: event.target.value };
@@ -131,12 +135,19 @@ const PlayerMenu = ({
const handleResetGame = () => { const handleResetGame = () => {
resetCurrentGame(); resetCurrentGame();
setShowPlayerMenu(false); setShowPlayerMenu(false);
setPlaying(false); setPlaying(false);
setRandomizingPlayer(true);
if (settings.preStartMode === PreStartMode.RandomKing) {
setRandomizingPlayer(true);
setPreStartCompleted(false);
}
analytics.trackEvent('reset_game'); analytics.trackEvent('reset_game');
}; };
const handleGoToStart = () => { const handleGoToStart = () => {
saveCurrentGame({ players, initialGameSettings });
goToStart(); goToStart();
setRandomizingPlayer(true); setRandomizingPlayer(true);
}; };
@@ -443,8 +454,14 @@ const PlayerMenu = ({
className="text-center text-text-primary" className="text-center text-text-primary"
style={{ fontSize: extraCountersSize }} style={{ fontSize: extraCountersSize }}
> >
End Game? Go to start?
</h1> </h1>
<div
style={{ fontSize: iconSize }}
className="text-center text-text-primary"
>
(Game will be saved)
</div>
<div className="flex justify-evenly gap-2"> <div className="flex justify-evenly gap-2">
<button <button
className="bg-primary-main border border-primary-dark text-text-primary rounded-lg flex-grow" className="bg-primary-main border border-primary-dark text-text-primary rounded-lg flex-grow"

View File

@@ -95,6 +95,7 @@ export const Play = () => {
}, []); }, []);
if ( if (
players.length > 1 &&
!preStartCompleted && !preStartCompleted &&
settings.preStartMode !== PreStartMode.None && settings.preStartMode !== PreStartMode.None &&
!playing && !playing &&

View File

@@ -19,9 +19,9 @@ import { SettingsModal } from '../../Misc/SettingsModal';
import { SupportMe } from '../../Misc/SupportMe'; import { SupportMe } from '../../Misc/SupportMe';
import { LayoutOptions } from './LayoutOptions'; import { LayoutOptions } from './LayoutOptions';
const MainWrapper = twc.div`w-[100dvw] h-fit pb-14 overflow-hidden items-center flex flex-col`; const MainWrapper = twc.div`w-[100dvw] h-fit pb-24 overflow-hidden items-center flex flex-col min-[349px]:pb-10`;
const StartButtonFooter = twc.div`w-full max-w-[548px] fixed bottom-4 z-1 items-center flex flex-col px-4 z-10`; const StartButtonFooter = twc.div`w-full max-w-[548px] fixed bottom-4 z-1 items-center flex flex-row flex-wrap px-4 z-10 gap-4`;
const SliderWrapper = twc.div`mx-8`; const SliderWrapper = twc.div`mx-8`;
@@ -92,6 +92,9 @@ const Start = () => {
isPWA, isPWA,
setRandomizingPlayer, setRandomizingPlayer,
version, version,
setPlaying,
savedGame,
saveCurrentGame,
} = useGlobalSettings(); } = useGlobalSettings();
const [openInfoModal, setOpenInfoModal] = useState(false); const [openInfoModal, setOpenInfoModal] = useState(false);
@@ -124,7 +127,7 @@ const Start = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [playerOptions.numberOfPlayers]); }, [playerOptions.numberOfPlayers]);
const doStartGame = () => { const doStartNewGame = () => {
if (!initialGameSettings) { if (!initialGameSettings) {
return; return;
} }
@@ -149,10 +152,40 @@ const Start = () => {
setInitialGameSettings(initialGameSettings); setInitialGameSettings(initialGameSettings);
setPlayers(createInitialPlayers(initialGameSettings)); setPlayers(createInitialPlayers(initialGameSettings));
setShowPlay(true);
setRandomizingPlayer(settings.preStartMode === PreStartMode.RandomKing); setRandomizingPlayer(settings.preStartMode === PreStartMode.RandomKing);
localStorage.setItem('playing', 'false'); setShowPlay(true);
localStorage.setItem('showPlay', 'true'); setPlaying(false);
};
const doResumeGame = () => {
if (!savedGame) {
return;
}
analytics.trackEvent('game_resumed', {
...initialGameSettings,
...settings,
isPWA,
});
try {
if (settings.goFullscreenOnStart) {
fullscreen.enableFullscreen();
}
} catch (error) {
console.error(error);
}
if (settings.keepAwake && !wakeLock.active) {
wakeLock.request();
}
setInitialGameSettings(savedGame.initialGameSettings);
setPlayers(savedGame.players);
saveCurrentGame(null);
setRandomizingPlayer(false);
setShowPlay(true);
setPlaying(true);
}; };
const valueText = (value: number) => { const valueText = (value: number) => {
@@ -196,48 +229,6 @@ const Start = () => {
<div className="overflow-hidden items-center flex flex-col max-w-[548px] w-full mb-8 px-4"> <div className="overflow-hidden items-center flex flex-col max-w-[548px] w-full mb-8 px-4">
<FormControl focused={false} style={{ width: '100%' }}> <FormControl focused={false} style={{ width: '100%' }}>
<FormLabel>Number of Players</FormLabel>
<SliderWrapper>
<Slider
title="Number of Players"
max={6}
min={1}
aria-label="Custom marks"
value={playerOptions?.numberOfPlayers ?? 4}
getAriaValueText={valueText}
step={null}
marks={playerMarks}
onChange={(_e, value) => {
setPlayerOptions({
...playerOptions,
numberOfPlayers: value as number,
orientation: Orientation.Landscape,
});
}}
/>
</SliderWrapper>
<FormLabel className="mt-[0.7rem]">Starting Health</FormLabel>
<SliderWrapper>
<Slider
title="Starting Health"
max={60}
min={20}
aria-label="Custom marks"
value={playerOptions?.startingLifeTotal ?? 40}
getAriaValueText={valueText}
step={10}
marks={healthMarks}
onChange={(_e, value) =>
setPlayerOptions({
...playerOptions,
startingLifeTotal: value as number,
orientation: Orientation.Landscape,
})
}
/>
</SliderWrapper>
<ToggleButtonsWrapper className="mt-4"> <ToggleButtonsWrapper className="mt-4">
<ToggleContainer> <ToggleContainer>
<FormLabel>Commander</FormLabel> <FormLabel>Commander</FormLabel>
@@ -295,6 +286,47 @@ const Start = () => {
</div> </div>
</div> </div>
</ToggleButtonsWrapper> </ToggleButtonsWrapper>
<FormLabel>Number of Players</FormLabel>
<SliderWrapper>
<Slider
title="Number of Players"
max={6}
min={1}
aria-label="Custom marks"
value={playerOptions?.numberOfPlayers ?? 4}
getAriaValueText={valueText}
step={null}
marks={playerMarks}
onChange={(_e, value) => {
setPlayerOptions({
...playerOptions,
numberOfPlayers: value as number,
orientation: Orientation.Landscape,
});
}}
/>
</SliderWrapper>
<FormLabel className="mt-[0.7rem]">Starting Health</FormLabel>
<SliderWrapper>
<Slider
title="Starting Health"
max={60}
min={20}
aria-label="Custom marks"
value={playerOptions?.startingLifeTotal ?? 40}
getAriaValueText={valueText}
step={10}
marks={healthMarks}
onChange={(_e, value) =>
setPlayerOptions({
...playerOptions,
startingLifeTotal: value as number,
orientation: Orientation.Landscape,
})
}
/>
</SliderWrapper>
<FormLabel>Layout</FormLabel> <FormLabel>Layout</FormLabel>
<LayoutOptions <LayoutOptions
@@ -318,14 +350,25 @@ const Start = () => {
</div> </div>
<StartButtonFooter> <StartButtonFooter>
<Button <button
size="large" className="flex flex-grow basis-0 justify-center self-center items-center bg-primary-main px-3 py-2 rounded-md text-text-primary min-w-[150px]"
variant="contained" onClick={doStartNewGame}
onClick={doStartGame}
fullWidth
> >
START GAME NEW GAME
</Button> </button>
{savedGame && (
<button
className="flex flex-grow basis-0 justify-center self-center items-center bg-primary-dark px-3 py-2 rounded-md text-text-primary min-w-[150px]"
onClick={doResumeGame}
>
RESUME&nbsp;
<span className="text-xs">
({savedGame.players.length}&nbsp;
{savedGame.players.length > 1 ? 'players' : 'player'})
</span>
</button>
)}
</StartButtonFooter> </StartButtonFooter>
</MainWrapper> </MainWrapper>
); );

View File

@@ -1,5 +1,6 @@
import { createContext } from 'react'; import { createContext } from 'react';
import { InitialGameSettings, Settings } from '../Types/Settings'; import { InitialGameSettings, Settings } from '../Types/Settings';
import { Player } from '../Types/Player';
type Version = { type Version = {
installedVersion: string; installedVersion: string;
@@ -8,6 +9,11 @@ type Version = {
remoteVersion?: string; remoteVersion?: string;
}; };
export type SavedGame = {
initialGameSettings: InitialGameSettings;
players: Player[];
} | null;
export type GlobalSettingsContextType = { export type GlobalSettingsContextType = {
fullscreen: { fullscreen: {
isFullscreen: boolean; isFullscreen: boolean;
@@ -25,7 +31,7 @@ export type GlobalSettingsContextType = {
goToStart: () => void; goToStart: () => void;
showPlay: boolean; showPlay: boolean;
setShowPlay: (showPlay: boolean) => void; setShowPlay: (showPlay: boolean) => void;
initialGameSettings: InitialGameSettings | null; initialGameSettings: InitialGameSettings;
setInitialGameSettings: (initialGameSettings: InitialGameSettings) => void; setInitialGameSettings: (initialGameSettings: InitialGameSettings) => void;
settings: Settings; settings: Settings;
setSettings: (settings: Settings) => void; setSettings: (settings: Settings) => void;
@@ -36,8 +42,9 @@ export type GlobalSettingsContextType = {
isPWA: boolean; isPWA: boolean;
preStartCompleted: boolean; preStartCompleted: boolean;
setPreStartCompleted: (completed: boolean) => void; setPreStartCompleted: (completed: boolean) => void;
version: Version; version: Version;
savedGame: SavedGame;
saveCurrentGame: (currentGame: SavedGame) => void;
}; };
export const GlobalSettingsContext = export const GlobalSettingsContext =

View File

@@ -2,7 +2,7 @@ import { initializeApp } from 'firebase/app';
import { getAnalytics, logEvent } from 'firebase/analytics'; import { getAnalytics, logEvent } from 'firebase/analytics';
const firebaseConfig = { const firebaseConfig = {
apiKey: import.meta.env.VITE_FIREBASE_API_KEY, apiKey: 'AIzaSyCZ1AHMb5zmWS4VoRnC-OBxTswUfrJ0mlY',
authDomain: 'life-trinket.firebaseapp.com', authDomain: 'life-trinket.firebaseapp.com',
projectId: 'life-trinket', projectId: 'life-trinket',
storageBucket: 'life-trinket.appspot.com', storageBucket: 'life-trinket.appspot.com',

View File

@@ -3,6 +3,7 @@ import { useWakeLock } from 'react-screen-wake-lock';
import { import {
GlobalSettingsContext, GlobalSettingsContext,
GlobalSettingsContextType, GlobalSettingsContextType,
SavedGame,
} from '../Contexts/GlobalSettingsContext'; } from '../Contexts/GlobalSettingsContext';
import { useAnalytics } from '../Hooks/useAnalytics'; import { useAnalytics } from '../Hooks/useAnalytics';
import { import {
@@ -21,12 +22,21 @@ export const GlobalSettingsProvider = ({
}) => { }) => {
const analytics = useAnalytics(); const analytics = useAnalytics();
const savedShowPlay = localStorage.getItem('showPlay'); const localSavedGame = localStorage.getItem('savedGame');
const savedGameSettings = localStorage.getItem('initialGameSettings'); const [savedGame, setCurrentGame] = useState<SavedGame>(
const savedSettings = localStorage.getItem('settings'); localSavedGame ? JSON.parse(localSavedGame) : null
const savedPlaying = localStorage.getItem('playing'); );
const savedPreStartComplete = localStorage.getItem('preStartComplete'); const setCurrentGameAndLocalStorage = (savedGame: SavedGame) => {
if (!savedGame) {
setCurrentGame(savedGame);
localStorage.removeItem('savedGame');
return;
}
setCurrentGame(savedGame);
localStorage.setItem('savedGame', JSON.stringify(savedGame));
};
const savedPlaying = localStorage.getItem('playing');
const [playing, setPlaying] = useState<boolean>( const [playing, setPlaying] = useState<boolean>(
savedPlaying ? savedPlaying === 'true' : false savedPlaying ? savedPlaying === 'true' : false
); );
@@ -35,23 +45,42 @@ export const GlobalSettingsProvider = ({
localStorage.setItem('playing', String(playing)); localStorage.setItem('playing', String(playing));
}; };
const savedPreStartComplete = localStorage.getItem('preStartComplete');
const [preStartCompleted, setPreStartCompleted] = useState<boolean>( const [preStartCompleted, setPreStartCompleted] = useState<boolean>(
savedPreStartComplete ? savedPreStartComplete === 'true' : false savedPreStartComplete ? savedPreStartComplete === 'true' : false
); );
const savedShowPlay = localStorage.getItem('showPlay');
const [showPlay, setShowPlay] = useState<boolean>( const [showPlay, setShowPlay] = useState<boolean>(
savedShowPlay ? savedShowPlay === 'true' : false savedShowPlay ? savedShowPlay === 'true' : false
); );
const setShowPlayAndLocalStorage = (showPlay: boolean) => {
setShowPlay(showPlay);
localStorage.setItem('showPlay', String(showPlay));
};
const savedSettings = localStorage.getItem('settings');
const [randomizingPlayer, setRandomizingPlayer] = useState<boolean>( const [randomizingPlayer, setRandomizingPlayer] = useState<boolean>(
savedSettings savedSettings
? Boolean(JSON.parse(savedSettings).preStartMode === 'random-king') ? Boolean(JSON.parse(savedSettings).preStartMode === 'random-king')
: true : true
); );
const [settings, setSettings] = useState<Settings>(
savedSettings ? JSON.parse(savedSettings) : defaultSettings
);
const setSettingsAndLocalStorage = (settings: Settings) => {
setSettings(settings);
localStorage.setItem('settings', JSON.stringify(settings));
};
const savedGameSettings = localStorage.getItem('initialGameSettings');
const [initialGameSettings, setInitialGameSettings] = const [initialGameSettings, setInitialGameSettings] =
useState<InitialGameSettings | null>( useState<InitialGameSettings>(
savedGameSettings ? JSON.parse(savedGameSettings) : null savedGameSettings
? JSON.parse(savedGameSettings)
: defaultInitialGameSettings
); );
const setInitialGameSettingsAndLocalStorage = ( const setInitialGameSettingsAndLocalStorage = (
@@ -64,15 +93,6 @@ export const GlobalSettingsProvider = ({
); );
}; };
const [settings, setSettings] = useState<Settings>(
savedSettings ? JSON.parse(savedSettings) : defaultSettings
);
const setSettingsAndLocalStorage = (settings: Settings) => {
setSettings(settings);
localStorage.setItem('settings', JSON.stringify(settings));
};
const removeLocalStorage = async () => { const removeLocalStorage = async () => {
localStorage.removeItem('initialGameSettings'); localStorage.removeItem('initialGameSettings');
localStorage.removeItem('players'); localStorage.removeItem('players');
@@ -252,7 +272,7 @@ export const GlobalSettingsProvider = ({
}, },
goToStart, goToStart,
showPlay, showPlay,
setShowPlay, setShowPlay: setShowPlayAndLocalStorage,
playing, playing,
setPlaying: setPlayingAndLocalStorage, setPlaying: setPlayingAndLocalStorage,
initialGameSettings, initialGameSettings,
@@ -264,6 +284,8 @@ export const GlobalSettingsProvider = ({
isPWA: window?.matchMedia('(display-mode: standalone)').matches, isPWA: window?.matchMedia('(display-mode: standalone)').matches,
preStartCompleted, preStartCompleted,
setPreStartCompleted: setPreStartCompletedAndLocalStorage, setPreStartCompleted: setPreStartCompletedAndLocalStorage,
savedGame,
saveCurrentGame: setCurrentGameAndLocalStorage,
version: { version: {
installedVersion: import.meta.env.VITE_APP_VERSION, installedVersion: import.meta.env.VITE_APP_VERSION,
@@ -285,6 +307,7 @@ export const GlobalSettingsProvider = ({
settings, settings,
randomizingPlayer, randomizingPlayer,
preStartCompleted, preStartCompleted,
savedGame,
remoteVersion, remoteVersion,
isLatestVersion, isLatestVersion,
analytics, analytics,

View File

@@ -31,6 +31,16 @@ code {
monospace; monospace;
} }
// hide scrollbar globally
::-webkit-scrollbar {
display: none;
}
* {
scrollbar-width: none;
-ms-overflow-style: none;
}
@layer utilities { @layer utilities {
.pointer-events-all { .pointer-events-all {
pointer-events: all; pointer-events: all;