Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c74dcf7675 | ||
|
|
b52c160905 | ||
|
|
980d5203a9 | ||
|
|
05bfc8c3d1 | ||
|
|
fb91ef3224 | ||
|
|
c3e8326be2 | ||
|
|
9bbe6cbb3b | ||
|
|
739048044c | ||
|
|
4656757554 | ||
|
|
9a6bfe49a1 | ||
|
|
e6e562a0c6 | ||
|
|
3e38332a68 | ||
|
|
c23509ae26 | ||
|
|
9f1cc35eab | ||
|
|
fe43e464de | ||
|
|
b074499c1f | ||
|
|
5953cf6b49 | ||
|
|
acefbb8846 | ||
|
|
adb79bf0e8 | ||
|
|
92be2e56d4 | ||
|
|
854ca81a40 |
@@ -1,12 +1,12 @@
|
||||
index.html,1711905710499,4b604b23faec8d63a58e07b96d724a1aea56a7c86d57c9af791832ce87a552e7
|
||||
manifest.webmanifest,1711905710499,f2bf253209f6e292a6b0dbfa06fb4ac188eb5f2dba568c3ad5511b9ed52c1f51
|
||||
manifest.json,1711905710294,7ff5111aa04a42adff3b38924ec467b6f345ed0309dba1486dc9b783b60c2a9d
|
||||
sw.js,1711905711506,1ef2f4f40ec8dca15cc42d547462ade1e84314ea9722276f5994ccee7bbdf80f
|
||||
robots.txt,1711905710294,391d14b3c2f8c9143a27a28c7399585142228d4d1bdbe2c87ac946de411fa9a2
|
||||
registerSW.js,1711905710499,5b6445b5215737c53ef0d379c151d57de165a056de2d1c5812ed614f158ebcbd
|
||||
workbox-3e911b1d.js,1711905711507,d5dbc868a5c07af633d29de7ba3ffe37542aaaabdf33713b4298df31f92f11ff
|
||||
assets/index-7m_Zw4yH.css,1711905710499,37997d06b32b3d0c724c054913e3c0583b86f786358121cb1615e6646ff46b56
|
||||
favicon.ico,1711905710294,8cefe5adbf00d337d8633fb744b9f2c4914f769b319be4bb7e184b7a4aa17160
|
||||
logo192.png,1711905710294,3d1e2e6f064d4fd325828f21bb6493ff0dbf2390b0e7d2aba9f2b6def4829799
|
||||
logo512.png,1711905710294,892a4da1cc5434929a83a71fcbcb0d0d80aa82f44e3c21e9b20ffe9267197133
|
||||
assets/index-CLJVONOc.js,1711905710499,22f3835412f82bb3f8a62e74a7f4602a9c43b447224790365dbcc6cbffb4e665
|
||||
index.html,1716462527631,3daeb4b4b2f195883f1e266f94c16156ee3c60a29c3eb8c44a8dcfdbb1fa0a03
|
||||
manifest.webmanifest,1716462527631,10e89b44378da695cb672bf7d801a4ade909383751f1665416f561bbe1434e5d
|
||||
manifest.json,1716462527513,91ce94afb71f33a477f5d8d48c3f98bd7de422279c74f17b6500eec72003ac1a
|
||||
logo192.png,1716462527512,14ac21c3975e11951c1eb7793eec18e1cc3274bfe7cf7858636d547a9a4efc1c
|
||||
registerSW.js,1716462527631,8db45a5ae8765ce12ec241d6c5bd5d30eb81dd9163b2685c5e1b867a0e487018
|
||||
robots.txt,1716462527513,391d14b3c2f8c9143a27a28c7399585142228d4d1bdbe2c87ac946de411fa9a2
|
||||
sw.js,1716462528602,b681e1b343578a0de67032920144d05430677997580429b7e2b749f6afff69ed
|
||||
workbox-3e911b1d.js,1716462528602,666146b896084273226c83dca0b93f99accb195688330d6aa5c8c570bd48a4ac
|
||||
assets/index-D9CdzROR.css,1716462527631,610c77754d47a35446b00c0a50488070c943f3a05e6d57658faefd943bc3fc46
|
||||
favicon.ico,1716462527511,c3d2b7ac7f6263cca7ee26f91725eb32e7539bf0564f3b31a1bfc23cc88e9739
|
||||
logo512.png,1716462527512,a9ebde1252bb76a5b474130ef07a7ed744448fde84221f715f3fec849eccbcd2
|
||||
assets/index-DgCoW5us.js,1716462527631,06a6d92ff20d7f9e1f5e0c4d3ad8f931d7d0636f109b5e2dbbe28abd3707bb50
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "life-trinket",
|
||||
"private": true,
|
||||
"version": "0.9.8",
|
||||
"version": "0.9.94",
|
||||
"type": "commonjs",
|
||||
"engines": {
|
||||
"node": ">=18",
|
||||
"node": ">=20",
|
||||
"yarn": "use pnpm",
|
||||
"npm": "please use pnpm"
|
||||
},
|
||||
@@ -24,6 +24,7 @@
|
||||
"react-screen-wake-lock": "^3.0.2",
|
||||
"react-swipeable": "^7.0.1",
|
||||
"react-twc": "^1.3.0",
|
||||
"semver": "^7.6.2",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -33,6 +34,7 @@
|
||||
"@svgr/cli": "^8.1.0",
|
||||
"@types/react": "^18.3.1",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@typescript-eslint/eslint-plugin": "^7.8.0",
|
||||
"@typescript-eslint/parser": "^7.8.0",
|
||||
"@vitejs/plugin-react-swc": "^3.6.0",
|
||||
|
||||
11091
pnpm-lock.yaml
generated
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 43 KiB |
@@ -1,5 +1,5 @@
|
||||
import { useEffect } from 'react';
|
||||
import { Cross } from '../../Icons/generated';
|
||||
import { Close } from '../../Icons/generated';
|
||||
import { useAnalytics } from '../../Hooks/useAnalytics';
|
||||
import { Separator } from '../Misc/Separator';
|
||||
|
||||
@@ -42,16 +42,18 @@ export const Dialog: React.FC<{
|
||||
analytics.trackEvent(`${id}_cross_clicked`);
|
||||
dialogRef.current?.close();
|
||||
}}
|
||||
className="flex absolute -top-2 right-2 z-10 w-10 h-10 bg-primary-main items-center justify-center rounded-full border-solid border-primary-dark border-2"
|
||||
className="flex absolute -top-2 right-2 z-10 w-10 h-10 bg-background-default rounded-full"
|
||||
>
|
||||
<Cross size="16px" className="text-text-primary" />
|
||||
<Close className="text-primary-main size-full" />
|
||||
</button>
|
||||
|
||||
<div className="bg-background-default rounded-2xl max-w-[548px] max-h-[80vh] flex flex-col">
|
||||
<div className="text-2xl text-center text-text-primary px-8 pt-4">
|
||||
<h2 className="">{title}</h2>
|
||||
<Separator height="1px" />
|
||||
</div>
|
||||
{title && (
|
||||
<div className="text-2xl text-center text-text-primary px-8 pt-4">
|
||||
<h2 className="">{title}</h2>
|
||||
<Separator height="1px" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="h-full overflow-auto text-text-primary show-scrollbar px-8 pb-8">
|
||||
{children}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useAnalytics } from '../../Hooks/useAnalytics';
|
||||
import { BuyMeCoffee } from '../../Icons/generated/Support';
|
||||
import { Separator } from '../Misc/Separator';
|
||||
import { Paragraph } from '../Misc/TextComponents';
|
||||
import { Dialog } from './Dialog';
|
||||
|
||||
@@ -10,57 +11,72 @@ export const InfoDialog = ({
|
||||
}) => {
|
||||
const analytics = useAnalytics();
|
||||
return (
|
||||
<Dialog id="info" title="📋 Usage Guide" dialogRef={dialogRef}>
|
||||
<div className="text-text-primary">
|
||||
<Paragraph className="my-4">
|
||||
There are some controls that you might not know about, so here's a
|
||||
short list of them.
|
||||
</Paragraph>
|
||||
<h3 className="text-lg font-bold mb-2">Life counter</h3>
|
||||
<ul className="list-disc ml-6 mb-4">
|
||||
<li>
|
||||
<strong>Tap</strong> on a player's + or - button to add or subtract{' '}
|
||||
<strong>1 life</strong>.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Long press</strong> on a player's + or - button to add or
|
||||
subtract <strong>10 life</strong>.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 className="text-lg font-bold mb-2">
|
||||
Commander damage and other counters
|
||||
</h3>
|
||||
<ul className="list-disc ml-6 mb-4">
|
||||
<li>
|
||||
<strong>Tap</strong> on the counter to add{' '}
|
||||
<strong>1 counter</strong>.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Long press</strong> on the counter to subtract{' '}
|
||||
<strong>1 counter</strong>.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 className="text-lg font-bold mb-2">Other functionality</h3>
|
||||
<ul className="list-disc ml-6">
|
||||
<li>
|
||||
<Paragraph className="mb-1">
|
||||
When a player is <strong>at or below 0 life</strong>, has taken{' '}
|
||||
<strong>21 or more Commander Damage</strong> or has{' '}
|
||||
<strong>10 or more poison counters</strong>, a button with a skull
|
||||
will appear on that player's card. Tapping it will dim the
|
||||
player's card.
|
||||
</Paragraph>
|
||||
</li>
|
||||
<li>
|
||||
<Paragraph className="mb-4">
|
||||
Swiping <strong>down</strong> on a player's card will show that
|
||||
player's settings menu.
|
||||
</Paragraph>
|
||||
</li>
|
||||
</ul>
|
||||
<Dialog id="info" title="Info" dialogRef={dialogRef}>
|
||||
<div className=" text-text-primary">
|
||||
<h2 className="text-md underline">Contributors</h2>
|
||||
<div className="flex flex-row items-center gap-1 text-sm">
|
||||
{/* <Trinket className="size-4" /> */}
|
||||
<a href="#">Elin:</a> Icon design
|
||||
</div>
|
||||
<Separator height="1px" />
|
||||
</div>
|
||||
<div className="text-text-primary mt-4">
|
||||
<div className="text">
|
||||
<h2 className="text-xl">📋 Usage Guide</h2>
|
||||
<Separator height="1px" />
|
||||
</div>
|
||||
<div className="text-text-primary">
|
||||
<Paragraph className="mb-4">
|
||||
There are some controls that you might not know about, so here's a
|
||||
short list of them.
|
||||
</Paragraph>
|
||||
<h3 className="text-lg font-bold mb-2">Life counter</h3>
|
||||
<ul className="list-disc ml-6 mb-4">
|
||||
<li>
|
||||
<strong>Tap</strong> on a player's + or - button to add or
|
||||
subtract <strong>1 life</strong>.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Long press</strong> on a player's + or - button to add or
|
||||
subtract <strong>10 life</strong>.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 className="text-lg font-bold mb-2">
|
||||
Commander damage and other counters
|
||||
</h3>
|
||||
<ul className="list-disc ml-6 mb-4">
|
||||
<li>
|
||||
<strong>Tap</strong> on the counter to add{' '}
|
||||
<strong>1 counter</strong>.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Long press</strong> on the counter to subtract{' '}
|
||||
<strong>1 counter</strong>.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 className="text-lg font-bold mb-2">Other functionality</h3>
|
||||
<ul className="list-disc ml-6">
|
||||
<li>
|
||||
<Paragraph className="mb-1">
|
||||
When a player is <strong>at or below 0 life</strong>, has taken{' '}
|
||||
<strong>21 or more Commander Damage</strong> or has{' '}
|
||||
<strong>10 or more poison counters</strong>, a button with a
|
||||
skull will appear on that player's card. Tapping it will dim the
|
||||
player's card.
|
||||
</Paragraph>
|
||||
</li>
|
||||
<li>
|
||||
<Paragraph className="mb-4">
|
||||
Swiping <strong>down</strong> on a player's card will show that
|
||||
player's settings menu.
|
||||
</Paragraph>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-center mt-4 text-text-primary">
|
||||
Visit my{' '}
|
||||
<a
|
||||
@@ -72,7 +88,6 @@ export const InfoDialog = ({
|
||||
</a>{' '}
|
||||
for more info about this web app.
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center mt-4">
|
||||
<a
|
||||
className="flex flex-row items-center self-center border-none cursor-pointer bg-primary-main rounded-md mx-4 pr-4 pl-3 py-2 transition-colors duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark"
|
||||
|
||||
@@ -11,7 +11,7 @@ const SettingContainer = twc.div`w-full flex flex-col mb-2`;
|
||||
|
||||
const ToggleContainer = twc.div`flex flex-row justify-between items-center -mb-1`;
|
||||
|
||||
const Description = twc.p`mr-16 text-xs text-left text-text-secondary`;
|
||||
const Description = twc.p`mr-16 text-xs text-left text-text-secondary mt-1`;
|
||||
|
||||
const baseGithubReleasesUrl =
|
||||
'https://github.com/Vikeo/LifeTrinket/releases/tag/';
|
||||
@@ -135,7 +135,7 @@ export const SettingsDialog = ({
|
||||
name="pre-start-modes"
|
||||
id="pre-start-modes"
|
||||
value={settings.preStartMode}
|
||||
className="bg-primary-main border-none outline-none text-text-primary rounded-md p-1 text-xs disabled:bg-primary-dark"
|
||||
className="bg-secondary-main border-none outline-none text-text-primary rounded-md p-1 text-xs disabled:saturate-50 font-semibold"
|
||||
onChange={(e) => {
|
||||
setSettings({
|
||||
...settings,
|
||||
@@ -220,10 +220,31 @@ export const SettingsDialog = ({
|
||||
Will enter fullscreen mode when starting a game if this is enabled.
|
||||
</Description>
|
||||
</SettingContainer>
|
||||
|
||||
<SettingContainer>
|
||||
<ToggleContainer>
|
||||
<label>Show animations</label>
|
||||
<ToggleButton
|
||||
checked={settings.showAnimations}
|
||||
onChange={() => {
|
||||
setSettings({
|
||||
...settings,
|
||||
showAnimations: !settings.showAnimations,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</ToggleContainer>
|
||||
<Description>
|
||||
Disables the following animation:
|
||||
<ul className="pl-1 list-inside">
|
||||
<li className="list-disc">Glow effect on start menu</li>
|
||||
</ul>
|
||||
</Description>
|
||||
</SettingContainer>
|
||||
<Separator height="1px" />
|
||||
<div className="flex w-full justify-center">
|
||||
<button
|
||||
className="mt-1 mb-1 bg-primary-main px-3 py-1 rounded-md duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark"
|
||||
className="mt-1 mb-1 bg-primary-main px-3 py-1 rounded-md duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark font-bold"
|
||||
onClick={() => {
|
||||
analytics.trackEvent('settings_save_clicked');
|
||||
dialogRef.current?.close();
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
|
||||
import { usePlayers } from '../../Hooks/usePlayers';
|
||||
import { useSafeRotate } from '../../Hooks/useSafeRotate';
|
||||
import {
|
||||
Cross,
|
||||
Close,
|
||||
Energy,
|
||||
Exit,
|
||||
Experience,
|
||||
@@ -61,6 +61,7 @@ const ButtonsSections = twc.div`
|
||||
justify-evenly
|
||||
items-center
|
||||
flex-wrap
|
||||
h-full
|
||||
mt-0
|
||||
px-2
|
||||
`;
|
||||
@@ -192,9 +193,9 @@ const PlayerMenu = ({
|
||||
analytics.trackEvent('close_player_menu_button');
|
||||
setShowPlayerMenu(false);
|
||||
}}
|
||||
className="flex absolute top-0 right-2 z-10 bg-transparent items-center justify-center rounded-full border-solid border-primary-main border-2 p-[0.2rem]"
|
||||
className="flex absolute top-2 right-2 z-10"
|
||||
>
|
||||
<Cross size={buttonFontSize} className="text-primary-main " />
|
||||
<Close size={buttonFontSize} className="text-primary-main " />
|
||||
</button>
|
||||
|
||||
<BetterRowContainer>
|
||||
|
||||
@@ -122,7 +122,7 @@ export const Trivia = () => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className="absolute flex justify-center items-center w-full h-full portrait:h-[100dvw] portrait:w-[100dvh] z-50 bg-primary-main overflow-hidden"
|
||||
className="absolute flex justify-center items-center w-full h-full portrait:h-[100dvw] portrait:w-[100dvh] z-50 bg-primary-dark overflow-hidden"
|
||||
onClick={() => setPlaying(true)}
|
||||
>
|
||||
<button
|
||||
@@ -146,7 +146,7 @@ export const Trivia = () => {
|
||||
<div className="size-full flex flex-col justify-between items-center whitespace-nowrap pointer-events-none webkit-user-select-none text-wrap text-center py-[10vmin] px-[10vmax]">
|
||||
<div className="text-[6vmin]">Decide who starts by answering:</div>
|
||||
<div className="flex flex-col">
|
||||
<div className="text-[8vmin] rotate-180 text-text-primary opacity-40">
|
||||
<div className="text-[8vmin] rotate-180 text-text-primary opacity-60">
|
||||
{randomQuestion}
|
||||
</div>
|
||||
<div className="text-[8vmin]">{randomQuestion}</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createInitialPlayers } from '../../../Data/getInitialPlayers';
|
||||
import { useAnalytics } from '../../../Hooks/useAnalytics';
|
||||
import { useGlobalSettings } from '../../../Hooks/useGlobalSettings';
|
||||
import { usePlayers } from '../../../Hooks/usePlayers';
|
||||
import { Cog, Info } from '../../../Icons/generated';
|
||||
import { Cog, Info, Trinket } from '../../../Icons/generated';
|
||||
import {
|
||||
InitialGameSettings,
|
||||
Orientation,
|
||||
@@ -12,10 +12,11 @@ import {
|
||||
defaultInitialGameSettings,
|
||||
} from '../../../Types/Settings';
|
||||
|
||||
import { LayoutOptions } from './LayoutOptions';
|
||||
import { baseColors } from '../../../../tailwind.config';
|
||||
import { InfoDialog } from '../../Dialogs/InfoDialog';
|
||||
import { SettingsDialog } from '../../Dialogs/SettingsDialog';
|
||||
import { ToggleButton } from '../../Misc/ToggleButton';
|
||||
import { LayoutOptions } from './LayoutOptions';
|
||||
|
||||
const commanderSettings: Pick<
|
||||
InitialGameSettings,
|
||||
@@ -35,7 +36,7 @@ const standardSettings: Pick<
|
||||
orientation: Orientation.Landscape,
|
||||
};
|
||||
|
||||
const MainWrapper = twc.div`w-[100dvw] h-fit pb-24 overflow-hidden items-center flex flex-col min-[349px]:pb-10`;
|
||||
const MainWrapper = twc.div`h-fit w-full 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-row flex-wrap px-4 z-10 gap-4`;
|
||||
|
||||
@@ -67,6 +68,8 @@ const Start = () => {
|
||||
|
||||
const infoDialogRef = useRef<HTMLDialogElement | null>(null);
|
||||
const settingsDialogRef = useRef<HTMLDialogElement | null>(null);
|
||||
const playersSliderRef = useRef<HTMLInputElement | null>(null);
|
||||
const healthSliderRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const [playerOptions, setPlayerOptions] = useState<InitialGameSettings>(
|
||||
initialGameSettings || defaultInitialGameSettings
|
||||
@@ -81,6 +84,68 @@ const Start = () => {
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!playersSliderRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
let progress = 0;
|
||||
|
||||
switch (playerOptions?.numberOfPlayers) {
|
||||
case 1:
|
||||
progress = 0;
|
||||
break;
|
||||
case 2:
|
||||
progress = 20;
|
||||
break;
|
||||
case 3:
|
||||
progress = 40;
|
||||
break;
|
||||
case 4:
|
||||
progress = 60;
|
||||
break;
|
||||
case 5:
|
||||
progress = 80;
|
||||
break;
|
||||
case 6:
|
||||
progress = 100;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
playersSliderRef.current.style.background = `linear-gradient(to right, ${baseColors.secondary.main} ${progress}%, ${baseColors.secondary.dark} ${progress}%)`;
|
||||
}, [playerOptions?.numberOfPlayers]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!healthSliderRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
let progress = 0;
|
||||
switch (playerOptions?.startingLifeTotal) {
|
||||
case 20:
|
||||
progress = 0;
|
||||
break;
|
||||
case 30:
|
||||
progress = 25;
|
||||
break;
|
||||
case 40:
|
||||
progress = 50;
|
||||
break;
|
||||
case 50:
|
||||
progress = 75;
|
||||
break;
|
||||
case 60:
|
||||
progress = 100;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
healthSliderRef.current.style.background = `linear-gradient(to right, ${baseColors.secondary.main} ${progress}%, ${baseColors.secondary.dark} ${progress}%)`;
|
||||
}, [playerOptions?.startingLifeTotal]);
|
||||
|
||||
useEffect(() => {
|
||||
setInitialGameSettings(playerOptions);
|
||||
}, [playerOptions, setInitialGameSettings]);
|
||||
@@ -171,179 +236,191 @@ const Start = () => {
|
||||
return (
|
||||
<>
|
||||
<InfoDialog dialogRef={infoDialogRef} />
|
||||
{settings.showAnimations && (
|
||||
<>
|
||||
<div className="spotlight1" />
|
||||
<div className="spotlight2" />
|
||||
</>
|
||||
)}
|
||||
|
||||
<SettingsDialog dialogRef={settingsDialogRef} />
|
||||
<div className="flex justify-center items-center w-screen">
|
||||
<MainWrapper>
|
||||
<Info
|
||||
className="size-8 absolute top-7 left-4 text-primary-main"
|
||||
onClick={() => {
|
||||
openInfo();
|
||||
}}
|
||||
/>
|
||||
<a href="https://lifetrinket.com/">
|
||||
<Trinket className="absolute w-12 h-12 top-4 right-4" />
|
||||
</a>
|
||||
|
||||
<MainWrapper>
|
||||
<Info
|
||||
className="size-8 absolute top-4 left-4 text-primary-main"
|
||||
onClick={() => {
|
||||
openInfo();
|
||||
}}
|
||||
/>
|
||||
<h1 className="relative flex flex-col text-3xl font-bold mt-6 mb-6 text-text-primary justify-center items-center">
|
||||
<div className="flex flex-row items-center">Life Trinket</div>
|
||||
<div className="h-[1px] w-[120%] bg-common-white opacity-50" />
|
||||
<div className="flex absolute text-xs font-medium -bottom-4">
|
||||
v{version.installedVersion}
|
||||
</div>
|
||||
</h1>
|
||||
|
||||
<h1 className="relative flex flex-col text-3xl font-bold mt-6 mb-6 text-text-primary justify-center items-center">
|
||||
Life Trinket
|
||||
<div className="h-[1px] w-[120%] bg-common-white opacity-50" />
|
||||
<div className="flex absolute text-xs font-medium -bottom-4">
|
||||
v{version.installedVersion}
|
||||
</div>
|
||||
</h1>
|
||||
|
||||
<div className="overflow-hidden items-center flex flex-col max-w-[548px] w-full mb-8 px-4">
|
||||
<div className="w-full">
|
||||
<ToggleButtonsWrapper className="mt-4">
|
||||
<ToggleButton
|
||||
label="Commander"
|
||||
checked={
|
||||
playerOptions.useCommanderDamage ??
|
||||
initialGameSettings?.useCommanderDamage ??
|
||||
true
|
||||
}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
<div className="overflow-hidden items-center flex flex-col max-w-[548px] w-full mb-8 px-4">
|
||||
<div className="w-full">
|
||||
<ToggleButtonsWrapper className="mt-4">
|
||||
<ToggleButton
|
||||
label="Commander"
|
||||
checked={
|
||||
playerOptions.useCommanderDamage ??
|
||||
initialGameSettings?.useCommanderDamage ??
|
||||
true
|
||||
}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
setPlayerOptions({
|
||||
...playerOptions,
|
||||
useCommanderDamage: e.target.checked,
|
||||
...commanderSettings,
|
||||
});
|
||||
return;
|
||||
}
|
||||
setPlayerOptions({
|
||||
...playerOptions,
|
||||
useCommanderDamage: e.target.checked,
|
||||
...commanderSettings,
|
||||
...standardSettings,
|
||||
});
|
||||
return;
|
||||
}
|
||||
setPlayerOptions({
|
||||
...playerOptions,
|
||||
useCommanderDamage: e.target.checked,
|
||||
...standardSettings,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="flex flex-nowrap text-nowrap relative justify-center items-start">
|
||||
<button
|
||||
className="flex justify-center self-center items-center mt-1 mb-1 bg-primary-main px-3 py-2 rounded-md transition-colors duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark"
|
||||
onClick={openSettings}
|
||||
>
|
||||
<span className="text-sm flex flex-row items-center text-text-primary">
|
||||
<Cog />
|
||||
Game Settings
|
||||
</span>
|
||||
</button>
|
||||
<div className="flex flex-nowrap text-nowrap relative justify-center items-start">
|
||||
<button
|
||||
className="flex justify-center self-center items-center mt-1 mb-1 bg-primary-main px-3 py-2 rounded-md transition-colors duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark"
|
||||
onClick={openSettings}
|
||||
>
|
||||
<span className="text-sm flex flex-row items-center text-text-primary font-bold">
|
||||
<Cog />
|
||||
Game Settings
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div
|
||||
data-not-latest-version={
|
||||
!version.isLatest && !!version.remoteVersion
|
||||
}
|
||||
className="absolute flex justify-center text-text-primary text-xxs -bottom-5 bg-primary-dark px-2 rounded-md
|
||||
<div
|
||||
data-not-latest-version={
|
||||
!version.isLatest && !!version.remoteVersion
|
||||
}
|
||||
className="absolute flex justify-center text-text-primary text-xxs -bottom-5 bg-primary-dark px-2 rounded-md
|
||||
opacity-0 transition-all duration-200 delay-500
|
||||
data-[not-latest-version=true]:opacity-100
|
||||
"
|
||||
>
|
||||
<div className="absolute bg-primary-dark rotate-45 size-2 -top-[2px] z-0" />
|
||||
<span className="z-10">
|
||||
v{version.remoteVersion} available!
|
||||
</span>
|
||||
>
|
||||
<div className="absolute bg-primary-dark rotate-45 size-2 -top-[2px] z-0" />
|
||||
<span className="z-10">
|
||||
v{version.remoteVersion} available!
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ToggleButtonsWrapper>
|
||||
<LabelText className="mt-4">Number of Players</LabelText>
|
||||
<SliderWrapper>
|
||||
<input
|
||||
className="accent-primary-main text-primary-dark w-full h-3 rounded-lg cursor-pointer"
|
||||
title="Number of Players"
|
||||
type="range"
|
||||
max={6}
|
||||
min={1}
|
||||
value={playerOptions?.numberOfPlayers ?? 4}
|
||||
onChange={(e) => {
|
||||
</ToggleButtonsWrapper>
|
||||
<LabelText className="mt-4">Number of Players</LabelText>
|
||||
<SliderWrapper>
|
||||
<input
|
||||
ref={playersSliderRef}
|
||||
className="accent-primary-main text-primary-dark w-full h-3 rounded-lg cursor-pointer"
|
||||
title="Number of Players"
|
||||
type="range"
|
||||
max={6}
|
||||
min={1}
|
||||
value={playerOptions?.numberOfPlayers ?? 4}
|
||||
onChange={(e) => {
|
||||
setPlayerOptions({
|
||||
...playerOptions,
|
||||
numberOfPlayers: Number.parseInt(e.target.value),
|
||||
orientation: Orientation.Landscape,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<div className="flex w-full justify-between px-[6px] text-text-primary pointer-events-none">
|
||||
<div>1</div>
|
||||
<div>2</div>
|
||||
<div>3</div>
|
||||
<div>4</div>
|
||||
<div>5</div>
|
||||
<div>6</div>
|
||||
</div>
|
||||
</SliderWrapper>
|
||||
|
||||
<LabelText className="mt-4">Starting Health</LabelText>
|
||||
<SliderWrapper>
|
||||
<input
|
||||
ref={healthSliderRef}
|
||||
className="accent-primary-main text-primary-dark w-full h-3 rounded-lg cursor-pointer"
|
||||
title="Starting Health"
|
||||
type="range"
|
||||
max={60}
|
||||
min={20}
|
||||
aria-label="Custom marks"
|
||||
value={playerOptions?.startingLifeTotal ?? 40}
|
||||
step={10}
|
||||
onChange={(e) =>
|
||||
setPlayerOptions({
|
||||
...playerOptions,
|
||||
startingLifeTotal: Number.parseInt(e.target.value),
|
||||
orientation: Orientation.Landscape,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<div className="flex w-full justify-between text-text-primary pointer-events-none">
|
||||
<div>20</div>
|
||||
<div>30</div>
|
||||
<div>40</div>
|
||||
<div>50</div>
|
||||
<div>60</div>
|
||||
</div>
|
||||
</SliderWrapper>
|
||||
|
||||
<LabelText className="mt-4">Layout</LabelText>
|
||||
<LayoutOptions
|
||||
numberOfPlayers={playerOptions.numberOfPlayers}
|
||||
selectedOrientation={playerOptions.orientation}
|
||||
onChange={(orientation) => {
|
||||
setPlayerOptions({
|
||||
...playerOptions,
|
||||
numberOfPlayers: Number.parseInt(e.target.value),
|
||||
orientation: Orientation.Landscape,
|
||||
orientation,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<div className="flex w-full justify-between px-1 text-text-primary pointer-events-none">
|
||||
<div>1</div>
|
||||
<div>2</div>
|
||||
<div>3</div>
|
||||
<div>4</div>
|
||||
<div>5</div>
|
||||
<div>6</div>
|
||||
</div>
|
||||
</SliderWrapper>
|
||||
|
||||
<LabelText className="mt-4">Starting Health</LabelText>
|
||||
<SliderWrapper>
|
||||
<input
|
||||
className="accent-primary-main text-primary-dark w-full h-3 rounded-lg cursor-pointer"
|
||||
title="Starting Health"
|
||||
type="range"
|
||||
max={60}
|
||||
min={20}
|
||||
aria-label="Custom marks"
|
||||
value={playerOptions?.startingLifeTotal ?? 40}
|
||||
step={10}
|
||||
onChange={(e) =>
|
||||
setPlayerOptions({
|
||||
...playerOptions,
|
||||
startingLifeTotal: Number.parseInt(e.target.value),
|
||||
orientation: Orientation.Landscape,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<div className="flex w-full justify-between px-1 text-text-primary pointer-events-none">
|
||||
<div>20</div>
|
||||
<div>30</div>
|
||||
<div>40</div>
|
||||
<div>50</div>
|
||||
<div>60</div>
|
||||
</div>
|
||||
</SliderWrapper>
|
||||
|
||||
<LabelText className="mt-4">Layout</LabelText>
|
||||
<LayoutOptions
|
||||
numberOfPlayers={playerOptions.numberOfPlayers}
|
||||
selectedOrientation={playerOptions.orientation}
|
||||
onChange={(orientation) => {
|
||||
setPlayerOptions({
|
||||
...playerOptions,
|
||||
orientation,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{!isPWA && (
|
||||
<p className="text-center text-xs text-text-primary w-11/12 mt-4">
|
||||
If you're on iOS, this page works better if you{' '}
|
||||
<strong>hide the toolbar</strong> or{' '}
|
||||
<strong>add the app to your home screen</strong>.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{!isPWA && (
|
||||
<p className="text-center text-xs text-text-primary w-11/12 mt-4">
|
||||
If you're on iOS, this page works better if you{' '}
|
||||
<strong>hide the toolbar</strong> or{' '}
|
||||
<strong>add the app to your home screen</strong>.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<StartButtonFooter>
|
||||
<button
|
||||
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] duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark"
|
||||
onClick={doStartNewGame}
|
||||
>
|
||||
NEW GAME
|
||||
</button>
|
||||
|
||||
{savedGame && (
|
||||
<StartButtonFooter>
|
||||
<button
|
||||
className="flex flex-grow basis-0 justify-center self-center items-center bg-secondary-main px-3 py-2 rounded-md text-text-primary min-w-[150px]
|
||||
|
||||
duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-secondary-dark"
|
||||
onClick={doResumeGame}
|
||||
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] duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark font-bold"
|
||||
onClick={doStartNewGame}
|
||||
>
|
||||
RESUME
|
||||
<span className="text-xs">
|
||||
({savedGame.players.length}
|
||||
{savedGame.players.length > 1 ? 'players' : 'player'})
|
||||
</span>
|
||||
NEW GAME
|
||||
</button>
|
||||
)}
|
||||
</StartButtonFooter>
|
||||
</MainWrapper>
|
||||
|
||||
{savedGame && (
|
||||
<button
|
||||
className="flex flex-grow basis-0 justify-center self-center items-center bg-secondary-main px-3 py-2 rounded-md text-text-primary min-w-[150px]
|
||||
|
||||
duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-secondary-dark font-bold"
|
||||
onClick={doResumeGame}
|
||||
>
|
||||
RESUME
|
||||
<span className="text-xs">
|
||||
({savedGame.players.length}
|
||||
{savedGame.players.length > 1 ? 'players' : 'player'})
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</StartButtonFooter>
|
||||
</MainWrapper>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
45
src/Icons/generated/Close.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { SVGProps } from 'react';
|
||||
interface SVGRProps {
|
||||
title?: string;
|
||||
titleId?: string;
|
||||
size?: string;
|
||||
}
|
||||
const Close = ({
|
||||
title,
|
||||
titleId,
|
||||
...props
|
||||
}: SVGProps<SVGSVGElement> & SVGRProps) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={props.size || 16}
|
||||
height={props.size || 16}
|
||||
fill="currentColor"
|
||||
viewBox="0 0 52 52"
|
||||
aria-labelledby={titleId}
|
||||
{...props}
|
||||
>
|
||||
{title ? <title id={titleId}>{title}</title> : null}
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M26 48c12.15 0 22-9.85 22-22S38.15 4 26 4 4 13.85 4 26s9.85 22 22 22m0 4c14.36 0 26-11.64 26-26S40.36 0 26 0 0 11.64 0 26s11.64 26 26 26"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M15.586 15.586a2 2 0 0 1 2.828 0l18 18a2 2 0 1 1-2.828 2.828l-18-18a2 2 0 0 1 0-2.828"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M36.414 15.586a2 2 0 0 1 0 2.828l-18 18a2 2 0 1 1-2.828-2.828l18-18a2 2 0 0 1 2.828 0"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
Close.propTypes = {
|
||||
title: PropTypes.string,
|
||||
};
|
||||
export default Close;
|
||||
@@ -15,15 +15,17 @@ const Info = ({
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={props.size || 16}
|
||||
height={props.size || 16}
|
||||
viewBox="0 0 524 524"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 52 52"
|
||||
aria-labelledby={titleId}
|
||||
{...props}
|
||||
>
|
||||
{title ? <title id={titleId}>{title}</title> : null}
|
||||
<g fill="currentColor">
|
||||
<path d="M236.7 33.5c-28.8 3.6-51.4 10.2-75.4 22C92.7 89.2 46.2 152.7 34.4 229c-2.5 16-2.5 50 0 66C46 370 91.6 433.3 158.5 467.2c32.9 16.7 65.2 24.3 103.5 24.3 22.1 0 34.2-1.4 54.5-6.2 31.1-7.3 65.4-24.3 90.8-45.2 9.1-7.4 27-25.5 34.3-34.6 25-31.3 41.8-70.1 48-111 2.5-16.4 2.5-48.3 0-65-11.4-76.2-58.6-140.8-126.9-174-23.2-11.2-42.1-17.1-67.7-21-14.5-2.2-44.7-2.8-58.3-1M294 78.9c74.6 13.2 133.6 70.2 149.6 144.4 8.6 40.3 3.5 82-14.6 119.3C390.5 422 302.6 463 216.5 441.9c-88.7-21.8-148.8-107.6-139.4-199 7.3-71.5 56.7-133.8 124.4-156.8 12.2-4.1 27.1-7.4 40.5-9.1 11.8-1.4 38.8-.4 52 1.9" />
|
||||
<path d="M246.5 112c-20.8 2-36.2 8.6-48.5 21.1-11.5 11.6-19.3 26.4-27.5 52-3.4 10.6-3.6 11.9-2.4 14.4 2 4.1 5.8 6.5 11.4 7.1 7.4.9 9.2-1 17.8-19 14.9-30.8 25.7-41.9 46-47.3 8.4-2.3 29.8-2.2 38.6.1 21.2 5.5 34.6 19.5 35.9 37.6.8 11.1-2.3 16.4-20.5 35.2-26 26.8-38 45.6-44.2 69.2-3 11.3-4.6 31.6-3.6 46 .7 11.3.9 12.1 3.5 14.8 2.6 2.5 3.5 2.8 9.5 2.8 5.7 0 6.9-.3 9-2.5 1.4-1.3 2.5-3.2 2.6-4.2s.2-9.9.3-19.8c.2-20.3 1.6-28.1 7.1-40.2 7.6-16.6 23.7-36 42.5-51.3 22.4-18.2 28.1-27.9 28-47.8-.1-19.8-6.1-35.4-17.9-46.5-8.6-8.1-14.8-11.8-26.6-15.7-16.5-5.4-41.3-7.8-61-6M252.4 372.9c-4.2 1.8-5.4 5-5.4 13.9 0 4.6.5 9.2 1 10.3 1.8 3.3 6.4 4.9 14 4.9 12.6 0 15.5-3.3 14.9-16.9-.6-11.3-1.9-12.5-13.7-12.8-4.8-.2-9.7.1-10.8.6" />
|
||||
</g>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M26 48c12.15 0 22-9.85 22-22S38.15 4 26 4 4 13.85 4 26s9.85 22 22 22m0 4c14.36 0 26-11.64 26-26S40.36 0 26 0 0 11.64 0 26s11.64 26 26 26m-4.191-36.87-.264.172c-2.513 1.644-2.671 5.27-.31 7.125a2 2 0 1 1-2.47 3.146c-4.514-3.547-4.213-10.477.591-13.619l.264-.172a11.66 11.66 0 0 1 12.76 0l.77.503c4.556 2.98 4.841 9.551.561 12.914-.422.332-.877.62-1.357.86l-.54.27A6.9 6.9 0 0 0 28 32.5a2 2 0 1 1-4 0 10.9 10.9 0 0 1 6.025-9.748l.54-.27q.36-.18.675-.428c2.128-1.672 1.986-4.94-.28-6.421l-.769-.503a7.66 7.66 0 0 0-8.382 0M26 42a3 3 0 1 0 0-6 3 3 0 0 0 0 6"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
51
src/Icons/generated/Trinket.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { SVGProps } from 'react';
|
||||
interface SVGRProps {
|
||||
title?: string;
|
||||
titleId?: string;
|
||||
size?: string;
|
||||
}
|
||||
const Trinket = ({
|
||||
title,
|
||||
titleId,
|
||||
...props
|
||||
}: SVGProps<SVGSVGElement> & SVGRProps) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={props.size || 16}
|
||||
height={props.size || 16}
|
||||
fill="none"
|
||||
viewBox="0 0 364 472"
|
||||
aria-labelledby={titleId}
|
||||
{...props}
|
||||
>
|
||||
{title ? <title id={titleId}>{title}</title> : null}
|
||||
<path
|
||||
fill="#78A083"
|
||||
d="M40.536 302.159c-6.366-1.843-13.102 1.822-14.118 8.371-1.599 10.309-1.49 20.912.345 31.493 2.538 14.638 8.327 28.947 17.037 42.11s18.895 21.898 32.45 31.582 32.017 14.639 48.355 19.37 34.674 9.982 51.306 9.04c16.633-.942 29.437-2.571 43.833-9.043s26.935-15.473 36.901-26.49c7.205-7.964 12.964-16.868 17.121-26.436 2.641-6.078-1.095-12.775-7.461-14.619L153.42 334.848zM323.254 168.504c6.37 1.832 13.099-1.846 14.102-8.397 1.579-10.312 1.449-20.915-.405-31.493-2.566-14.633-8.382-28.93-17.117-42.077s-18.937-21.862-32.511-31.52-32.044-14.578-48.391-19.278-34.693-9.916-51.324-8.942-29.432 2.626-43.815 9.126-26.905 15.525-36.851 26.56c-7.19 7.978-12.93 16.893-17.07 26.468-2.629 6.084 1.12 12.774 7.489 14.605l112.946 32.474z"
|
||||
/>
|
||||
<path
|
||||
fill="url(#a)"
|
||||
d="M75.577 127.413c2.38-5.473 3.57-8.209 5.537-9.963a12 12 0 0 1 6.168-2.906c2.604-.399 5.472.425 11.207 2.074l216.593 62.274c5.73 1.648 8.595 2.471 10.588 4.191a12 12 0 0 1 3.683 5.732c.736 2.528.295 5.476-.588 11.372l-10.644 71.096c-.14.937-.211 1.405-.317 1.865q-.141.614-.347 1.209c-.154.446-.343.881-.721 1.749l-28.861 66.269c-2.382 5.469-3.573 8.204-5.538 9.956a12 12 0 0 1-6.168 2.904c-2.603.398-5.469-.426-11.202-2.074L48.371 290.886c-5.732-1.648-8.597-2.472-10.59-4.191a12 12 0 0 1-3.684-5.735c-.735-2.527-.293-5.476.592-11.374l10.654-71.027c.14-.934.21-1.401.316-1.86q.142-.612.346-1.206c.153-.445.342-.878.718-1.744z"
|
||||
/>
|
||||
<defs>
|
||||
<radialGradient
|
||||
id="a"
|
||||
cx={0}
|
||||
cy={0}
|
||||
r={1}
|
||||
gradientTransform="matrix(-25.05324 87.13566 -130.70377 -37.57994 181.959 234.953)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#FFF4BE" />
|
||||
<stop offset={1} stopColor="#FFC374" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
Trinket.propTypes = {
|
||||
title: PropTypes.string,
|
||||
};
|
||||
export default Trinket;
|
||||
@@ -1,3 +1,4 @@
|
||||
export { default as Close } from './Close';
|
||||
export { default as Cog } from './Cog';
|
||||
export { default as CommanderTax } from './CommanderTax';
|
||||
export { default as Cross } from './Cross';
|
||||
@@ -13,3 +14,4 @@ export { default as PartnerTax } from './PartnerTax';
|
||||
export { default as Poison } from './Poison';
|
||||
export { default as ResetGame } from './ResetGame';
|
||||
export { default as Skull } from './Skull';
|
||||
export { default as Trinket } from './Trinket';
|
||||
|
||||
5
src/Icons/svgs/Close.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg width="52" height="52" viewBox="0 0 52 52" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M26 48C38.1503 48 48 38.1503 48 26C48 13.8497 38.1503 4 26 4C13.8497 4 4 13.8497 4 26C4 38.1503 13.8497 48 26 48ZM26 52C40.3594 52 52 40.3594 52 26C52 11.6406 40.3594 0 26 0C11.6406 0 0 11.6406 0 26C0 40.3594 11.6406 52 26 52Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.5858 15.5858C16.3668 14.8047 17.6332 14.8047 18.4142 15.5858L36.4142 33.5858C37.1953 34.3668 37.1953 35.6332 36.4142 36.4142C35.6332 37.1953 34.3668 37.1953 33.5858 36.4142L15.5858 18.4142C14.8047 17.6332 14.8047 16.3668 15.5858 15.5858Z" fill="currentColor"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M36.4142 15.5858C37.1953 16.3668 37.1953 17.6332 36.4142 18.4142L18.4142 36.4142C17.6332 37.1953 16.3668 37.1953 15.5858 36.4142C14.8047 35.6332 14.8047 34.3668 15.5858 33.5858L33.5858 15.5858C34.3668 14.8047 35.6332 14.8047 36.4142 15.5858Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -1,30 +1,3 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="524.000000pt" height="524.000000pt" viewBox="0 0 524.000000 524.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
|
||||
<g transform="translate(0.000000,524.000000) scale(0.100000,-0.100000)"
|
||||
fill="currentColor" stroke="none">
|
||||
<path d="M2367 4905 c-288 -36 -514 -102 -754 -220 -686 -337 -1151 -972
|
||||
-1269 -1735 -25 -160 -25 -500 0 -660 116 -750 572 -1383 1241 -1722 329 -167
|
||||
652 -243 1035 -243 221 0 342 14 545 62 311 73 654 243 908 452 91 74 270 255
|
||||
343 346 250 313 418 701 480 1110 25 164 25 483 0 650 -114 762 -586 1408
|
||||
-1269 1740 -232 112 -421 171 -677 210 -145 22 -447 28 -583 10z m573 -454
|
||||
c746 -132 1336 -702 1496 -1444 86 -403 35 -820 -146 -1193 -385 -794 -1264
|
||||
-1204 -2125 -993 -887 218 -1488 1076 -1394 1990 73 715 567 1338 1244 1568
|
||||
122 41 271 74 405 91 118 14 388 4 520 -19z"/>
|
||||
<path d="M2465 4120 c-208 -20 -362 -86 -485 -211 -115 -116 -193 -264 -275
|
||||
-520 -34 -106 -36 -119 -24 -144 20 -41 58 -65 114 -71 74 -9 92 10 178 190
|
||||
149 308 257 419 460 473 84 23 298 22 386 -1 212 -55 346 -195 359 -376 8
|
||||
-111 -23 -164 -205 -352 -260 -268 -380 -456 -442 -692 -30 -113 -46 -316 -36
|
||||
-460 7 -113 9 -121 35 -148 26 -25 35 -28 95 -28 57 0 69 3 90 25 14 13 25 32
|
||||
26 42 1 10 2 99 3 198 2 203 16 281 71 402 76 166 237 360 425 513 224 182
|
||||
281 279 280 478 -1 198 -61 354 -179 465 -86 81 -148 118 -266 157 -165 54
|
||||
-413 78 -610 60z"/>
|
||||
<path d="M2524 1511 c-42 -18 -54 -50 -54 -139 0 -46 5 -92 10 -103 18 -33 64
|
||||
-49 140 -49 126 0 155 33 149 169 -6 113 -19 125 -137 128 -48 2 -97 -1 -108
|
||||
-6z"/>
|
||||
</g>
|
||||
<svg width="52" height="52" viewBox="0 0 52 52" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M26 48C38.1503 48 48 38.1503 48 26C48 13.8497 38.1503 4 26 4C13.8497 4 4 13.8497 4 26C4 38.1503 13.8497 48 26 48ZM26 52C40.3594 52 52 40.3594 52 26C52 11.6406 40.3594 0 26 0C11.6406 0 0 11.6406 0 26C0 40.3594 11.6406 52 26 52ZM21.8089 15.1299L21.5454 15.3022C19.0317 16.9458 18.874 20.5718 21.2356 22.4274C22.1042 23.1098 22.2551 24.3671 21.5726 25.2356C20.8902 26.1042 19.6329 26.2551 18.7644 25.5726C14.2506 22.0261 14.5519 15.0957 19.3564 11.9543L19.62 11.782C23.4958 9.24777 28.5042 9.24777 32.3801 11.782L33.1496 12.2851C37.7056 15.2641 37.9913 21.8361 33.711 25.1992C33.2888 25.5309 32.834 25.8191 32.3537 26.0592L31.8138 26.3292C29.4764 27.4978 28 29.8868 28 32.5C28 33.6046 27.1046 34.5 26 34.5C24.8954 34.5 24 33.6046 24 32.5C24 28.3717 26.3325 24.5977 30.0249 22.7515L30.5649 22.4815C30.8037 22.3621 31.0298 22.2188 31.2397 22.0539C33.3679 20.3817 33.2258 17.1141 30.9606 15.633L30.1911 15.1299C27.645 13.4651 24.355 13.4651 21.8089 15.1299ZM26 42C27.6569 42 29 40.6569 29 39C29 37.3431 27.6569 36 26 36C24.3431 36 23 37.3431 23 39C23 40.6569 24.3431 42 26 42Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.2 KiB |
11
src/Icons/svgs/Trinket.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="364" height="472" viewBox="0 0 364 472" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M40.5356 302.159C34.1697 300.316 27.4337 303.981 26.418 310.53C24.8192 320.839 24.9288 331.442 26.7633 342.023C29.3011 356.661 35.0901 370.97 43.7998 384.133C52.5096 397.296 62.695 406.031 76.2507 415.715C89.8064 425.399 108.267 430.354 124.605 435.085C140.943 439.816 159.279 445.067 175.911 444.125C192.544 443.183 205.348 441.554 219.744 435.082C234.14 428.61 246.679 419.609 256.645 408.592C263.85 400.628 269.609 391.724 273.766 382.156C276.407 376.078 272.671 369.381 266.305 367.537L153.42 334.848L40.5356 302.159Z" fill="#78A083"/>
|
||||
<path d="M323.254 168.504C329.624 170.336 336.353 166.658 337.356 160.107C338.935 149.795 338.805 139.192 336.951 128.614C334.385 113.981 328.569 99.6837 319.834 86.5374C311.099 73.3911 300.897 64.6755 287.323 55.0171C273.749 45.3588 255.279 40.4391 238.932 35.7391C222.585 31.0391 204.239 25.8229 187.608 26.7968C170.977 27.7707 158.176 29.4234 143.793 35.923C129.41 42.4225 116.888 51.4477 106.942 62.4834C99.7528 70.4607 94.0113 79.3756 89.8724 88.9514C87.2431 95.035 90.9911 101.725 97.3605 103.556L210.307 136.03L323.254 168.504Z" fill="#78A083"/>
|
||||
<path d="M75.5773 127.413C77.9577 121.94 79.1479 119.204 81.1136 117.45C82.8455 115.905 84.9883 114.896 87.2823 114.544C89.8859 114.145 92.7537 114.969 98.4892 116.618L315.082 178.892C320.812 180.54 323.677 181.363 325.67 183.083C327.426 184.597 328.705 186.589 329.353 188.815C330.089 191.343 329.648 194.291 328.765 200.187L318.121 271.283C317.981 272.22 317.91 272.688 317.804 273.148C317.71 273.557 317.594 273.961 317.457 274.357C317.303 274.803 317.114 275.238 316.736 276.106L287.875 342.375C285.493 347.844 284.302 350.579 282.337 352.331C280.605 353.875 278.462 354.883 276.169 355.235C273.566 355.633 270.7 354.809 264.967 353.161L48.371 290.886C42.6395 289.238 39.7737 288.414 37.7805 286.695C36.0242 285.179 34.7448 283.188 34.097 280.96C33.3618 278.433 33.8042 275.484 34.6888 269.586L45.3425 198.559C45.4826 197.625 45.5527 197.158 45.6588 196.699C45.753 196.291 45.8686 195.889 46.0048 195.493C46.1582 195.048 46.3466 194.615 46.7235 193.749L75.5773 127.413Z" fill="url(#paint0_radial_14799_77884)"/>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial_14799_77884" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(181.959 234.953) rotate(106.041) scale(90.6658 135.999)">
|
||||
<stop stop-color="#FFF4BE"/>
|
||||
<stop offset="1" stop-color="#FFC374"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -14,6 +14,7 @@ import {
|
||||
initialGameSettingsSchema,
|
||||
settingsSchema,
|
||||
} from '../Types/Settings';
|
||||
import { gte as semverGreaterThanOrEqual } from 'semver';
|
||||
|
||||
export const GlobalSettingsProvider = ({
|
||||
children,
|
||||
@@ -244,7 +245,12 @@ export const GlobalSettingsProvider = ({
|
||||
|
||||
setRemoteVersion(data.name);
|
||||
|
||||
if (data.name === import.meta.env.VITE_APP_VERSION) {
|
||||
const isLatest = semverGreaterThanOrEqual(
|
||||
import.meta.env.VITE_APP_VERSION,
|
||||
data.name
|
||||
);
|
||||
|
||||
if (isLatest) {
|
||||
setIsLatestVersion(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ export type Settings = {
|
||||
showPlayerMenuCog: boolean;
|
||||
goFullscreenOnStart: boolean;
|
||||
preStartMode: PreStartMode;
|
||||
showAnimations: boolean;
|
||||
};
|
||||
|
||||
export type InitialGameSettings = {
|
||||
@@ -57,6 +58,7 @@ export const settingsSchema = z.object({
|
||||
showPlayerMenuCog: z.boolean(),
|
||||
goFullscreenOnStart: z.boolean(),
|
||||
preStartMode: z.nativeEnum(PreStartMode),
|
||||
showAnimations: z.boolean(),
|
||||
});
|
||||
|
||||
export const defaultSettings: Settings = {
|
||||
@@ -65,4 +67,5 @@ export const defaultSettings: Settings = {
|
||||
showStartingPlayer: true,
|
||||
showPlayerMenuCog: true,
|
||||
preStartMode: PreStartMode.None,
|
||||
showAnimations: true,
|
||||
};
|
||||
|
||||
149
src/index.css
@@ -80,3 +80,152 @@ code {
|
||||
-ms-overflow-style: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes background-orb {
|
||||
0% {
|
||||
bottom: 10%;
|
||||
}
|
||||
50% {
|
||||
bottom: 90%;
|
||||
}
|
||||
|
||||
100% {
|
||||
bottom: 10%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes move-right-left {
|
||||
0% {
|
||||
rotate: 0deg;
|
||||
right: 10%;
|
||||
}
|
||||
|
||||
25% {
|
||||
right: 70%;
|
||||
}
|
||||
|
||||
50% {
|
||||
rotate: 360deg;
|
||||
right: 10%;
|
||||
}
|
||||
|
||||
75% {
|
||||
right: 90%;
|
||||
}
|
||||
|
||||
100% {
|
||||
rotate: 0deg;
|
||||
right: 10%;
|
||||
}
|
||||
}
|
||||
|
||||
.spotlight1 {
|
||||
background: theme('colors.primary.dark');
|
||||
|
||||
position: fixed;
|
||||
height: 100px;
|
||||
width: 300px;
|
||||
border-radius: 100%;
|
||||
transform: translate(50%, 50%);
|
||||
animation-duration: 30s, 60s;
|
||||
animation-name: background-orb, move-right-left;
|
||||
animation-iteration-count: infinite, infinite;
|
||||
animation-direction: alternate, alternate;
|
||||
animation-timing-function: ease-in-out;
|
||||
|
||||
animation-delay: -15s, -15s;
|
||||
|
||||
opacity: 0.8;
|
||||
mix-blend-mode: screen;
|
||||
|
||||
filter: blur(125px);
|
||||
}
|
||||
|
||||
.spotlight2 {
|
||||
background: theme('colors.primary.dark');
|
||||
|
||||
position: fixed;
|
||||
height: 300px;
|
||||
width: 100px;
|
||||
border-radius: 100%;
|
||||
transform: translate(50%, 50%);
|
||||
animation-duration: 60s, 120s;
|
||||
animation-name: background-orb, move-right-left;
|
||||
animation-iteration-count: infinite, infinite;
|
||||
animation-direction: reverse, reverse;
|
||||
animation-timing-function: ease-in-out;
|
||||
|
||||
opacity: 0.8;
|
||||
mix-blend-mode: screen;
|
||||
|
||||
filter: blur(125px);
|
||||
}
|
||||
|
||||
input[type='range'] {
|
||||
-webkit-appearance: none;
|
||||
transition: background 0ms ease-in;
|
||||
margin: 10px 0;
|
||||
width: 100%;
|
||||
background: theme('colors.secondary.main');
|
||||
}
|
||||
input[type='range']:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type='range']::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: 0.875rem;
|
||||
cursor: pointer;
|
||||
box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d;
|
||||
border-radius: 25px;
|
||||
border: 0px solid #000101;
|
||||
}
|
||||
input[type='range']::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d;
|
||||
border: 0px solid #000000;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 100px;
|
||||
background: theme('colors.primary.main');
|
||||
cursor: pointer;
|
||||
margin-top: -3px;
|
||||
}
|
||||
|
||||
input[type='range']::-moz-range-track {
|
||||
width: 100%;
|
||||
height: 0.875rem;
|
||||
cursor: pointer;
|
||||
animate: 0.2s;
|
||||
box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d;
|
||||
border-radius: 25px;
|
||||
border: 0px solid #000101;
|
||||
}
|
||||
input[type='range']::-moz-range-thumb {
|
||||
box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d;
|
||||
border: 0px solid #000000;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 100px;
|
||||
background: theme('colors.primary.main');
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type='range']::-ms-track {
|
||||
width: 100%;
|
||||
height: 0.875rem;
|
||||
cursor: pointer;
|
||||
animate: 0.2s;
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
border-width: 39px 0;
|
||||
color: transparent;
|
||||
}
|
||||
input[type='range']::-ms-thumb {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 100px;
|
||||
background: theme('colors.primary.main');
|
||||
cursor: pointer;
|
||||
margin-top: -3px;
|
||||
}
|
||||
|
||||
@@ -4,28 +4,25 @@ import type { Config } from 'tailwindcss';
|
||||
|
||||
export const baseColors = {
|
||||
primary: {
|
||||
main: '#3E7D78',
|
||||
dark: '#2D5F5B',
|
||||
main: '#78A083',
|
||||
dark: '#608069',
|
||||
},
|
||||
secondary: {
|
||||
main: '#284F4C',
|
||||
dark: '#1B3B38',
|
||||
main: '#5D7965',
|
||||
dark: '#4a6151',
|
||||
},
|
||||
background: {
|
||||
default: '#08253B',
|
||||
default: '#35374B',
|
||||
backdrop: 'rgba(0, 0, 0, 0.3)',
|
||||
settings: 'rgba(20, 20, 0, 0.9)',
|
||||
settings: 'rgba(0, 0, 0, 0.8)',
|
||||
},
|
||||
icons: {
|
||||
dark: '#00000080',
|
||||
light: '#ffffff4f',
|
||||
light: '#F9FFE34f',
|
||||
},
|
||||
text: {
|
||||
primary: '#F5F5F5',
|
||||
secondary: '#76A6A5',
|
||||
},
|
||||
action: {
|
||||
disabled: '#234A47',
|
||||
primary: '#F9FFE3',
|
||||
secondary: '#c7ccb6',
|
||||
},
|
||||
common: {
|
||||
white: '#F9FFE3',
|
||||
|
||||