Compare commits

...

9 Commits

Author SHA1 Message Date
Vikeo
559d828fed fix lint 2024-09-22 09:55:39 +02:00
Vikeo
8a54b168c9 fix lint 2024-09-22 09:51:56 +02:00
Vikeo
b118fce4a9 Merge remote-tracking branch 'origin/main' into monarch 2024-09-22 09:47:54 +02:00
Vikeo
a9f99ab5a1 last cleanup 2024-09-22 09:45:43 +02:00
Vikeo
0cf7761e6b use monarch button 2024-09-22 09:27:48 +02:00
Vikeo
0ad5dfadb6 monarch button 2024-09-22 09:27:15 +02:00
Vikeo
7021646790 cleanup 2024-09-22 09:25:57 +02:00
Vikeo
6c6964dc22 Merge branch 'main' into monarch 2024-08-31 20:15:06 +02:00
Vikeo
b8ac0b9250 wip 2024-08-31 20:05:13 +02:00
14 changed files with 183 additions and 26 deletions

View File

@@ -3,6 +3,7 @@ import {
CommanderTax,
Energy,
Experience,
Monarch,
PartnerTax,
Poison,
} from '../../Icons/generated';
@@ -108,6 +109,9 @@ export const InfoDialog = ({
<li className="flex items-center">
<Experience className="size-6" /> - Experience
</li>
<li className="flex items-center">
<Monarch className="size-6" /> - Monarch
</li>
</ul>
<h3 className="text-lg font-bold mb-2">Other functionality</h3>

View File

@@ -1,8 +1,10 @@
import { useEffect, useRef, useState } from 'react';
import { twc } from 'react-twc';
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
import { Player, Rotation } from '../../Types/Player';
import { RotationDivProps } from '../Buttons/CommanderDamage';
import LifeCounterButton from '../Buttons/LifeCounterButton';
import { MonarchCrown } from '../Misc/MonarchCrown';
import { OutlinedText } from '../Misc/OutlinedText';
const LifeContainer = twc.div<RotationDivProps>((props) => [
@@ -33,7 +35,7 @@ const RecentDifference = twc.div`
absolute min-w-[20vmin] drop-shadow-none text-center bg-interface-recentDifference-background tabular-nums rounded-full p-[6px 12px] text-[8vmin] text-interface-recentDifference-text animate-fadeOut
top-1/4 left-[50%] -translate-x-1/2
data-[isSide=true]:top-1/3 data-[isSide=true]:translate-x-1/4 data-[isSide=true]:translate-y-1/2 data-[isSide=true]:rotate-[270deg] data-[isSide=true]:left-auto
data-[is-side=true]:top-1/3 data-[is-side=true]:translate-x-1/4 data-[is-side=true]:translate-y-1/2 data-[is-side=true]:rotate-[270deg] data-[is-side=true]:left-auto
`;
type HealthProps = {
@@ -53,6 +55,8 @@ const Health = ({
const [fontSize, setFontSize] = useState(16);
const textContainerRef = useRef<HTMLDivElement | null>(null);
const { settings } = useGlobalSettings();
useEffect(() => {
if (!textContainerRef.current) {
return;
@@ -104,6 +108,8 @@ const Health = ({
return (
<LifeContainer $rotation={player.settings.rotation}>
{settings.useMonarch && <MonarchCrown player={player} />}
<LifeCounterButton
player={player}
setLifeTotal={handleLifeChange}
@@ -111,29 +117,32 @@ const Health = ({
increment={-1}
/>
{player.name && isSide ? (
<div className="size-full relative flex items-center justify-start">
<div className="fixed flex justify-center -rotate-90 left-[5.4vmax] ">
<div
data-is-side={isSide}
className="size-full absolute flex items-start justify-center pointer-events-none webkit-user-select-none
data-[is-side=true]:items-center data-[is-side=true]:justify-start
"
>
{player.name && isSide ? (
<div className="fixed flex justify-center -rotate-90 left-[5.4vmax]">
<div
data-contrast={player.iconTheme}
className="absolute text-[4vmin] opacity-50 font-bold
className="absolute text-[4vmin] opacity-50 font-bold text-center text-nowrap
data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
>
{player.name}
</div>
</div>
</div>
) : (
<div className="w-full h-full relative flex items-start justify-center">
) : (
<div
data-contrast={player.iconTheme}
className="absolute text-[4vmin] -top-[1.1vmin] opacity-50 font-bold
className="absolute text-[4vmin] -top-[1.1vmin] opacity-50 font-bold text-center
data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
>
{player.name}
</div>
</div>
)}
)}
</div>
<TextWrapper>
<LifeCounterTextContainer
@@ -148,7 +157,7 @@ const Health = ({
{player.lifeTotal}
</OutlinedText>
{recentDifference !== 0 && (
<RecentDifference data-isSide={isSide} key={differenceKey}>
<RecentDifference data-is-side={isSide} key={differenceKey}>
{recentDifference > 0 ? '+' : ''}
{recentDifference}
</RecentDifference>

View File

@@ -238,6 +238,7 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
recentDifference={recentDifference}
handleLifeChange={handleLifeChange}
/>
<ExtraCountersBar player={player} />
<PlayerMenu
isShown={showPlayerMenu}

View File

@@ -14,7 +14,7 @@ export const IconCheckbox = ({
className?: string;
}) => {
return (
<div className={className}>
<div className={className} >
<label>
<input
name={name}

View File

@@ -0,0 +1,67 @@
import { usePlayers } from '../../Hooks/usePlayers';
import { Monarch } from '../../Icons/generated';
import { Player, Rotation } from '../../Types/Player';
import { IconCheckbox } from './IconCheckbox';
export const MonarchCrown = ({ player }: { player: Player }) => {
const { players, setPlayers } = usePlayers();
const iconSize =
player.settings.rotation === Rotation.SideFlipped ||
player.settings.rotation === Rotation.Side
? '5vmax'
: '10vmin';
const rotationIsSide =
player.settings.rotation === Rotation.SideFlipped ||
player.settings.rotation === Rotation.Side;
return (
<div
data-rotation-is-side={rotationIsSide}
className="absolute w-full h-full flex items-start justify-center pointer-events-none z-[1]
data-[rotation-is-side=true]:justify-start data-[rotation-is-side=true]:items-center
"
>
<div
data-rotation-is-side={rotationIsSide}
className="data-[rotation-is-side=true]:-rotate-90"
>
<IconCheckbox
className="pointer-events-all"
name="useMonarch"
checked={player.isMonarch}
icon={<Monarch size={iconSize} color={player.color} stroke="white" />}
checkedIcon={
<div>
<Monarch
size={iconSize}
stroke="white"
className="absolute blur z-[-1] text-icons-gold"
/>
<Monarch
size={iconSize}
stroke="white"
className="text-icons-gold"
/>
</div>
}
onChange={(e) => {
const updatedPlayer = { ...player, isMonarch: e.target.checked };
const updatedPlayers = players.map((p) => {
if (p.index === player.index) {
return updatedPlayer;
}
return { ...p, isMonarch: false };
});
setPlayers(updatedPlayers);
}}
aria-checked={player.isMonarch}
aria-label="Monarch"
/>
</div>
</div>
);
};

View File

@@ -11,6 +11,7 @@ import {
Experience,
FullscreenOff,
FullscreenOn,
Monarch,
NameTag,
PartnerTax,
Poison,
@@ -110,6 +111,7 @@ const PlayerMenu = ({
wakeLock,
goToStart,
settings,
setSettings,
setPlaying,
setRandomizingPlayer,
saveCurrentGame,
@@ -360,6 +362,36 @@ const PlayerMenu = ({
aria-label="Experience"
/>
</div>
<div>
<IconCheckbox
name="useMonarch"
checked={settings.useMonarch}
icon={
<Monarch
size={extraCountersSize}
color="black"
stroke="white"
strokeWidth={2.5}
/>
}
checkedIcon={
<Monarch
size={extraCountersSize}
color={player.color}
stroke="white"
strokeWidth={2.5}
/>
}
onChange={(e) => {
analytics.trackEvent('toggle_monarch', {
checked: e.target.checked,
});
setSettings({ ...settings, useMonarch: e.target.checked });
}}
aria-checked={settings.useMonarch}
aria-label="Monarch"
/>
</div>
</TogglesSection>
<ButtonsSections>
<button

View File

@@ -231,6 +231,7 @@ export const createInitialPlayers = ({
isStartingPlayer: false,
isSide: rotation === Rotation.Side || rotation === Rotation.SideFlipped,
name: '',
isMonarch: false,
};
players.push(player);

View File

@@ -0,0 +1,34 @@
import PropTypes from 'prop-types';
import { SVGProps } from 'react';
interface SVGRProps {
title?: string;
titleId?: string;
size?: string;
}
const Monarch = ({
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 52 52"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId}>{title}</title> : null}
<path
fill="currentColor"
d="M46.163 38.82s-8.614 2.73-14.234 3.106c-2.508.167-3.918 0-6.429 0-2.51 0-3.921.167-6.429 0-5.62-.376-14.234-3.107-14.234-3.107s.637-3.944.459-6.471C5.053 28.888 3 24.038 3 24.038s2.897 2.25 4.592 1.294C9.78 24.098 10.5 20 10.5 20s3.006 6.024 7 5.332c2.386-.414 3.327-1.974 4.5-4.016.97-1.69 1.27-4.827 1.27-4.827l1.77-4.827L25.5 10l.46 1.662 1.77 4.827s.3 3.136 1.27 4.827c1.173 2.042 2.388 3.353 4.5 4.016 4.051 1.273 7-5.332 7-5.332s.72 4.098 2.908 5.332c1.695.956 4.592-1.294 4.592-1.294s-2.053 4.85-2.296 8.31c-.178 2.527.46 6.471.46 6.471"
/>
</svg>
);
};
Monarch.propTypes = {
title: PropTypes.string,
};
export default Monarch;

View File

@@ -10,6 +10,7 @@ export { default as FullscreenOn } from './FullscreenOn';
export { default as Info } from './Info';
export { default as LittleGuy } from './LittleGuy';
export { default as Logo } from './Logo';
export { default as Monarch } from './Monarch';
export { default as NameTag } from './NameTag';
export { default as PartnerTax } from './PartnerTax';
export { default as Poison } from './Poison';

View File

@@ -0,0 +1,3 @@
<svg width="52" height="52" viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M46.1633 38.8191C46.1633 38.8191 37.5494 41.5504 31.9286 41.9256C29.421 42.093 28.0105 41.9256 25.5 41.9256C22.9895 41.9256 21.579 42.093 19.0714 41.9256C13.4506 41.5504 4.83673 38.8191 4.83673 38.8191C4.83673 38.8191 5.47353 34.8751 5.29592 32.3476C5.05284 28.8883 3 24.0377 3 24.0377C3 24.0377 5.89664 26.2882 7.59184 25.332C9.77975 24.0978 10.5 20 10.5 20C10.5 20 13.5058 26.0243 17.5 25.332C19.886 24.9184 20.8269 23.3583 22 21.3158C22.9708 19.6255 23.2704 16.4887 23.2704 16.4887L25.0408 11.6616L25.5 10L25.9592 11.6616L27.7296 16.4887C27.7296 16.4887 28.0292 19.6255 29 21.3158C30.1731 23.3583 31.3881 24.6686 33.5 25.332C37.5515 26.6047 40.5 20 40.5 20C40.5 20 41.2203 24.0978 43.4082 25.332C45.1034 26.2882 48 24.0377 48 24.0377C48 24.0377 45.9472 28.8883 45.7041 32.3476C45.5265 34.8751 46.1633 38.8191 46.1633 38.8191Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 964 B

View File

@@ -94,18 +94,6 @@ export const GlobalSettingsProvider = ({
);
};
const removeLocalStorage = async () => {
localStorage.removeItem('initialGameSettings');
localStorage.removeItem('players');
localStorage.removeItem('playing');
localStorage.removeItem('showPlay');
localStorage.removeItem('preStartComplete');
setPlaying(false);
setShowPlay(false);
setPreStartCompleted(false);
};
// Set settings if they are not valid
useEffect(() => {
// If there are no saved settings, set default settings
@@ -177,6 +165,19 @@ export const GlobalSettingsProvider = ({
}
const ctxValue = useMemo((): GlobalSettingsContextType => {
const removeLocalStorage = async () => {
localStorage.removeItem('initialGameSettings');
localStorage.removeItem('players');
localStorage.removeItem('playing');
localStorage.removeItem('showPlay');
localStorage.removeItem('preStartComplete');
setPlaying(false);
setShowPlay(false);
setPreStartCompleted(false);
setSettings({ ...settings, useMonarch: false });
};
const goToStart = async () => {
const currentPlayers = localStorage.getItem('players');
@@ -292,7 +293,6 @@ export const GlobalSettingsProvider = ({
setPreStartCompleted: setPreStartCompletedAndLocalStorage,
savedGame,
saveCurrentGame: setCurrentGameAndLocalStorage,
version: {
installedVersion: import.meta.env.VITE_APP_VERSION,
remoteVersion,

View File

@@ -7,6 +7,7 @@ export type Player = {
commanderDamage: CommanderDamage[];
extraCounters: ExtraCounter[];
isStartingPlayer: boolean;
isMonarch: boolean;
hasLost: boolean;
isSide: boolean;
name: string;

View File

@@ -26,6 +26,7 @@ export type Settings = {
goFullscreenOnStart: boolean;
preStartMode: PreStartMode;
showAnimations: boolean;
useMonarch: boolean;
};
export type InitialGameSettings = {
@@ -59,6 +60,7 @@ export const settingsSchema = z.object({
goFullscreenOnStart: z.boolean(),
preStartMode: z.nativeEnum(PreStartMode),
showAnimations: z.boolean(),
useMonarch: z.boolean().default(false),
});
export const defaultSettings: Settings = {
@@ -68,4 +70,5 @@ export const defaultSettings: Settings = {
showPlayerMenuCog: true,
preStartMode: PreStartMode.None,
showAnimations: true,
useMonarch: false,
};

View File

@@ -20,6 +20,7 @@ export const baseColors = {
icons: {
dark: '#000000',
light: '#F9FFE3',
gold: '#FFD700',
},
text: {
primary: '#F9FFE3',