Compare commits

...

165 Commits

Author SHA1 Message Date
Vikeo
e6e562a0c6 fix types 2024-05-22 00:14:25 +02:00
Vikeo
3e38332a68 check version using semver 2024-05-22 00:10:16 +02:00
Vikeo
c23509ae26 icon tweak 2024-05-21 23:48:25 +02:00
Vikeo
9f1cc35eab new logo by elin 2024-05-21 21:06:43 +02:00
Vikeo
fe43e464de pnpm 9 2024-05-21 20:13:36 +02:00
Viktor Rådberg
b074499c1f small settings menu tweak 2024-05-15 18:25:45 +02:00
Viktor Rådberg
5953cf6b49 debump 2024-05-15 18:06:18 +02:00
Viktor Rådberg
acefbb8846 fix start menu width 2024-05-15 18:05:02 +02:00
Viktor Rådberg
adb79bf0e8 fix width 2024-05-10 23:23:32 +02:00
Viktor Rådberg
92be2e56d4 bump 2024-05-05 20:52:51 +02:00
Viktor Rådberg
854ca81a40 fix width 2024-05-05 20:52:30 +02:00
Viktor Rådberg
9119ca5518 fix dialog on iOS 2024-05-01 14:46:53 +02:00
Vikeo
2837bb59b3 bumpb 2024-05-01 14:25:03 +02:00
Viktor Rådberg
623aec29f8 Remove MUI (#36)
* remove mui from layouts

* remove button

* remove switch

* remove mui
2024-05-01 14:24:38 +02:00
Vikeo
63aace2b07 fix typo and adjust touch roulette timings 2024-04-07 23:58:39 +02:00
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
Viktor Rådberg
92f954130f bump 2024-03-31 19:13:27 +02:00
Viktor Rådberg
112023bdd5 log if it works or not 2024-03-31 19:08:17 +02:00
Viktor Rådberg
4e6dc56d99 add api key back 2024-03-31 19:02:59 +02:00
Viktor Rådberg
e427bfd0cf new analytics api key 2024-03-31 18:52:08 +02:00
Viktor Rådberg
ed10edc6d2 bump 2024-03-31 12:30:18 +02:00
Viktor Rådberg
7696b357b4 add trivia prestart mode 2024-03-31 12:30:03 +02:00
Viktor Rådberg
a7b78b8e7a settings tracking 2024-03-31 12:28:34 +02:00
Viktor Rådberg
fa95d171b7 bump 2024-03-30 14:23:26 +01:00
Viktor Rådberg
00a556be0e always include version in tracked events 2024-03-30 14:22:37 +01:00
Viktor Rådberg
3276dc81fc Clearer way to see if there is an update, more fun tracking 2024-03-30 14:04:12 +01:00
Viktor Rådberg
28c2ff536f bump 2024-03-30 10:28:49 +01:00
Viktor Rådberg
6beddf06e2 better invalid settings handling 2024-03-30 10:27:16 +01:00
Viktor Rådberg
2a885f9a43 bump 2024-03-29 23:25:50 +01:00
Viktor Rådberg
9c27f34261 More Pre-Start modes. (#34)
* do

* parse settings before setting
2024-03-29 23:24:35 +01:00
Viktor Rådberg
fa5829b402 fix keep away state 2024-03-23 16:23:03 +01:00
Viktor Rådberg
71f26d0dc5 fix keep awake toggle and layout styling 2024-03-23 16:22:48 +01:00
Viktor Rådberg
3a568fc3ab Better scaling on small devices 2024-03-23 16:05:29 +01:00
Viktor Rådberg
355f4bd4cd track only on prod, and add life changed amount tracking 2024-03-23 12:39:07 +01:00
Viktor Rådberg
17e174bfe1 new deploy 2024-03-23 11:24:14 +01:00
Viktor Rådberg
e1e8da858b remove prod check 2024-03-23 11:23:46 +01:00
Viktor Rådberg
e02f071415 new deploy 2024-03-23 11:23:06 +01:00
Viktor Rådberg
e04f31bb67 prod log 2024-03-23 11:22:12 +01:00
Viktor Rådberg
e5386d08a4 fix settings cog color 2024-03-17 19:01:15 +01:00
Viktor Rådberg
d6cd678e9f fix random interval wrapper showing if enabled but show player is disabled 2024-03-17 18:41:58 +01:00
Viktor Rådberg
334b46db6e bump 2024-03-16 22:29:25 +01:00
Viktor Rådberg
e03ecc6f51 Merge pull request #33 from Vikeo/random-player-interval
random player interval
2024-03-16 22:28:49 +01:00
Viktor Rådberg
d4dc44076d fix lint 2024-03-16 22:28:37 +01:00
Viktor Rådberg
a1b5cfd871 fix tsc 2024-03-16 22:26:06 +01:00
Viktor Rådberg
f11eea5e53 better styling 2024-03-16 22:23:03 +01:00
Viktor Rådberg
905912a7fd fix random interval 2024-03-16 21:59:24 +01:00
Viktor Rådberg
a90dd7c9ea wip 2024-03-16 14:40:18 +01:00
Viktor Rådberg
ef1310d674 bump 2024-03-16 13:22:47 +01:00
Viktor Rådberg
fe3bb6c78c show starting player untill press 2024-03-16 13:22:03 +01:00
Viktor Rådberg
6d2b3b6a6f Add option to show player menu cog 2024-03-16 12:29:16 +01:00
Viktor Rådberg
0f86928cb3 Merge pull request #32 from Vikeo/better-colors
Better colors
2024-03-16 10:42:13 +01:00
Viktor Rådberg
efbfb7719c tsc 2024-03-16 10:40:18 +01:00
Viktor Rådberg
71e5614f52 bump to new version 2024-03-16 10:38:23 +01:00
Viktor Rådberg
677fd79bee fix long press down 2024-03-16 10:23:15 +01:00
Viktor Rådberg
1bff41bc10 remove colorful 2024-03-16 10:04:35 +01:00
Viktor Rådberg
7852520f8e minus plus icon color 2024-03-16 09:59:40 +01:00
Viktor Rådberg
04c3d60967 use normal picker again 2024-03-16 09:31:59 +01:00
Viktor Rådberg
664e2e5688 round color picker 2024-02-19 07:38:17 +01:00
Viktor Rådberg
6eb7ac9f50 Merge branch 'main' into better-colors 2024-02-18 16:08:09 +01:00
Viktor Rådberg
ef06e0d125 bump 2024-02-09 23:04:29 +01:00
Viktor Rådberg
ae9f5707b2 update blur 2024-02-09 23:04:14 +01:00
Viktor Rådberg
a18c253624 bump 2024-01-31 23:12:46 +01:00
Viktor Rådberg
3f319c4f3c add some blur to settings 2024-01-31 23:12:31 +01:00
Viktor Rådberg
8b33a2a38a wip 2024-01-28 17:04:30 +01:00
Viktor Rådberg
cc915dff36 better color picker 2024-01-28 11:54:37 +01:00
Viktor Rådberg
db80e563f2 bump 2024-01-27 18:05:54 +01:00
Viktor Rådberg
573af42b75 fix taps and some settings stuff 2024-01-27 18:05:18 +01:00
Viktor Rådberg
89e1eaff4e bump 2024-01-27 16:25:40 +01:00
Viktor Rådberg
0f4e896342 Merge pull request #31 from Vikeo/swipable-settings
Swipable settings
2024-01-27 16:23:54 +01:00
Viktor Rådberg
dc1d5fe01d tsc 2024-01-27 16:20:09 +01:00
Viktor Rådberg
41e73d2c0c swipe 2024-01-27 11:05:54 +01:00
Viktor Rådberg
724dcf086c is side 2024-01-27 09:32:00 +01:00
Viktor Rådberg
51f9c4d20e initial test 2024-01-26 21:24:40 +01:00
Viktor Rådberg
354c0dbbb2 bump 2024-01-20 11:11:03 +01:00
Viktor Rådberg
3770d13beb fix some styling 2024-01-20 10:56:53 +01:00
Viktor Rådberg
13733242a2 bump 2024-01-14 14:39:20 +01:00
Viktor Rådberg
81f3891b20 add better pwa support 2024-01-14 14:38:56 +01:00
Viktor Rådberg
e153de9093 Release 0.5.51 2024-01-14 13:42:11 +01:00
Viktor Rådberg
07775f85d2 fix start menu style 2024-01-14 13:41:51 +01:00
Viktor Rådberg
10039175a1 bump 2024-01-14 13:14:53 +01:00
Viktor Rådberg
bcf2a0a840 new colors 2024-01-14 13:14:33 +01:00
Viktor Rådberg
d25da5d97b fix styling 2024-01-14 12:31:57 +01:00
Viktor Rådberg
f5a80e573e cache 2024-01-14 10:41:14 +01:00
Viktor Rådberg
1f36264e39 update package json 2024-01-14 10:40:48 +01:00
Viktor Rådberg
d615cfd3ba reset game styling 2024-01-14 10:38:28 +01:00
Viktor Rådberg
4453b12ce6 bump 2024-01-13 20:32:10 +01:00
Viktor Rådberg
d601a820f8 remove log 2024-01-13 20:32:00 +01:00
Viktor Rådberg
0455f43794 test 2024-01-13 20:26:07 +01:00
Viktor Rådberg
f94103fe51 fix 2024-01-13 20:20:50 +01:00
Viktor Rådberg
c36668b933 fix 2024-01-13 20:20:20 +01:00
Viktor Rådberg
f9d0346300 bump 2024-01-13 20:18:50 +01:00
Viktor Rådberg
2f3ee74c74 test 2024-01-13 20:15:43 +01:00
Viktor Rådberg
f8f0788b97 bump 2024-01-13 19:53:02 +01:00
Viktor Rådberg
bfe25eacb7 fix lint 2024-01-13 19:44:27 +01:00
Viktor Rådberg
7b0965c0dd test release 2024-01-13 19:40:36 +01:00
Viktor Rådberg
e55ea6a83a final test 2024-01-13 19:32:16 +01:00
Viktor Rådberg
481196de9b test 2024-01-13 19:30:42 +01:00
Viktor Rådberg
a136dbd3f9 test 2024-01-13 19:28:50 +01:00
Viktor Rådberg
8d23349dac test 2024-01-13 19:27:25 +01:00
Viktor Rådberg
a7caa46156 test 2024-01-13 19:22:31 +01:00
Viktor Rådberg
39cd3faae2 test 2024-01-13 19:19:30 +01:00
Viktor Rådberg
bdaa8e602f test 2024-01-13 19:18:45 +01:00
Viktor Rådberg
26490103a9 Merge pull request #30 from Vikeo/develop
test pr
2024-01-13 19:14:03 +01:00
Viktor Rådberg
56b07784d5 fix 2024-01-13 19:13:34 +01:00
Viktor Rådberg
4544c689a5 test 2024-01-13 19:12:11 +01:00
Viktor Rådberg
8a7a4b4127 test 2024-01-13 19:11:49 +01:00
Viktor Rådberg
391e654779 test 2024-01-13 18:58:07 +01:00
Viktor Rådberg
f79a0d3e7e test 2024-01-13 18:55:41 +01:00
Viktor Rådberg
0664e340a0 test 2024-01-13 18:49:06 +01:00
Viktor Rådberg
dcb98aeac6 test 2024-01-13 18:48:11 +01:00
Viktor Rådberg
89b62ddac4 release 2024-01-13 18:37:12 +01:00
Viktor Rådberg
c704e3c7f4 release workflow 2024-01-13 18:26:34 +01:00
Viktor Rådberg
69a71e2d6e version check 2024-01-13 18:17:24 +01:00
Viktor Rådberg
18945204bf test 2024-01-13 18:03:59 +01:00
Viktor Rådberg
495e731636 tet 2024-01-13 17:57:03 +01:00
Viktor Rådberg
67b231f0d4 more logs 2024-01-13 17:12:31 +01:00
Viktor Rådberg
9d42fb1635 check version 2024-01-13 17:07:03 +01:00
Viktor Rådberg
38ad046344 test 2024-01-13 15:39:56 +01:00
Viktor Rådberg
bc87f073af test 2024-01-13 15:35:42 +01:00
Viktor Rådberg
da46c25944 test version update 2024-01-13 15:34:24 +01:00
Viktor Rådberg
104f54f5b7 Merge pull request #29 from Vikeo/develop
Release 0.5.0
2024-01-13 15:13:52 +01:00
Viktor Rådberg
101a055694 bump version 2024-01-13 15:09:58 +01:00
Viktor Rådberg
38e4cb8e8c dvh 2024-01-13 15:02:33 +01:00
Viktor Rådberg
4ecb83060d fix webkit highlight 2024-01-13 14:55:39 +01:00
Viktor Rådberg
4f231ba6f4 finish up tailwind 2024-01-13 14:50:36 +01:00
Viktor Rådberg
3cd982c643 wip tailwind 2024-01-07 19:02:07 +01:00
Viktor Rådberg
1013914cdf commander damage tailwind 2024-01-07 00:15:37 +01:00
Viktor Rådberg
db85fc2102 fix some tailwind 2024-01-06 19:45:25 +01:00
Viktor Rådberg
2b0d8102d8 fix version 2024-01-06 01:14:30 +01:00
Viktor Rådberg
35e0224066 fix backdrop 2024-01-06 00:47:21 +01:00
Viktor Rådberg
1fa433a38f remove styled components global styles 2024-01-06 00:45:35 +01:00
Viktor Rådberg
26821273d7 Merge branch 'develop' 2023-12-27 21:39:59 +01:00
Viktor Rådberg
7f19214624 reload window 2023-12-27 21:39:50 +01:00
Viktor Rådberg
8b2cd43a96 Merge pull request #28 from Vikeo/develop
New release
2023-12-27 21:31:40 +01:00
Viktor Rådberg
23e18f8f41 fix styling 2023-12-27 21:29:28 +01:00
Viktor Rådberg
23b844c47e fix 2023-12-27 21:11:44 +01:00
Viktor Rådberg
6ade1998f6 fix 2023-12-27 21:08:43 +01:00
Viktor Rådberg
cc98a1b84a test 2023-12-27 21:05:27 +01:00
Viktor Rådberg
2ca6b91d09 test 2023-12-27 21:03:22 +01:00
Viktor Rådberg
00bda4fb68 test 2023-12-27 20:54:22 +01:00
Viktor Rådberg
d09d992535 test 2023-12-27 20:52:34 +01:00
Viktor Rådberg
e96e4f3aa9 test 2023-12-27 20:50:57 +01:00
Viktor Rådberg
cb132360a9 test 2023-12-27 20:48:29 +01:00
Viktor Rådberg
66b0892461 revert 2023-12-27 20:42:57 +01:00
Viktor Rådberg
fdab09d598 fix 2023-12-27 20:41:30 +01:00
Viktor Rådberg
ec030e7076 remove local storage when initial settings is not correct 2023-12-27 20:39:13 +01:00
Viktor Rådberg
9812c6737c add zod validation 2023-12-27 20:35:10 +01:00
Viktor Rådberg
e8528f46ae remove console logs 2023-12-27 19:57:16 +01:00
Viktor Rådberg
4ff7f67484 Merge pull request #27 from Vikeo/tailwind-wip
Tailwind wip
2023-12-27 19:56:38 +01:00
Viktor Rådberg
bc97e459cd Merge pull request #26 from Vikeo/develop
style fixes
2023-12-24 14:48:14 +01:00
Viktor Rådberg
866dca8e41 Merge pull request #25 from Vikeo/develop
Change start menu header from h2 to h1
2023-10-01 19:01:12 +02:00
Viktor Rådberg
5859bb5a49 Merge pull request #24 from Vikeo/develop
Release 0.4.0
2023-09-28 14:34:29 +02:00
Viktor Rådberg
20fb2153b3 Merge pull request #23 from Vikeo/develop
Just another small release
2023-09-26 17:31:25 +02:00
Viktor Rådberg
75038212c5 Merge pull request #22 from Vikeo/develop
Release 0.3.0
2023-09-22 14:39:31 +02:00
Viktor Rådberg
b712fb6e03 Merge pull request #21 from Vikeo/develop
add button to go back to start if render fails
2023-09-18 22:32:46 +02:00
Viktor Rådberg
3d27335fd0 Merge pull request #20 from Vikeo/develop
Add reset game button
2023-09-18 18:29:44 +02:00
Viktor Rådberg
18b53669d2 Merge pull request #19 from Vikeo/develop
New Release
2023-09-18 14:01:27 +02:00
Viktor Rådberg
28954eb948 Merge pull request #18 from Vikeo/develop
yarn instead of bun
2023-09-17 19:23:32 +02:00
Viktor Rådberg
3c59d5d05b Merge pull request #17 from Vikeo/develop
New release
2023-09-17 19:20:10 +02:00
Viktor Rådberg
22b58c74d6 Update start page text (#16)
* fix z index of settings and lose button

* update start page promt
2023-09-02 08:32:14 +02:00
98 changed files with 14190 additions and 9675 deletions

View File

@@ -1,8 +1,12 @@
robots.txt,1693082171694,391d14b3c2f8c9143a27a28c7399585142228d4d1bdbe2c87ac946de411fa9a2 index.html,1711905710499,4b604b23faec8d63a58e07b96d724a1aea56a7c86d57c9af791832ce87a552e7
manifest.json,1693082171694,91ce94afb71f33a477f5d8d48c3f98bd7de422279c74f17b6500eec72003ac1a manifest.webmanifest,1711905710499,f2bf253209f6e292a6b0dbfa06fb4ac188eb5f2dba568c3ad5511b9ed52c1f51
assets/index-5265c558.css,1693082171837,08c4451946bbdf520fe337edb365417a8bbf91914c018b83866723ef52d57b43 manifest.json,1711905710294,7ff5111aa04a42adff3b38924ec467b6f345ed0309dba1486dc9b783b60c2a9d
index.html,1693082171837,09e1919fbaaa3a0bf08f43eb46c29136d62a7747b41f8b5d0f4a7ed23337c344 sw.js,1711905711506,1ef2f4f40ec8dca15cc42d547462ade1e84314ea9722276f5994ccee7bbdf80f
logo192.png,1693082171693,4309255bccbdbb341b5ab88708677e3d43b9e171d2666528ff932295a8257e4e robots.txt,1711905710294,391d14b3c2f8c9143a27a28c7399585142228d4d1bdbe2c87ac946de411fa9a2
favicon.ico,1693082171692,48d8c1b9714dbc9bcb012d9c9f04112d229f20e6c889bda588ac159f973e6a8d registerSW.js,1711905710499,5b6445b5215737c53ef0d379c151d57de165a056de2d1c5812ed614f158ebcbd
logo512.png,1693082171694,92c7c05dc98170596d04f48e5e60eaae9535f409bcaeff129fd98fef8aba9f4e workbox-3e911b1d.js,1711905711507,d5dbc868a5c07af633d29de7ba3ffe37542aaaabdf33713b4298df31f92f11ff
assets/index-5023e89e.js,1693082171838,8a6177168e95e1ca90e5ad8774252a8a02a9a78765bd329b7deae729c01aedf3 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

View File

@@ -1,20 +0,0 @@
# This file was auto-generated by the Firebase CLI
# https://github.com/firebase/firebase-tools
name: Deploy to Firebase Hosting on merge
'on':
push:
branches:
- main
jobs:
build_and_deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: oven-sh/setup-bun@v1
- run: bun install && bun run build && bun run lint
- uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: '${{ secrets.GITHUB_TOKEN }}'
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_LIFE_TRINKET }}'
channelId: live
projectId: life-trinket

59
.github/workflows/firebase-release.yml vendored Normal file
View File

@@ -0,0 +1,59 @@
name: Deploy to Firebase Hosting
'on':
push:
tags:
- '*'
jobs:
build_and_deploy:
runs-on: ubuntu-latest
env:
VITE_REPO_READ_ACCESS_TOKEN: ${{ secrets.REPO_READ_ACCESS_TOKEN }}
VITE_FIREBASE_ANALYTICS_API_KEY: ${{ secrets.FIREBASE_ANALYTICS_API_KEY }}
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Setup bun
uses: oven-sh/setup-bun@v1
- name: Build, lint, and deploy
run: |
bun install
bun run build
bun run lint
- name: Deploy to Firebase Hosting
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: '${{ secrets.GITHUB_TOKEN }}'
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_LIFE_TRINKET }}'
channelId: live
projectId: life-trinket
release:
needs: build_and_deploy
runs-on: ubuntu-latest
env:
working-directory: ${{ github.workspace }}
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: get version
id: version
uses: notiz-dev/github-action-json-property@v0.2.0
with:
path: 'package.json'
prop_path: 'version'
- name: Create Release Note
id: create_release_note
run: echo "Release Note for version ${{ steps.version.outputs.prop }}" > release_note.txt
- name: Create Release
uses: ncipollo/release-action@v1.13.0
with:
bodyFile: release_note.txt
commit: ${{ github.sha }}
tag: '${{ steps.version.outputs.prop }}'
token: ${{ secrets.RELEASE_TOKEN }}

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
node-linker=hoisted

12
CHANGELOG.md Normal file
View File

@@ -0,0 +1,12 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.5.0] - 2024-01-13
### Changed
- Styling with Tailwind CSS instead of styled-components

BIN
bun.lockb

Binary file not shown.

View File

@@ -1,11 +1,12 @@
{ {
"name": "life-trinket", "name": "life-trinket",
"private": true, "private": true,
"version": "0.4.0", "version": "0.9.9",
"type": "commonjs", "type": "commonjs",
"engines": { "engines": {
"node": ">=18", "node": ">=20",
"npm": "please use bun or yarn :) " "yarn": "use pnpm",
"npm": "please use pnpm"
}, },
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@@ -13,37 +14,42 @@
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview", "preview": "vite preview",
"generate-icons": "npx @svgr/cli src/Icons/svgs", "generate-icons": "npx @svgr/cli src/Icons/svgs",
"deploy": "bun build && firebase deploy --only hosting" "deploy": "bun run build && firebase deploy --only hosting"
}, },
"dependencies": { "dependencies": {
"@mui/material": "^5.13.6",
"firebase": "^10.3.0", "firebase": "^10.3.0",
"ga-4-react": "^0.1.281", "ga-4-react": "^0.1.281",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-screen-wake-lock": "^3.0.2", "react-screen-wake-lock": "^3.0.2",
"styled-components": "^6.0.7" "react-swipeable": "^7.0.1",
"react-twc": "^1.3.0",
"semver": "^7.6.2",
"zod": "^3.22.4"
}, },
"devDependencies": { "devDependencies": {
"@emotion/react": "^11.11.1", "@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.5",
"@savvywombat/tailwindcss-grid-areas": "^3.1.0", "@savvywombat/tailwindcss-grid-areas": "^4.0.0",
"@svgr/cli": "^8.1.0", "@svgr/cli": "^8.1.0",
"@types/react": "^18.2.15", "@types/react": "^18.3.1",
"@types/react-dom": "^18.2.7", "@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^6.0.0", "@types/semver": "^7.5.8",
"@typescript-eslint/parser": "^6.0.0", "@typescript-eslint/eslint-plugin": "^7.8.0",
"@vitejs/plugin-react-swc": "^3.3.2", "@typescript-eslint/parser": "^7.8.0",
"autoprefixer": "^10.4.16", "@vitejs/plugin-react-swc": "^3.6.0",
"autoprefixer": "^10.4.19",
"eslint": "^8.45.0", "eslint": "^8.45.0",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.3", "eslint-plugin-react-refresh": "^0.4.6",
"firebase-tools": "^12.5.2", "firebase-tools": "^13.7.5",
"install": "^0.13.0", "install": "^0.13.0",
"postcss": "^8.4.32", "postcss": "^8.4.38",
"prettier": "2.8.8", "prettier": "2.8.8",
"tailwindcss": "^3.4.0", "prop-types": "^15.8.1",
"typescript": "^5.0.2", "tailwindcss": "^3.4.3",
"vite": "^4.4.5" "typescript": "^5.4.5",
"vite": "^5.2.10",
"vite-plugin-pwa": "^0.20.0"
} }
} }

10506
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -1,30 +1,14 @@
import { createGlobalStyle } from 'styled-components';
import { ThemeProvider } from '@mui/material';
import { LifeTrinket } from './Components/LifeTrinket'; import { LifeTrinket } from './Components/LifeTrinket';
import { theme } from './Data/theme';
import { GlobalSettingsProvider } from './Providers/GlobalSettingsProvider'; import { GlobalSettingsProvider } from './Providers/GlobalSettingsProvider';
import { PlayersProvider } from './Providers/PlayersProvider'; import { PlayersProvider } from './Providers/PlayersProvider';
const GlobalStyles = createGlobalStyle`
html,
body {
background-color: ${theme.palette.background.default};
}
#root {
touch-action: manipulation;
}
`;
const App = () => { const App = () => {
return ( return (
<ThemeProvider theme={theme}> <PlayersProvider>
<GlobalStyles /> <GlobalSettingsProvider>
<PlayersProvider> <LifeTrinket />
<GlobalSettingsProvider> </GlobalSettingsProvider>
<LifeTrinket /> </PlayersProvider>
</GlobalSettingsProvider>
</PlayersProvider>
</ThemeProvider>
); );
}; };

View File

@@ -1,110 +1,51 @@
import styled from 'styled-components';
import { css } from 'styled-components';
import { Player, Rotation } from '../../Types/Player';
import { useRef, useState } from 'react'; import { useRef, useState } from 'react';
import { OutlinedText } from '../Misc/OutlinedText'; import { TwcComponentProps, twc } from 'react-twc';
import { decrementTimeoutMs } from '../../Data/constants'; import { decrementTimeoutMs } from '../../Data/constants';
import { usePlayers } from '../../Hooks/usePlayers'; import { usePlayers } from '../../Hooks/usePlayers';
import { Player, Rotation } from '../../Types/Player';
import { OutlinedText } from '../Misc/OutlinedText';
const CommanderDamageContainer = styled.div<{ export type RotationDivProps = TwcComponentProps<'div'> & {
$rotation: number; $rotation?: number;
}>` };
display: flex;
flex-direction: row;
flex-grow: 1;
width: 100%;
${(props) => { export type RotationSpanProps = TwcComponentProps<'span'> & {
if ( $rotation?: number;
props.$rotation === Rotation.SideFlipped || };
props.$rotation === Rotation.Side
) {
return css`
flex-direction: column;
`;
}
}}
`;
const CommanderDamageButton = styled.button<{ export type RotationButtonProps = TwcComponentProps<'button'> & {
$backgroundColor?: string; $rotation?: number;
$rotation: number; };
}>`
display: flex;
flex-grow: 1;
border: none;
height: 10vmin;
width: 50%;
outline: none;
cursor: pointer;
background-color: ${(props) => props.$backgroundColor || 'antiquewhite'};
margin: 0;
user-select: none;
-webkit-touch-callout: none;
-webkit-tap-highlight-color: transparent;
-moz-user-select: -moz-none;
-webkit-user-select: none;
-ms-user-select: none;
padding: 0;
${(props) => {
if (
props.$rotation === Rotation.SideFlipped ||
props.$rotation === Rotation.Side
) {
return css`
width: 6vmax;
height: auto;
`;
}
}}
`;
const CommanderDamageTextContainer = styled.div<{ export const MAX_TAP_MOVE_DISTANCE = 20;
$rotation: number;
}>`
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-variant-numeric: tabular-nums;
pointer-events: none;
user-select: none;
-webkit-touch-callout: none;
-webkit-tap-highlight-color: transparent;
-moz-user-select: -moz-none;
-webkit-user-select: none;
-ms-user-select: none;
${(props) => { const CommanderDamageContainer = twc.div<RotationDivProps>((props) => [
if ( 'flex flex-grow',
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
props.$rotation === Rotation.Side ? 'flex-col'
) { : 'flex-row',
return css` ]);
rotate: 270deg;
`;
}
}}
`;
const PartnerDamageSeperator = styled.div<{ const CommanderDamageButton = twc.button<RotationButtonProps>((props) => [
$rotation: number; 'flex flex-grow border-none outline-none cursor-pointer m-0 p-0 webkit-user-select-none',
}>` props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
width: 1px; ? 'w-[6vmax] h-auto'
background-color: rgba(0, 0, 0, 1); : 'h-[10vmin] w-1/2',
]);
${(props) => { const CommanderDamageTextContainer = twc.div<RotationDivProps>((props) => [
if ( 'relative top-1/2 left-1/2 tabular-nums pointer-events-none select-none webkit-user-select-none',
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
props.$rotation === Rotation.Side ? 'rotate-[270deg]'
) { : '',
return css` ]);
width: auto;
height: 1px; const PartnerDamageSeparator = twc.div<RotationDivProps>((props) => [
`; 'bg-black',
} props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
}} ? 'w-full h-px'
`; : 'w-px',
]);
type CommanderDamageButtonComponentProps = { type CommanderDamageButtonComponentProps = {
player: Player; player: Player;
@@ -115,6 +56,7 @@ type CommanderDamageButtonComponentProps = {
type InputProps = { type InputProps = {
opponentIndex: number; opponentIndex: number;
isPartner: boolean; isPartner: boolean;
event: React.PointerEvent<HTMLButtonElement>;
}; };
export const CommanderDamage = ({ export const CommanderDamage = ({
@@ -124,12 +66,8 @@ export const CommanderDamage = ({
}: CommanderDamageButtonComponentProps) => { }: CommanderDamageButtonComponentProps) => {
const { updatePlayer } = usePlayers(); const { updatePlayer } = usePlayers();
const timeoutRef = useRef<NodeJS.Timeout | undefined>(undefined); const timeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
const [timeoutFinished, setTimeoutFinished] = useState(false); const [downLongPressed, setDownLongPressed] = useState(false);
const [hasPressedDown, setHasPressedDown] = useState(false); const downPositionRef = useRef({ x: 0, y: 0 });
const isSide =
player.settings.rotation === Rotation.Side ||
player.settings.rotation === Rotation.SideFlipped;
const handleCommanderDamageChange = ( const handleCommanderDamageChange = (
index: number, index: number,
@@ -168,34 +106,47 @@ export const CommanderDamage = ({
handleLifeChange(player.lifeTotal - increment); handleLifeChange(player.lifeTotal - increment);
}; };
const handleDownInput = ({ opponentIndex, isPartner }: InputProps) => { const handleDownInput = ({ opponentIndex, isPartner, event }: InputProps) => {
setTimeoutFinished(false); downPositionRef.current = { x: event.clientX, y: event.clientY };
setHasPressedDown(true); setDownLongPressed(false);
timeoutRef.current = setTimeout(() => { timeoutRef.current = setTimeout(() => {
setTimeoutFinished(true); setDownLongPressed(true);
handleCommanderDamageChange(opponentIndex, -1, isPartner); handleCommanderDamageChange(opponentIndex, -1, isPartner);
}, decrementTimeoutMs); }, decrementTimeoutMs);
}; };
const handleUpInput = ({ opponentIndex, isPartner }: InputProps) => { const handleUpInput = ({ opponentIndex, isPartner, event }: InputProps) => {
if (!(hasPressedDown && !timeoutFinished)) { if (downLongPressed) {
return; return;
} }
const upPosition = { x: event.clientX, y: event.clientY };
const hasMoved =
Math.abs(upPosition.x - downPositionRef.current.x) >
MAX_TAP_MOVE_DISTANCE ||
Math.abs(upPosition.y - downPositionRef.current.y) >
MAX_TAP_MOVE_DISTANCE;
if (hasMoved) {
return;
}
clearTimeout(timeoutRef.current); clearTimeout(timeoutRef.current);
handleCommanderDamageChange(opponentIndex, 1, isPartner); handleCommanderDamageChange(opponentIndex, 1, isPartner);
setHasPressedDown(false);
}; };
const handleLeaveInput = () => { const handleLeaveInput = () => {
setTimeoutFinished(true); setDownLongPressed(true);
clearTimeout(timeoutRef.current); clearTimeout(timeoutRef.current);
setHasPressedDown(false);
}; };
const opponentIndex = opponent.index; const opponentIndex = opponent.index;
const fontSize = isSide ? '4vmax' : '7vmin'; const fontSize = player.isSide ? '4vmax' : '7vmin';
const fontWeight = 'bold'; const fontWeight = 'bold';
const strokeWidth = isSide ? '0.4vmax' : '0.7vmin'; const strokeWidth = player.isSide ? '0.4vmax' : '0.7vmin';
return ( return (
<CommanderDamageContainer <CommanderDamageContainer
@@ -206,16 +157,18 @@ export const CommanderDamage = ({
<CommanderDamageButton <CommanderDamageButton
key={opponentIndex} key={opponentIndex}
$rotation={player.settings.rotation} $rotation={player.settings.rotation}
onPointerDown={() => onPointerDown={(e) =>
handleDownInput({ opponentIndex, isPartner: false }) handleDownInput({ opponentIndex, isPartner: false, event: e })
}
onPointerUp={(e) =>
handleUpInput({ opponentIndex, isPartner: false, event: e })
} }
onPointerUp={() => handleUpInput({ opponentIndex, isPartner: false })}
onPointerLeave={handleLeaveInput} onPointerLeave={handleLeaveInput}
onContextMenu={(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => { onContextMenu={(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
e.preventDefault(); e.preventDefault();
}} }}
$backgroundColor={opponent.color}
aria-label={`Commander damage. Player ${player.index}, opponent ${opponent.index}`} aria-label={`Commander damage. Player ${player.index}, opponent ${opponent.index}`}
style={{ background: opponent.color }}
> >
<CommanderDamageTextContainer $rotation={player.settings.rotation}> <CommanderDamageTextContainer $rotation={player.settings.rotation}>
<OutlinedText <OutlinedText
@@ -232,15 +185,15 @@ export const CommanderDamage = ({
{opponent.settings.usePartner && ( {opponent.settings.usePartner && (
<> <>
<PartnerDamageSeperator $rotation={player.settings.rotation} /> <PartnerDamageSeparator $rotation={player.settings.rotation} />
<CommanderDamageButton <CommanderDamageButton
key={opponentIndex} key={opponentIndex}
$rotation={player.settings.rotation} $rotation={player.settings.rotation}
onPointerDown={() => onPointerDown={(e) =>
handleDownInput({ opponentIndex, isPartner: true }) handleDownInput({ opponentIndex, isPartner: true, event: e })
} }
onPointerUp={() => onPointerUp={(e) =>
handleUpInput({ opponentIndex, isPartner: true }) handleUpInput({ opponentIndex, isPartner: true, event: e })
} }
onPointerLeave={handleLeaveInput} onPointerLeave={handleLeaveInput}
onContextMenu={( onContextMenu={(
@@ -248,8 +201,8 @@ export const CommanderDamage = ({
) => { ) => {
e.preventDefault(); e.preventDefault();
}} }}
$backgroundColor={opponent.color}
aria-label={`Partner Commander damage. Player ${player.index}, opponent ${opponent.index}`} aria-label={`Partner Commander damage. Player ${player.index}, opponent ${opponent.index}`}
style={{ background: opponent.color }}
> >
<CommanderDamageTextContainer $rotation={player.settings.rotation}> <CommanderDamageTextContainer $rotation={player.settings.rotation}>
<OutlinedText <OutlinedText

View File

@@ -1,60 +1,45 @@
import { ReactNode, useRef, useState } from 'react'; import { ReactNode, useRef, useState } from 'react';
import styled from 'styled-components'; import { twc } from 'react-twc';
import { css } from 'styled-components';
import { decrementTimeoutMs } from '../../Data/constants'; import { decrementTimeoutMs } from '../../Data/constants';
import { CounterType, Rotation } from '../../Types/Player'; import { CounterType, Rotation } from '../../Types/Player';
import { OutlinedText } from '../Misc/OutlinedText'; import { OutlinedText } from '../Misc/OutlinedText';
import { MAX_TAP_MOVE_DISTANCE, RotationDivProps } from './CommanderDamage';
const ExtraCounterContainer = styled.div` const ExtraCounterContainer = twc.div`
display: flex; flex
justify-content: center; justify-center
align-items: center; items-center
pointer-events: all; pointer-events-all
flex-grow: 1; flex-grow
`; `;
export const StyledExtraCounterButton = styled.button` const ExtraCounterButton = twc.button`
display: flex; flex
justify-content: center; justify-center
align-items: center; items-center
position: relative; relative
flex-grow: 1; flex-grow
border: none; border-none
outline: none; outline-none
cursor: pointer; cursor-pointer
background-color: transparent; bg-transparent
user-select: none; select-none
-webkit-touch-callout: none; h-full
-webkit-tap-highlight-color: transparent; webkit-user-select-none
-moz-user-select: -moz-none; `;
-webkit-user-select: none;
-ms-user-select: none;
height: 100%;
`;
const IconContainer = styled.div<{ const IconContainer = twc.div<RotationDivProps>((props) => [
$rotation: number; 'w-auto',
}>` props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
width: auto; ? 'rotate-[-90deg]'
: '',
]);
${(props) => { const TextContainer = twc.div`
if ( absolute
props.$rotation === Rotation.SideFlipped || top-1/2
props.$rotation === Rotation.Side left-1/2
) { `;
return css`
rotate: -90deg;
`;
}
}}
`;
const TextContainer = styled.div`
position: absolute;
translate: -50%;
top: 50%;
left: 50%;
`;
type ExtraCounterProps = { type ExtraCounterProps = {
Icon: ReactNode; Icon: ReactNode;
@@ -62,6 +47,7 @@ type ExtraCounterProps = {
type: CounterType; type: CounterType;
setCounterTotal: (updatedCounterTotal: number, type: CounterType) => void; setCounterTotal: (updatedCounterTotal: number, type: CounterType) => void;
rotation: number; rotation: number;
isSide: boolean;
playerIndex: number; playerIndex: number;
}; };
@@ -71,14 +57,13 @@ const ExtraCounter = ({
setCounterTotal, setCounterTotal,
type, type,
rotation, rotation,
isSide,
playerIndex, playerIndex,
}: ExtraCounterProps) => { }: ExtraCounterProps) => {
const timeoutRef = useRef<NodeJS.Timeout | undefined>(undefined); const timeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
const [timeoutFinished, setTimeoutFinished] = useState(false); const [timeoutFinished, setTimeoutFinished] = useState(false);
const [hasPressedDown, setHasPressedDown] = useState(false); const [hasPressedDown, setHasPressedDown] = useState(false);
const downPositionRef = useRef({ x: 0, y: 0 });
const isSide =
rotation === Rotation.Side || rotation === Rotation.SideFlipped;
const handleCountChange = (increment: number) => { const handleCountChange = (increment: number) => {
if (!counterTotal) { if (!counterTotal) {
@@ -88,7 +73,8 @@ const ExtraCounter = ({
setCounterTotal(counterTotal + increment, type); setCounterTotal(counterTotal + increment, type);
}; };
const handleDownInput = () => { const handleDownInput = (event: React.PointerEvent<HTMLButtonElement>) => {
downPositionRef.current = { x: event.clientX, y: event.clientY };
setTimeoutFinished(false); setTimeoutFinished(false);
setHasPressedDown(true); setHasPressedDown(true);
timeoutRef.current = setTimeout(() => { timeoutRef.current = setTimeout(() => {
@@ -97,10 +83,23 @@ const ExtraCounter = ({
}, decrementTimeoutMs); }, decrementTimeoutMs);
}; };
const handleUpInput = () => { const handleUpInput = (event: React.PointerEvent<HTMLButtonElement>) => {
if (!(hasPressedDown && !timeoutFinished)) { if (!(hasPressedDown && !timeoutFinished)) {
return; return;
} }
const upPosition = { x: event.clientX, y: event.clientY };
const hasMoved =
Math.abs(upPosition.x - downPositionRef.current.x) >
MAX_TAP_MOVE_DISTANCE ||
Math.abs(upPosition.y - downPositionRef.current.y) >
MAX_TAP_MOVE_DISTANCE;
if (hasMoved) {
return;
}
clearTimeout(timeoutRef.current); clearTimeout(timeoutRef.current);
handleCountChange(1); handleCountChange(1);
setHasPressedDown(false); setHasPressedDown(false);
@@ -118,7 +117,7 @@ const ExtraCounter = ({
return ( return (
<ExtraCounterContainer> <ExtraCounterContainer>
<StyledExtraCounterButton <ExtraCounterButton
onPointerDown={handleDownInput} onPointerDown={handleDownInput}
onPointerUp={handleUpInput} onPointerUp={handleUpInput}
onPointerLeave={handleLeaveInput} onPointerLeave={handleLeaveInput}
@@ -139,7 +138,7 @@ const ExtraCounter = ({
</OutlinedText> </OutlinedText>
</TextContainer> </TextContainer>
</IconContainer> </IconContainer>
</StyledExtraCounterButton> </ExtraCounterButton>
</ExtraCounterContainer> </ExtraCounterContainer>
); );
}; };

View File

@@ -1,89 +1,80 @@
import { useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import styled from 'styled-components'; import { TwcComponentProps, twc } from 'react-twc';
import { css } from 'styled-components';
import { lifeLongPressMultiplier } from '../../Data/constants'; import { lifeLongPressMultiplier } from '../../Data/constants';
import { Player, Rotation } from '../../Types/Player';
import { MAX_TAP_MOVE_DISTANCE } from './CommanderDamage';
import { checkContrast } from '../../Utils/checkContrast';
import { Rotation } from '../../Types/Player'; type RotationButtonProps = TwcComponentProps<'div'> & {
export const StyledLifeCounterButton = styled.button`
width: 100%;
height: 100%;
color: rgba(0, 0, 0, 0.4);
font-weight: 600;
background-color: transparent;
border: none;
outline: none;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
user-select: none;
-webkit-touch-callout: none;
-webkit-tap-highlight-color: transparent;
-moz-user-select: -moz-none;
-webkit-user-select: none;
-ms-user-select: none;
`;
const TextContainer = styled.div<{
$align?: string; $align?: string;
$rotation: number; $rotation?: number;
}>` };
position: relative;
${(props) => { const LifeCounterButtonTwc = twc.button`
if ( h-full
props.$rotation === Rotation.SideFlipped || w-full
props.$rotation === Rotation.Side flex
) { font-semibold
if (props.$align === 'right') { bg-transparent
return css` border-none
rotate: -90deg; outline-none
bottom: 25%; cursor-pointer
top: auto; justify-center
`; items-center
} select-none
return css` webkit-user-select-none
rotate: -90deg; `;
top: 25%;
`;
}
if (props.$align === 'right') { const TextContainer = twc.div<RotationButtonProps>((props) => [
return css` 'relative',
left: 25%; props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
`; ? props.$align === 'right'
} ? '-rotate-90 bottom-1/4 top-auto'
return css` : '-rotate-90 top-1/4'
right: 25%; : 'top-auto',
`; props.$rotation === Rotation.Flipped || props.$rotation === Rotation.Normal
}} ? props.$align === 'right'
`; ? 'left-1/4'
: 'right-1/4'
: '',
]);
type LifeCounterButtonProps = { type LifeCounterButtonProps = {
lifeTotal: number; player: Player;
setLifeTotal: (lifeTotal: number) => void; setLifeTotal: (lifeTotal: number) => void;
rotation: number;
operation: 'add' | 'subtract'; operation: 'add' | 'subtract';
increment: number; increment: number;
}; };
const LifeCounterButton = ({ const LifeCounterButton = ({
lifeTotal, player,
setLifeTotal, setLifeTotal,
rotation,
operation, operation,
increment, increment,
}: LifeCounterButtonProps) => { }: LifeCounterButtonProps) => {
const timeoutRef = useRef<NodeJS.Timeout | undefined>(undefined); const timeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
const [timeoutFinished, setTimeoutFinished] = useState(false); const [timeoutFinished, setTimeoutFinished] = useState(false);
const [hasPressedDown, setHasPressedDown] = useState(false); const [hasPressedDown, setHasPressedDown] = useState(false);
const downPositionRef = useRef({ x: 0, y: 0 });
const [iconColor, setIconColor] = useState<'dark' | 'light'>('dark');
useEffect(() => {
const contrast = checkContrast(player.color, '#00000080');
if (contrast === 'Fail') {
setIconColor('light');
} else {
setIconColor('dark');
}
}, [player.color]);
const handleLifeChange = (increment: number) => { const handleLifeChange = (increment: number) => {
setLifeTotal(lifeTotal + increment); setLifeTotal(player.lifeTotal + increment);
}; };
const handleDownInput = () => { const handleDownInput = (event: React.PointerEvent<HTMLButtonElement>) => {
downPositionRef.current = { x: event.clientX, y: event.clientY };
setTimeoutFinished(false); setTimeoutFinished(false);
setHasPressedDown(true); setHasPressedDown(true);
timeoutRef.current = setTimeout(() => { timeoutRef.current = setTimeout(() => {
@@ -92,10 +83,23 @@ const LifeCounterButton = ({
}, 500); }, 500);
}; };
const handleUpInput = () => { const handleUpInput = (event: React.PointerEvent<HTMLButtonElement>) => {
if (!(hasPressedDown && !timeoutFinished)) { if (!(hasPressedDown && !timeoutFinished)) {
return; return;
} }
const upPosition = { x: event.clientX, y: event.clientY };
const hasMoved =
Math.abs(upPosition.x - downPositionRef.current.x) >
MAX_TAP_MOVE_DISTANCE ||
Math.abs(upPosition.y - downPositionRef.current.y) >
MAX_TAP_MOVE_DISTANCE;
if (hasMoved) {
return;
}
clearTimeout(timeoutRef.current); clearTimeout(timeoutRef.current);
handleLifeChange(operation === 'add' ? 1 : -1); handleLifeChange(operation === 'add' ? 1 : -1);
setHasPressedDown(false); setHasPressedDown(false);
@@ -108,12 +112,13 @@ const LifeCounterButton = ({
}; };
const fontSize = const fontSize =
rotation === Rotation.SideFlipped || rotation === Rotation.Side player.settings.rotation === Rotation.SideFlipped ||
player.settings.rotation === Rotation.Side
? '8vmax' ? '8vmax'
: '12vmin'; : '12vmin';
return ( return (
<StyledLifeCounterButton <LifeCounterButtonTwc
onPointerDown={handleDownInput} onPointerDown={handleDownInput}
onPointerUp={handleUpInput} onPointerUp={handleUpInput}
onPointerLeave={handleLeaveInput} onPointerLeave={handleLeaveInput}
@@ -124,12 +129,15 @@ const LifeCounterButton = ({
aria-label={`${operation === 'add' ? 'Add' : 'Subtract'} life`} aria-label={`${operation === 'add' ? 'Add' : 'Subtract'} life`}
> >
<TextContainer <TextContainer
$rotation={rotation} $rotation={player.settings.rotation}
$align={operation === 'add' ? 'right' : 'left'} $align={operation === 'add' ? 'right' : 'left'}
data-contrast={iconColor}
className="data-[contrast=dark]:text-icons-dark
data-[contrast=light]:text-icons-light"
> >
{operation === 'add' ? '\u002B' : '\u2212'} {operation === 'add' ? '\u002B' : '\u2212'}
</TextContainer> </TextContainer>
</StyledLifeCounterButton> </LifeCounterButtonTwc>
); );
}; };

View File

@@ -1,43 +1,15 @@
import styled, { css } from 'styled-components'; import { twc } from 'react-twc';
import { Skull } from '../../Icons/generated'; import { Skull } from '../../Icons/generated';
import { Rotation } from '../../Types/Player'; import { Rotation } from '../../Types/Player';
import { RotationDivProps } from './CommanderDamage';
export const LoseButton = styled.button<{ $rotation: Rotation }>` const LoseButton = twc.div<RotationDivProps>((props) => [
position: absolute; 'absolute flex-grow border-none outline-none cursor-pointer bg-interface-loseButton-background rounded-lg select-none z-[1] webkit-user-select-none py-2 px-4 ',
flex-grow: 1;
border: none;
outline: none;
cursor: pointer;
top: 25%;
right: 15%;
background-color: #43434380;
border-radius: 8px;
-webkit-touch-callout: none;
-webkit-tap-highlight-color: transparent;
user-select: none;
-moz-user-select: -moz-none;
-webkit-user-select: none;
-ms-user-select: none;
z-index: 1;
${(props) => { props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
if (props.$rotation === Rotation.SideFlipped) { ? `left-[21%]`
return css` : 'top-[21%]',
right: auto; ]);
top: 15%;
left: 27%;
rotate: ${props.$rotation}deg;
`;
} else if (props.$rotation === Rotation.Side) {
return css`
right: auto;
top: 15%;
left: 27%;
rotate: ${props.$rotation - 180}deg;
`;
}
}}
`;
type LoseButtonProps = { type LoseButtonProps = {
onClick: () => void; onClick: () => void;
@@ -45,9 +17,23 @@ type LoseButtonProps = {
}; };
export const LoseGameButton = ({ rotation, onClick }: LoseButtonProps) => { export const LoseGameButton = ({ rotation, onClick }: LoseButtonProps) => {
const calcRotation =
rotation === Rotation.SideFlipped
? rotation
: rotation === Rotation.Side
? rotation - 180
: rotation === Rotation.Flipped
? rotation - 180
: rotation;
return ( return (
<LoseButton $rotation={rotation} onClick={onClick} aria-label={`Lose Game`}> <LoseButton
<Skull size="5vmin" color="black" opacity={0.5} /> $rotation={rotation}
onClick={onClick}
aria-label={`Lose Game`}
style={{ rotate: `${calcRotation}deg` }}
>
<Skull size="8vmin" color="black" opacity={0.5} />
</LoseButton> </LoseButton>
); );
}; };

View File

@@ -1,53 +0,0 @@
import styled from 'styled-components';
import { css } from 'styled-components';
import { Rotation } from '../../Types/Player';
import { Cog } from '../../Icons/generated';
export const StyledSettingsButton = styled.button<{ $rotation: Rotation }>`
position: absolute;
flex-grow: 1;
border: none;
outline: none;
cursor: pointer;
top: 25%;
right: 1vmax;
background-color: transparent;
user-select: none;
-webkit-touch-callout: none;
-webkit-tap-highlight-color: transparent;
-moz-user-select: -moz-none;
-webkit-user-select: none;
-ms-user-select: none;
z-index: 1;
${(props) => {
if (
props.$rotation === Rotation.Side ||
props.$rotation === Rotation.SideFlipped
) {
return css`
right: auto;
top: 1vmax;
left: 27%;
`;
}
}}
`;
type SettingsButtonProps = {
onClick: () => void;
rotation: Rotation;
};
const SettingsButton = ({ onClick, rotation }: SettingsButtonProps) => {
return (
<StyledSettingsButton
onClick={onClick}
$rotation={rotation}
aria-label={`Settings`}
>
<Cog size="5vmin" color="black" opacity="0.3" />
</StyledSettingsButton>
);
};
export default SettingsButton;

View File

@@ -1,27 +1,13 @@
import { twc } from 'react-twc';
import { Player, Rotation } from '../../Types/Player'; import { Player, Rotation } from '../../Types/Player';
import styled from 'styled-components'; import { CommanderDamage, RotationDivProps } from '../Buttons/CommanderDamage';
import { css } from 'styled-components';
import { CommanderDamage } from '../Buttons/CommanderDamage';
const CommanderDamageGrid = styled.div<{ $rotation: number }>` const CommanderDamageGrid = twc.div<RotationDivProps>((props) => [
display: flex; 'flex flex-grow',
flex-direction: row; props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
flex-grow: 1; ? 'flex-col h-full w-auto'
width: 100%; : 'flex-row w-full',
]);
${(props) => {
if (
props.$rotation === Rotation.SideFlipped ||
props.$rotation === Rotation.Side
) {
return css`
flex-direction: column;
height: 100%;
width: auto;
`;
}
}}
`;
type CommanderDamageBarProps = { type CommanderDamageBarProps = {
opponents: Player[]; opponents: Player[];

View File

@@ -1,59 +0,0 @@
import styled from 'styled-components';
import { usePlayers } from '../../Hooks/usePlayers';
import LifeCounter from '../LifeCounter/LifeCounter';
export const CountersWrapper = styled.div`
width: 100%;
height: 100%;
background-color: black;
`;
export const CountersGrid = styled.div<{ $gridTemplateAreas: string }>`
display: grid;
gap: 4px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
height: 100%;
grid-template-areas: ${({ $gridTemplateAreas }) => $gridTemplateAreas};
`;
export const GridItemContainer = styled.div<{
$gridArea: string;
}>`
display: flex;
justify-content: center;
align-items: center;
grid-area: ${(props) => props.$gridArea};
`;
type CountersProps = {
gridAreas: string;
};
const Counters = ({ gridAreas }: CountersProps) => {
const { players } = usePlayers();
return (
<CountersWrapper>
<CountersGrid $gridTemplateAreas={gridAreas}>
{players.map((player) => {
return (
<GridItemContainer
key={player.index}
$gridArea={`player${player.index}`}
>
<LifeCounter
player={player}
opponents={players.filter(
(opponent) => opponent.index !== player.index
)}
/>
</GridItemContainer>
);
})}
</CountersGrid>
</CountersWrapper>
);
};
export default Counters;

View File

@@ -1,8 +1,5 @@
import { CounterType, Player } from '../../Types/Player'; import { twc } from 'react-twc';
import ExtraCounter from '../Buttons/ExtraCounter'; import { usePlayers } from '../../Hooks/usePlayers';
import styled from 'styled-components';
import { css } from 'styled-components';
import { Rotation } from '../../Types/Player';
import { import {
CommanderTax, CommanderTax,
Energy, Energy,
@@ -10,49 +7,25 @@ import {
PartnerTax, PartnerTax,
Poison, Poison,
} from '../../Icons/generated'; } from '../../Icons/generated';
import { usePlayers } from '../../Hooks/usePlayers'; import { CounterType, Player, Rotation } from '../../Types/Player';
import { RotationDivProps } from '../Buttons/CommanderDamage';
import ExtraCounter from '../Buttons/ExtraCounter';
import { useEffect, useState } from 'react';
import { checkContrast } from '../../Utils/checkContrast';
const Container = styled.div<{ $rotation: Rotation }>` const Container = twc.div<RotationDivProps>((props) => [
width: 100%; 'flex',
height: 20vmin; props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
display: flex; ? 'h-full w-[8vmax]'
: 'h-[20vmin] w-full',
]);
${(props) => { export const ExtraCountersGrid = twc.div<RotationDivProps>((props) => [
if ( 'flex absolute flex-row flex-grow pointer-events-none overflow-x-scroll overflow-y-hidden',
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
props.$rotation === Rotation.Side ? 'flex-col-reverse h-full w-auto bottom-auto right-0'
) { : 'w-full bottom-0',
return css` ]);
height: 100%;
width: 9.3vmax;
`;
}
}}
`;
export const ExtraCountersGrid = styled.div<{ $rotation: Rotation }>`
display: flex;
position: absolute;
width: 100%;
flex-direction: row;
flex-grow: 1;
bottom: 0;
pointer-events: none;
${(props) => {
if (
props.$rotation === Rotation.SideFlipped ||
props.$rotation === Rotation.Side
) {
return css`
flex-direction: column-reverse;
height: 100%;
width: auto;
bottom: auto;
`;
}
}}
`;
type ExtraCountersBarProps = { type ExtraCountersBarProps = {
player: Player; player: Player;
@@ -60,6 +33,17 @@ type ExtraCountersBarProps = {
const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => { const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
const { updatePlayer } = usePlayers(); const { updatePlayer } = usePlayers();
const [iconColor, setIconColor] = useState<'dark' | 'light'>('dark');
useEffect(() => {
const contrast = checkContrast(player.color, '#00000080');
if (contrast === 'Fail') {
setIconColor('light');
} else {
setIconColor('dark');
}
}, [player.color]);
const handleCounterChange = ( const handleCounterChange = (
updatedCounterTotal: number, updatedCounterTotal: number,
@@ -122,13 +106,20 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
{useCommanderDamage && ( {useCommanderDamage && (
<ExtraCounter <ExtraCounter
rotation={player.settings.rotation} rotation={player.settings.rotation}
Icon={<CommanderTax size={iconSize} opacity="0.5" color="black" />} Icon={
<CommanderTax
size={iconSize}
data-contrast={iconColor}
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
/>
}
type={CounterType.CommanderTax} type={CounterType.CommanderTax}
counterTotal={ counterTotal={
player.extraCounters?.find( player.extraCounters?.find(
(counter) => counter.type === 'commanderTax' (counter) => counter.type === 'commanderTax'
)?.value )?.value
} }
isSide={player.isSide}
setCounterTotal={handleCounterChange} setCounterTotal={handleCounterChange}
playerIndex={player.index} playerIndex={player.index}
/> />
@@ -136,13 +127,20 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
{Boolean(useCommanderDamage && usePartner) && ( {Boolean(useCommanderDamage && usePartner) && (
<ExtraCounter <ExtraCounter
rotation={player.settings.rotation} rotation={player.settings.rotation}
Icon={<PartnerTax size={iconSize} opacity="0.5" color="black" />} Icon={
<PartnerTax
size={iconSize}
data-contrast={iconColor}
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
/>
}
type={CounterType.PartnerTax} type={CounterType.PartnerTax}
counterTotal={ counterTotal={
player.extraCounters?.find( player.extraCounters?.find(
(counter) => counter.type === 'partnerTax' (counter) => counter.type === 'partnerTax'
)?.value )?.value
} }
isSide={player.isSide}
setCounterTotal={handleCounterChange} setCounterTotal={handleCounterChange}
playerIndex={player.index} playerIndex={player.index}
/> />
@@ -150,12 +148,19 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
{usePoison && ( {usePoison && (
<ExtraCounter <ExtraCounter
rotation={player.settings.rotation} rotation={player.settings.rotation}
Icon={<Poison size={iconSize} opacity="0.5" color="black" />} Icon={
<Poison
size={iconSize}
data-contrast={iconColor}
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
/>
}
type={CounterType.Poison} type={CounterType.Poison}
counterTotal={ counterTotal={
player.extraCounters?.find((counter) => counter.type === 'poison') player.extraCounters?.find((counter) => counter.type === 'poison')
?.value ?.value
} }
isSide={player.isSide}
setCounterTotal={handleCounterChange} setCounterTotal={handleCounterChange}
playerIndex={player.index} playerIndex={player.index}
/> />
@@ -163,12 +168,19 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
{useEnergy && ( {useEnergy && (
<ExtraCounter <ExtraCounter
rotation={player.settings.rotation} rotation={player.settings.rotation}
Icon={<Energy size={iconSize} opacity="0.5" color="black" />} Icon={
<Energy
size={iconSize}
data-contrast={iconColor}
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
/>
}
type={CounterType.Energy} type={CounterType.Energy}
counterTotal={ counterTotal={
player.extraCounters?.find((counter) => counter.type === 'energy') player.extraCounters?.find((counter) => counter.type === 'energy')
?.value ?.value
} }
isSide={player.isSide}
setCounterTotal={handleCounterChange} setCounterTotal={handleCounterChange}
playerIndex={player.index} playerIndex={player.index}
/> />
@@ -176,13 +188,20 @@ const ExtraCountersBar = ({ player }: ExtraCountersBarProps) => {
{useExperience && ( {useExperience && (
<ExtraCounter <ExtraCounter
rotation={player.settings.rotation} rotation={player.settings.rotation}
Icon={<Experience size={iconSize} opacity="0.5" color="black" />} Icon={
<Experience
size={iconSize}
data-contrast={iconColor}
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
/>
}
type={CounterType.Experience} type={CounterType.Experience}
counterTotal={ counterTotal={
player.extraCounters?.find( player.extraCounters?.find(
(counter) => counter.type === 'experience' (counter) => counter.type === 'experience'
)?.value )?.value
} }
isSide={player.isSide}
setCounterTotal={handleCounterChange} setCounterTotal={handleCounterChange}
playerIndex={player.index} playerIndex={player.index}
/> />

View File

@@ -0,0 +1,64 @@
import { useEffect } from 'react';
import { Cross } from '../../Icons/generated';
import { useAnalytics } from '../../Hooks/useAnalytics';
import { Separator } from '../Misc/Separator';
export const Dialog: React.FC<{
id: string;
title?: string;
children: React.ReactNode;
dialogRef: React.MutableRefObject<HTMLDialogElement | null>;
}> = ({ id, title, children, dialogRef }) => {
const analytics = useAnalytics();
useEffect(() => {
if (!dialogRef.current) {
return;
}
dialogRef.current.addEventListener('click', (e) => {
const dialogDimensions = dialogRef.current!.getBoundingClientRect();
if (
(e.clientX < dialogDimensions.left ||
e.clientX > dialogDimensions.right ||
e.clientY < dialogDimensions.top ||
e.clientY > dialogDimensions.bottom) &&
dialogRef.current?.open
) {
analytics.trackEvent(`${id}_outside_clicked`);
dialogRef.current?.close();
}
});
});
return (
<dialog
id={id}
ref={dialogRef}
className="backdrop:bg-background-backdrop border-none backdrop:backdrop-blur-[1px] open:visible invisible bg-transparent overflow-visible my-0 justify-self-center top-[10%]"
>
<button
onClick={() => {
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"
>
<Cross size="16px" className="text-text-primary" />
</button>
<div className="bg-background-default rounded-2xl max-w-[548px] max-h-[80vh] flex flex-col">
{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}
</div>
</div>
</dialog>
);
};

View File

@@ -0,0 +1,106 @@
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';
export const InfoDialog = ({
dialogRef,
}: {
dialogRef: React.MutableRefObject<HTMLDialogElement | null>;
}) => {
const analytics = useAnalytics();
return (
<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
href="https://github.com/Vikeo/LifeTrinket"
target="_blank"
className="text-text-secondary underline"
>
GitHub
</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"
onClick={() => {
analytics.trackEvent('click_bmc');
}}
href="https://www.buymeacoffee.com/vikeo"
target="_blank"
>
<BuyMeCoffee height="1.5rem" width="1.5rem" className="mr-2" />
<Paragraph className="text-xs">Buy me a tea</Paragraph>
</a>
</div>
</Dialog>
);
};

View File

@@ -0,0 +1,257 @@
import { twc } from 'react-twc';
import { useAnalytics } from '../../Hooks/useAnalytics';
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
import { PreStartMode } from '../../Types/Settings';
import { Separator } from '../Misc/Separator';
import { Paragraph } from '../Misc/TextComponents';
import { ToggleButton } from '../Misc/ToggleButton';
import { Dialog } from './Dialog';
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 baseGithubReleasesUrl =
'https://github.com/Vikeo/LifeTrinket/releases/tag/';
export const SettingsDialog = ({
dialogRef,
}: {
dialogRef: React.MutableRefObject<HTMLDialogElement | null>;
}) => {
const { settings, setSettings, isPWA, version } = useGlobalSettings();
const analytics = useAnalytics();
return (
<Dialog id="settings" title="⚙️ Settings ⚙️" dialogRef={dialogRef}>
<div className="flex flex-col mb-2 w-full">
<div className="text-text-primary flex items-center gap-2">
Current version: {version.installedVersion}{' '}
{version.isLatest && (
<span className="text-sm text-text-secondary">(latest)</span>
)}
<div className="text-xs text-text-primary opacity-75">
(
<a
href={baseGithubReleasesUrl + version.installedVersion}
target="_blank"
className="underline"
onClick={() => {
analytics.trackEvent(
`current_change_log_clicked_v${version.installedVersion}`
);
}}
>
Release notes
</a>
)
</div>
</div>
{!version.isLatest && version.remoteVersion && (
<>
<div className="flex gap-2 items-center mt-2">
<Paragraph className="text-text-secondary">
{version.remoteVersion} available!
</Paragraph>
<div className="text-xs text-text-primary opacity-75">
(
<a
href={baseGithubReleasesUrl + version.remoteVersion}
target="_blank"
className="underline"
onClick={() => {
analytics.trackEvent(
`new_change_log_clicked_v${version.remoteVersion}`
);
}}
>
Release notes
</a>
)
</div>
</div>
<button
className="flex justify-center items-center self-start mt-2 bg-primary-main px-3 py-1 rounded-md"
onClick={() => {
{
analytics.trackEvent(`pressed_update`, {
toVersion: version.remoteVersion,
fromVersion: version.installedVersion,
});
window?.location?.reload();
}
}}
>
<span className="text-sm">Update</span>
<span className="text-xs">&nbsp;(reload app)</span>
</button>
</>
)}
</div>
<Separator height="1px" />
<SettingContainer>
<ToggleContainer>
<label>Show Player Menu Cog</label>
<ToggleButton
checked={settings.showPlayerMenuCog}
onChange={() => {
setSettings({
...settings,
showPlayerMenuCog: !settings.showPlayerMenuCog,
});
}}
/>
</ToggleContainer>
<Description>
A cog on the top right of each player's card will be shown if this is
enabled.
</Description>
</SettingContainer>
<SettingContainer>
<ToggleContainer>
<label>Show Start Player</label>
<ToggleButton
checked={settings.showStartingPlayer}
onChange={() => {
setSettings({
...settings,
showStartingPlayer: !settings.showStartingPlayer,
});
}}
/>
</ToggleContainer>
<Description>
On start or reset of game, will pick a random starting player,
according to the <b>Pre-Start mode</b>
</Description>
</SettingContainer>
<SettingContainer>
<div className="flex flex-row justify-between items-center mb-1">
<label htmlFor="pre-start-modes">Player selection style</label>
<select
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"
onChange={(e) => {
setSettings({
...settings,
preStartMode: e.target.value as PreStartMode,
});
}}
disabled={!settings.showStartingPlayer}
>
<option value={PreStartMode.None}>Instant</option>
<option value={PreStartMode.RandomKing}>Royal Shuffle</option>
<option value={PreStartMode.FingerGame}>Touch Roulette</option>
<option value={PreStartMode.Trivia}>Group Trivia</option>
</select>
</div>
<div className="text-xs text-left text-text-secondary">
Different ways to determine the starting player before the game
starts.
</div>
{settings.preStartMode === PreStartMode.None && (
<div className="text-xs text-left text-text-secondary mt-1">
<span className="text-text-primary">Instant:</span> A random
starting player will simply be shown on start.
</div>
)}
{settings.preStartMode === PreStartMode.RandomKing && (
<div className="text-xs text-left text-text-secondary mt-1">
<span className="text-text-primary">Royal Shuffle:</span> 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.
</div>
)}
{settings.preStartMode === PreStartMode.FingerGame && (
<div className="text-xs text-left text-text-secondary mt-1">
<span className="text-text-primary">Touch Roulette:</span> All
players put a finger on the screen, one will be chosen at random.
</div>
)}
{settings.preStartMode === PreStartMode.Trivia && (
<div className="text-xs text-left text-text-secondary mt-1">
<span className="text-text-primary">Group Trivia:</span> A random
"who is the most ..." type question will be shown, the group decides
which player fits the question best.
</div>
)}
</SettingContainer>
<SettingContainer>
<ToggleContainer>
<label>Keep Awake</label>
<ToggleButton
checked={settings.keepAwake}
onChange={() => {
setSettings({
...settings,
keepAwake: !settings.keepAwake,
});
}}
/>
</ToggleContainer>
<Description>
Will prevent device from going to sleep while this app is open if this
is enabled.
</Description>
</SettingContainer>
<SettingContainer>
<ToggleContainer>
<label>
Fullscreen on start <span className="text-xs">(Android only)</span>
</label>
<ToggleButton
checked={settings.goFullscreenOnStart}
onChange={() => {
setSettings({
...settings,
goFullscreenOnStart: !settings.goFullscreenOnStart,
});
}}
/>
</ToggleContainer>
<Description>
Will enter fullscreen mode when starting a game if this is enabled.
</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"
onClick={() => {
analytics.trackEvent('settings_save_clicked');
dialogRef.current?.close();
}}
>
<span className="text-sm">Save and Close</span>
</button>
</div>
{!isPWA && (
<>
<Separator height="1px" />
<SettingContainer>
<ToggleContainer>
<Paragraph>
<b>Tip:</b> You can{' '}
<b>add this webapp to your home page on iOS</b> or{' '}
<b>install it on Android</b> to have it act just like a normal
app!
</Paragraph>
</ToggleContainer>
<Description className="mt-1">
If you do, this app will work offline and the toolbar will be
automatically hidden.
</Description>
</SettingContainer>
</>
)}
<Separator height="1px" />
</Dialog>
);
};

View File

@@ -1,115 +1,43 @@
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import styled, { css, keyframes } from 'styled-components'; import { twc } from 'react-twc';
import { Player, Rotation } from '../../Types/Player'; import { Player, Rotation } from '../../Types/Player';
import {
RotationDivProps,
RotationSpanProps,
} from '../Buttons/CommanderDamage';
import LifeCounterButton from '../Buttons/LifeCounterButton'; import LifeCounterButton from '../Buttons/LifeCounterButton';
import { OutlinedText } from '../Misc/OutlinedText'; import { OutlinedText } from '../Misc/OutlinedText';
const LifeCountainer = styled.div<{ const LifeContainer = twc.div<RotationDivProps>((props) => [
$rotation: Rotation; 'flex flex-grow relative w-full h-full justify-between items-center',
}>` props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
position: relative; ? 'flex-col-reverse'
display: flex; : 'flex-row',
flex-direction: row; ]);
flex-grow: 1;
width: 100%;
height: 100%;
justify-content: space-between;
align-items: center;
${(props) => { const LifeCounterTextContainer = twc.div<RotationDivProps>((props) => [
if ( 'absolute m-0 p-0 pointer-events-none select-none webkit-user-select-none',
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
props.$rotation === Rotation.Side ? 'w-full h-2/3'
) { : 'w-2/3 h-full',
return css` ]);
flex-direction: column-reverse;
`; const TextWrapper = twc.div`
} flex
}} absolute
justify-center
items-center
w-full
h-full
z-[-1]
`; `;
const LifeCounterTextContainer = styled.div<{ const RecentDifference = twc.div<RotationSpanProps>((props) => [
$rotation: Rotation; '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',
}>` props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
position: absolute; ? 'top-1/3 translate-x-1/4 translate-y-1/2 rotate-[270deg]'
width: 60%; : 'top-1/4 left-[50%] -translate-x-1/2',
height: 100%; ]);
margin: 0;
padding: 0;
pointer-events: none;
-webkit-touch-callout: none;
-webkit-tap-highlight-color: transparent;
user-select: none;
-moz-user-select: -moz-none;
-webkit-user-select: none;
-ms-user-select: none;
${(props) => {
if (
props.$rotation === Rotation.SideFlipped ||
props.$rotation === Rotation.Side
) {
return css`
width: 100%;
height: 60%;
`;
}
}}
`;
const TextWrapper = styled.div`
display: flex;
position: absolute;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
z-index: -1;
`;
const fadeOut = keyframes`
0% {
opacity: 1;
}
33% {
opacity: 0.6;
}
100% {
opacity: 0;
}
`;
export const RecentDifference = styled.span<{ $rotation: Rotation }>`
position: absolute;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
min-width: 15vmin;
text-shadow: none;
text-align: center;
background-color: rgba(255, 255, 255, 0.6);
font-variant-numeric: tabular-nums;
border-radius: 10vmin;
padding: 5px 10px;
font-size: 8vmin;
color: #333333;
animation: ${fadeOut} 3s 1s ease-out forwards;
${(props) => {
if (
props.$rotation === Rotation.SideFlipped ||
props.$rotation === Rotation.Side
) {
return css`
top: 27%;
left: 30%;
transform: translate(-50%, -50%);
rotate: 270deg;
`;
}
}}
`;
type HealthProps = { type HealthProps = {
player: Player; player: Player;
@@ -121,28 +49,13 @@ type HealthProps = {
const Health = ({ const Health = ({
player, player,
rotation,
handleLifeChange, handleLifeChange,
differenceKey, differenceKey,
recentDifference, recentDifference,
}: HealthProps) => { }: HealthProps) => {
const [showStartingPlayer, setShowStartingPlayer] = useState(
localStorage.getItem('playing') === 'true'
);
const [fontSize, setFontSize] = useState(16); const [fontSize, setFontSize] = useState(16);
const textContainerRef = useRef<HTMLDivElement | null>(null); const textContainerRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
if (!showStartingPlayer) {
const playingTimer = setTimeout(() => {
localStorage.setItem('playing', 'true');
setShowStartingPlayer(localStorage.getItem('playing') === 'true');
}, 3_000);
return () => clearTimeout(playingTimer);
}
}, [showStartingPlayer]);
useEffect(() => { useEffect(() => {
if (!textContainerRef.current) { if (!textContainerRef.current) {
return; return;
@@ -151,7 +64,6 @@ const Health = ({
const textContainer = textContainerRef.current; const textContainer = textContainerRef.current;
const resizeObserver = new ResizeObserver(() => { const resizeObserver = new ResizeObserver(() => {
const calcFontSize = calculateFontSize(textContainer); const calcFontSize = calculateFontSize(textContainer);
console.log(calcFontSize);
setFontSize(calcFontSize); setFontSize(calcFontSize);
}); });
@@ -172,12 +84,13 @@ const Health = ({
}, [textContainerRef]); }, [textContainerRef]);
const calculateFontSize = (container: HTMLDivElement) => { const calculateFontSize = (container: HTMLDivElement) => {
const isSide = const widthRatio = player.isSide
rotation === Rotation.SideFlipped || rotation === Rotation.Side; ? container.clientHeight
: container.clientWidth;
const widthRatio = isSide ? container.clientHeight : container.clientWidth; const heightRatio = player.isSide
? container.clientWidth
const heightRatio = isSide ? container.clientWidth : container.clientHeight; : container.clientHeight;
const minRatio = Math.min(widthRatio, heightRatio); const minRatio = Math.min(widthRatio, heightRatio);
@@ -189,11 +102,10 @@ const Health = ({
}; };
return ( return (
<LifeCountainer $rotation={player.settings.rotation}> <LifeContainer $rotation={player.settings.rotation}>
<LifeCounterButton <LifeCounterButton
lifeTotal={player.lifeTotal} player={player}
setLifeTotal={handleLifeChange} setLifeTotal={handleLifeChange}
rotation={player.settings.rotation}
operation="subtract" operation="subtract"
increment={-1} increment={-1}
/> />
@@ -221,13 +133,12 @@ const Health = ({
</LifeCounterTextContainer> </LifeCounterTextContainer>
</TextWrapper> </TextWrapper>
<LifeCounterButton <LifeCounterButton
lifeTotal={player.lifeTotal} player={player}
setLifeTotal={handleLifeChange} setLifeTotal={handleLifeChange}
rotation={player.settings.rotation}
operation="add" operation="add"
increment={1} increment={1}
/> />
</LifeCountainer> </LifeContainer>
); );
}; };

View File

@@ -1,137 +1,80 @@
import { useEffect, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import styled, { css, keyframes } from 'styled-components'; import { useSwipeable } from 'react-swipeable';
import { theme } from '../../Data/theme'; import { twc } from 'react-twc';
import { useAnalytics } from '../../Hooks/useAnalytics';
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
import { usePlayers } from '../../Hooks/usePlayers';
import { Cog } from '../../Icons/generated';
import { Player, Rotation } from '../../Types/Player'; import { Player, Rotation } from '../../Types/Player';
import { checkContrast } from '../../Utils/checkContrast';
import {
RotationButtonProps,
RotationDivProps,
} from '../Buttons/CommanderDamage';
import { LoseGameButton } from '../Buttons/LoseButton'; import { LoseGameButton } from '../Buttons/LoseButton';
import SettingsButton from '../Buttons/SettingsButton';
import CommanderDamageBar from '../Counters/CommanderDamageBar'; import CommanderDamageBar from '../Counters/CommanderDamageBar';
import ExtraCountersBar from '../Counters/ExtraCountersBar'; import ExtraCountersBar from '../Counters/ExtraCountersBar';
import PlayerMenu from '../Player/PlayerMenu'; import PlayerMenu from '../Players/PlayerMenu';
import { StartingPlayerCard } from '../PreStartGame/StartingPlayerCard';
import Health from './Health'; import Health from './Health';
import { usePlayers } from '../../Hooks/usePlayers';
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
const LifeCounterContentWrapper = styled.div<{ const SettingsButtonTwc = twc.button<RotationButtonProps>((props) => [
$backgroundColor: string; 'absolute flex-grow border-none outline-none cursor-pointer bg-transparent z-[1] select-none webkit-user-select-none',
}>` props.$rotation === Rotation.Side || props.$rotation === Rotation.SideFlipped
position: relative; ? `right-auto top-[1vmax] left-[27%]`
display: flex; : 'top-1/4 right-[1vmax]',
flex-grow: 1; ]);
flex-direction: column;
align-items: center;
height: 100%;
width: 100%;
background-color: ${(props) => props.$backgroundColor || 'antiquewhite'};
@media (orientation: landscape) {
max-width: 100vmax;
max-height: 100vmin;
}
overflow: hidden; type SettingsButtonProps = {
`; onClick: () => void;
rotation: Rotation;
color: string;
};
const LifeCounterWrapper = styled.div<{ const SettingsButton = ({ onClick, rotation, color }: SettingsButtonProps) => {
$rotation: Rotation; const [iconColor, setIconColor] = useState<'dark' | 'light'>('dark');
}>`
position: relative;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: 100%;
z-index: 1; useEffect(() => {
const contrast = checkContrast(color, '#00000080');
${(props) => { if (contrast === 'Fail') {
if ( setIconColor('light');
props.$rotation === Rotation.SideFlipped || } else {
props.$rotation === Rotation.Side setIconColor('dark');
) {
return css`
flex-direction: row;
rotate: ${props.$rotation - 90}deg;
`;
} }
}, [color]);
return css` return (
flex-direction: column; <SettingsButtonTwc
rotate: ${props.$rotation}deg; onClick={onClick}
`; $rotation={rotation}
}} aria-label={`Settings`}
`; >
<Cog
size="5vmin"
data-contrast={iconColor}
className="data-[contrast=dark]:text-icons-dark data-[contrast=light]:text-icons-light"
/>
</SettingsButtonTwc>
);
};
const PlayerNoticeWrapper = styled.div<{ const LifeCounterContentWrapper = twc.div`
$rotation: Rotation; relative flex flex-grow flex-col items-center w-full h-full overflow-hidden`;
$backgroundColor: string;
}>`
z-index: 1;
display: flex;
position: absolute;
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
background: ${(props) => props.$backgroundColor};
pointer-events: none;
-webkit-touch-callout: none;
-webkit-tap-highlight-color: transparent;
user-select: none;
-moz-user-select: -moz-none;
-webkit-user-select: none;
-ms-user-select: none;
${(props) => { const LifeCounterWrapper = twc.div<RotationDivProps>((props) => [
if ( 'relative flex items-center w-full h-full z-[1]',
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
props.$rotation === Rotation.Side ? `flex-row`
) { : `flex-col`,
return css` ]);
rotate: ${props.$rotation - 90}deg;
`;
}
}}
`;
const DynamicText = styled.div<{ $rotation: Rotation }>` const PlayerLostWrapper = twc.div<RotationDivProps>((props) => [
font-size: 8vmin; 'z-[1] flex absolute w-full h-full justify-center items-center pointer-events-none select-none webkit-user-select-none bg-lifeCounter-lostWrapper opacity-75',
${(props) => { props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
if ( ? `rotate-[${props.$rotation - 90}deg]`
props.$rotation === Rotation.SideFlipped || : '',
props.$rotation === Rotation.Side ]);
) {
return css`
rotate: ${props.$rotation - 180}deg;
`;
}
}}
`;
const fadeOut = keyframes`
0% {
opacity: 1;
}
33% {
opacity: 0.6;
}
100% {
opacity: 0;
}
`;
export const RecentDifference = styled.span`
position: absolute;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
text-shadow: none;
background-color: rgba(255, 255, 255, 0.6);
font-variant-numeric: tabular-nums;
border-radius: 50%;
padding: 5px 10px;
font-size: 8vmin;
color: #333333;
animation: ${fadeOut} 3s 1s ease-out forwards;
`;
const hasCommanderDamageReached21 = (player: Player) => { const hasCommanderDamageReached21 = (player: Player) => {
const commanderDamageTotals = player.commanderDamage.map( const commanderDamageTotals = player.commanderDamage.map(
@@ -163,36 +106,81 @@ const playerCanLose = (player: Player) => {
type LifeCounterProps = { type LifeCounterProps = {
player: Player; player: Player;
opponents: Player[]; opponents: Player[];
isStartingPlayer?: boolean;
}; };
const RECENT_DIFFERENCE_TTL = 3_000;
const LifeCounter = ({ player, opponents }: LifeCounterProps) => { const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
const { updatePlayer, updateLifeTotal } = usePlayers(); const { updatePlayer, updateLifeTotal } = usePlayers();
const { settings } = useGlobalSettings(); const { settings, playing } = useGlobalSettings();
const recentDifferenceTimerRef = useRef<NodeJS.Timeout | undefined>(
undefined
);
const [showPlayerMenu, setShowPlayerMenu] = useState(false); const [showPlayerMenu, setShowPlayerMenu] = useState(false);
const [recentDifference, setRecentDifference] = useState(0); const [recentDifference, setRecentDifference] = useState(0);
const [differenceKey, setDifferenceKey] = useState(Date.now()); const [differenceKey, setDifferenceKey] = useState(Date.now());
const [isLandscape, setIsLandscape] = useState(false);
const calcRot = player.isSide
? player.settings.rotation - 180
: player.settings.rotation;
const rotationAngle = isLandscape ? calcRot : calcRot + 90;
const handlers = useSwipeable({
trackMouse: true,
onSwipedDown: (e) => {
e.event.stopPropagation();
analytics.trackEvent('open_player_menu_swipe');
setShowPlayerMenu(true);
},
onSwipedUp: (e) => {
e.event.stopPropagation();
analytics.trackEvent('close_player_menu_swipe');
setShowPlayerMenu(false);
},
swipeDuration: 500,
onSwiping: (e) => e.event.stopPropagation(),
rotationAngle,
});
const analytics = useAnalytics();
useEffect(() => { useEffect(() => {
const timer = setTimeout(() => { if (recentDifference === 0) {
setRecentDifference(0); clearTimeout(recentDifferenceTimerRef.current);
}, 3_000); return;
}
return () => clearTimeout(timer); recentDifferenceTimerRef.current = setTimeout(() => {
analytics.trackEvent('life_changed', {
lifeChangedAmount: recentDifference,
});
setRecentDifference(0);
}, RECENT_DIFFERENCE_TTL);
return () => {
clearTimeout(recentDifferenceTimerRef.current);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [recentDifference]); }, [recentDifference]);
useEffect(() => { useEffect(() => {
if (player.showStartingPlayer) { const resizeObserver = new ResizeObserver(() => {
const playingTimer = setTimeout(() => { if (document.body.clientWidth > document.body.clientHeight)
localStorage.setItem('playing', 'true'); setIsLandscape(true);
player.showStartingPlayer = false; else setIsLandscape(false);
updatePlayer(player); return () => {
}, 3_000); // Cleanup: disconnect the ResizeObserver when the component unmounts.
resizeObserver.disconnect();
};
});
return () => clearTimeout(playingTimer); resizeObserver.observe(document.body);
}
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [player.showStartingPlayer]); }, [document.body.clientHeight, document.body.clientWidth]);
player.settings.rotation === Rotation.SideFlipped || player.settings.rotation === Rotation.SideFlipped ||
player.settings.rotation === Rotation.Side; player.settings.rotation === Rotation.Side;
@@ -208,40 +196,46 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
updatePlayer(updatedPlayer); updatePlayer(updatedPlayer);
}; };
const calcRotation =
player.settings.rotation === Rotation.SideFlipped ||
player.settings.rotation === Rotation.Side
? player.settings.rotation - 90
: player.settings.rotation;
const amountOfPlayers = opponents.length + 1;
return ( return (
<LifeCounterContentWrapper $backgroundColor={player.color}> <LifeCounterContentWrapper style={{ background: player.color }}>
<LifeCounterWrapper $rotation={player.settings.rotation}> <LifeCounterWrapper
{settings.showStartingPlayer && $rotation={player.settings.rotation}
player.isStartingPlayer && style={{ rotate: `${calcRotation}deg` }}
player.showStartingPlayer && ( {...handlers}
<PlayerNoticeWrapper >
$rotation={player.settings.rotation} {amountOfPlayers > 1 &&
$backgroundColor={theme.palette.primary.main} !playing &&
> settings.showStartingPlayer &&
<DynamicText $rotation={player.settings.rotation}> player.isStartingPlayer && <StartingPlayerCard player={player} />}
You start!
</DynamicText>
</PlayerNoticeWrapper>
)}
{player.hasLost && ( {player.hasLost && (
<PlayerNoticeWrapper <PlayerLostWrapper $rotation={player.settings.rotation} />
$rotation={player.settings.rotation}
$backgroundColor={'#00000070'}
/>
)} )}
<CommanderDamageBar <CommanderDamageBar
opponents={opponents} opponents={opponents}
player={player} player={player}
key={player.index} key={player.index}
handleLifeChange={handleLifeChange} handleLifeChange={handleLifeChange}
/> />
<SettingsButton {settings.showPlayerMenuCog && (
onClick={() => { <SettingsButton
setShowPlayerMenu(!showPlayerMenu); onClick={() => {
}} analytics.trackEvent('open_player_menu_button');
rotation={player.settings.rotation} setShowPlayerMenu(!showPlayerMenu);
/> }}
rotation={player.settings.rotation}
color={player.color}
/>
)}
{playerCanLose(player) && ( {playerCanLose(player) && (
<LoseGameButton <LoseGameButton
rotation={player.settings.rotation} rotation={player.settings.rotation}
@@ -256,11 +250,13 @@ const LifeCounter = ({ player, opponents }: LifeCounterProps) => {
handleLifeChange={handleLifeChange} handleLifeChange={handleLifeChange}
/> />
<ExtraCountersBar player={player} /> <ExtraCountersBar player={player} />
</LifeCounterWrapper>
{showPlayerMenu && ( <PlayerMenu
<PlayerMenu player={player} setShowPlayerMenu={setShowPlayerMenu} /> isShown={showPlayerMenu}
)} player={player}
setShowPlayerMenu={setShowPlayerMenu}
/>
</LifeCounterWrapper>
</LifeCounterContentWrapper> </LifeCounterContentWrapper>
); );
}; };

View File

@@ -1,47 +1,37 @@
import styled from 'styled-components'; import { twc } from 'react-twc';
import { useGlobalSettings } from '../Hooks/useGlobalSettings'; import { useGlobalSettings } from '../Hooks/useGlobalSettings';
import StartMenu from './Views/StartMenu/StartMenu';
import { Play } from './Views/Play'; import { Play } from './Views/Play';
import StartMenu from './Views/StartMenu/StartMenu';
const StartWrapper = styled.div` const StartWrapper = twc.div`max-w-fit max-h-fit`;
max-width: fit-content;
max-height: fit-content;
`;
const PlayWrapper = styled.div` const PlayWrapper = twc.div`relative z-0 max-w-fit max-h-fit portrait:rotate-90`;
position: relative;
z-index: 0;
max-width: fit-content;
max-height: fit-content;
@media (orientation: portrait) {
rotate: 90deg;
}
`;
const EmergencyResetButton = styled.button` const EmergencyResetButton = () => {
width: 100vmax; const { goToStart } = useGlobalSettings();
height: 100vmin;
font-size: 4vmax; const EmergencyResetButton = twc.button`w-[100dvmax] h-[100dvmin] absolute top-0 z-[-1] bg-background-default`;
position: absolute; const Paragraph = twc.p`text-[4vmax] text-text-secondary`;
top: 0;
z-index: -1; return (
background-color: #4e6815; <EmergencyResetButton onClick={goToStart}>
`; <Paragraph>If you can see this, something is wrong.</Paragraph>
<Paragraph>Press screen to go to start.</Paragraph>
<br />
<Paragraph>If the issue persists, please inform me.</Paragraph>
</EmergencyResetButton>
);
};
export const LifeTrinket = () => { export const LifeTrinket = () => {
const { showPlay, goToStart, initialGameSettings } = useGlobalSettings(); const { showPlay, initialGameSettings } = useGlobalSettings();
return ( return (
<> <>
{showPlay && initialGameSettings ? ( {showPlay && initialGameSettings ? (
<PlayWrapper> <PlayWrapper>
<Play /> <Play />
<EmergencyResetButton onClick={goToStart}> <EmergencyResetButton />
<p>If you can see this, something is wrong.</p>
<p>Press screen to go to start.</p>
<br />
<p>If the issue persists, please inform me.</p>
</EmergencyResetButton>
</PlayWrapper> </PlayWrapper>
) : ( ) : (
<StartWrapper> <StartWrapper>

View File

@@ -0,0 +1,31 @@
export const IconCheckbox = ({
name,
icon,
checkedIcon,
checked,
onChange,
className,
}: {
name: string;
icon: JSX.Element;
checkedIcon: JSX.Element;
checked: boolean;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
className?: string;
}) => {
return (
<div className={className}>
<label>
<input
name={name}
type="checkbox"
checked={checked}
onChange={onChange}
className="sr-only peer"
/>
<div className="peer-checked:hidden block">{icon}</div>
<div className="peer-checked:block hidden">{checkedIcon}</div>
</label>
</div>
);
};

View File

@@ -1,96 +0,0 @@
import { Modal } from '@mui/material';
import { theme } from '../../Data/theme';
import styled from 'styled-components';
export const ModalWrapper = styled.div`
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80vw;
height: 85vh;
background-color: ${theme.palette.background.default};
padding: 1rem;
overflow: scroll;
border-radius: 1rem;
color: ${theme.palette.text.primary};
border: none;
`;
type InfoModalProps = {
isOpen: boolean;
closeModal: () => void;
};
export const InfoModal = ({ isOpen, closeModal }: InfoModalProps) => {
return (
<Modal open={isOpen} onClose={closeModal}>
<ModalWrapper>
<div>
<h2 style={{ textAlign: 'center' }}>📋 Usage Guide</h2>
<p>
There are some controls that you might not know about, so here's a
short list of them.
</p>
<h3>Life counter</h3>
<ul>
<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>Commander damage and other counters</h3>
<ul>
<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>Other</h3>
<p>
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.
</p>
<p>
Tap on the button to mark that player as lost, dimming their player
card.
</p>
</div>
<br />
<div
style={{
textAlign: 'center',
marginTop: '1rem',
}}
>
Visit my
<a
href="https://github.com/Vikeo/LifeTrinket"
target="_blank"
style={{
textDecoration: 'none',
color: theme.palette.primary.light,
}}
>
{' '}
GitHub{' '}
</a>
for more info about this web app.
</div>
</ModalWrapper>
</Modal>
);
};

View File

@@ -1,58 +1,30 @@
import styled, { css } from 'styled-components';
import { theme } from '../../Data/theme';
import { Rotation } from '../../Types/Player'; import { Rotation } from '../../Types/Player';
const Container = styled.div` import { twc } from 'react-twc';
display: flex; //TODO Create provider for this
position: relative; import tailwindConfig from './../../../tailwind.config';
width: 100%; import resolveConfig from 'tailwindcss/resolveConfig';
height: 100%;
align-items: center;
justify-content: center;
`;
const CenteredText = styled.div<{ const fullConfig = resolveConfig(tailwindConfig);
strokeWidth?: string;
strokeColor?: string;
fillColor?: string;
fontSize?: string;
fontWeight?: string;
$rotation?: Rotation;
}>`
position: absolute;
font-weight: ${(props) => props.fontWeight || ''};
font-variant-numeric: tabular-nums;
user-select: none;
-webkit-touch-callout: none;
-webkit-tap-highlight-color: transparent;
-moz-user-select: -moz-none;
-webkit-user-select: none;
-ms-user-select: none;
color: ${(props) => props.fillColor || theme.palette.common.black}; const Container = twc.div`
font-size: ${(props) => props.fontSize || '6vmin'}; flex
-webkit-text-stroke: ${(props) => props.strokeWidth || '1vmin'}${(props) => props.strokeColor || theme.palette.common.white}; relative
-webkit-text-fill-color: ${(props) => w-full
props.fillColor || theme.palette.common.black}; h-full
items-center
justify-center
`;
${(props) => { const CenteredText = twc.div`absolute select-none text-common-black text-[6vmin] stroke-common-white
if ( webkit-user-select-none tabular-nums`;
props.$rotation === Rotation.SideFlipped ||
props.$rotation === Rotation.Side
) {
return css`
rotate: 270deg;
`;
}
}}
`;
const CenteredTextOutline = styled.span` const CenteredTextOutline = twc.span`
position: absolute; absolute
left: 0; left-0
-webkit-text-stroke: 0; stroke-none
pointer-events: none; pointer-events-none
`; `;
type OutlinedTextProps = { type OutlinedTextProps = {
children?: React.ReactNode; children?: React.ReactNode;
@@ -73,18 +45,33 @@ export const OutlinedText: React.FC<OutlinedTextProps> = ({
fillColor, fillColor,
rotation, rotation,
}) => { }) => {
const calcRotation =
rotation === Rotation.Side
? rotation - 180
: rotation === Rotation.SideFlipped
? rotation
: 0;
return ( return (
<Container> <Container>
<CenteredText <CenteredText
fontSize={fontSize} style={{
fontWeight={fontWeight} fontSize,
strokeWidth={strokeWidth} fontWeight,
strokeColor={strokeColor} strokeWidth: strokeWidth || '1vmin',
fillColor={fillColor} color: fillColor || fullConfig.theme.colors.common.black,
$rotation={rotation} WebkitTextStroke: `${strokeWidth || '1vmin'} ${
strokeColor || fullConfig.theme.colors.common.white
}`,
WebkitTextFillColor:
fillColor || fullConfig.theme.colors.common.black,
rotate: `${calcRotation}deg`,
}}
> >
{children} {children}
<CenteredTextOutline aria-hidden>{children}</CenteredTextOutline> <CenteredTextOutline aria-hidden style={{ WebkitTextStroke: 0 }}>
{children}
</CenteredTextOutline>
</CenteredText> </CenteredText>
</Container> </Container>
); );

View File

@@ -1,13 +1,3 @@
import styled from 'styled-components';
import { Spacer } from './Spacer';
const SeparatorContainer = styled.div<{ width?: string; height?: string }>`
width: ${(props) => props.width};
height: ${(props) => props.height};
background-color: #00000025;
border-radius: 50px;
`;
export const Separator = ({ export const Separator = ({
width = '100%', width = '100%',
height = '100%', height = '100%',
@@ -16,10 +6,9 @@ export const Separator = ({
height?: string; height?: string;
}) => { }) => {
return ( return (
<> <div
<Spacer height="0.5rem" /> className={`bg-common-white bg-opacity-30 rounded-full mt-2 mb-2`}
<SeparatorContainer width={width} height={height} /> style={{ width, height }}
<Spacer height="0.5rem" /> />
</>
); );
}; };

View File

@@ -1,133 +0,0 @@
import { Button, FormLabel, Modal, Switch } from '@mui/material';
import { ModalWrapper } from './InfoModal';
import styled from 'styled-components';
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
import { theme } from '../../Data/theme';
import { Separator } from './Separator';
import { Paragraph } from './TextComponents';
const SettingContainer = styled.div`
width: 100%;
display: flex;
flex-direction: column;
`;
const ToggleContainer = styled.div`
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
`;
const Container = styled.div`
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
`;
const Description = styled.p`
margin-top: -0.25rem;
margin-right: 3.5rem;
font-size: 0.8rem;
text-align: left;
color: ${theme.palette.text.secondary};
`;
type SettingsModalProps = {
isOpen: boolean;
closeModal: () => void;
};
export const SettingsModal = ({ isOpen, closeModal }: SettingsModalProps) => {
const { settings, setSettings, isPWA } = useGlobalSettings();
return (
<Modal open={isOpen} onClose={closeModal}>
<ModalWrapper>
<Container>
<h2 style={{ textAlign: 'center' }}> Settings </h2>
<SettingContainer>
<ToggleContainer>
<FormLabel>Show Start Player</FormLabel>
<Switch
checked={settings.showStartingPlayer}
onChange={() => {
setSettings({
...settings,
showStartingPlayer: !settings.showStartingPlayer,
});
}}
/>
</ToggleContainer>
<Description>
On start or reset of game, will pick a random player who will
start first if this is enabled.
</Description>
</SettingContainer>
<SettingContainer>
<ToggleContainer>
<FormLabel>Keep Awake</FormLabel>
<Switch
checked={settings.keepAwake}
onChange={() => {
setSettings({ ...settings, keepAwake: !settings.keepAwake });
}}
/>
</ToggleContainer>
<Description>
Will prevent device from going to sleep while this app is open if
this is enabled.
</Description>
</SettingContainer>
<SettingContainer>
<ToggleContainer>
<FormLabel>Go fullscreen on start (Android only)</FormLabel>
<Switch
checked={settings.goFullscreenOnStart}
onChange={() => {
setSettings({
...settings,
goFullscreenOnStart: !settings.goFullscreenOnStart,
});
}}
/>
</ToggleContainer>
<Description>
Will enter fullscreen mode when starting a game if this is
enabled.
</Description>
</SettingContainer>
{!isPWA && (
<>
<Separator height="2px" />
<SettingContainer>
<ToggleContainer>
<Paragraph>
<b>Tip:</b> You can{' '}
<b>add this webapp to your home page on iOS</b> or{' '}
<b>install it on Android</b> to have it act just like a
normal app!
</Paragraph>
</ToggleContainer>
<Description>
If you do, this app will work offline and the toolbar will be
automatically hidden.
</Description>
</SettingContainer>
</>
)}
<Separator height="2px" />
<SettingContainer>
<Paragraph>Version: 0.4.0</Paragraph>
</SettingContainer>
<Separator height="2px" />
<Button variant="contained" onClick={closeModal}>
Save and Close
</Button>
</Container>
</ModalWrapper>
</Modal>
);
};

View File

@@ -1,6 +0,0 @@
import styled from 'styled-components';
export const Spacer = styled.div<{ width?: string; height?: string }>`
width: ${(props) => props.width};
height: ${(props) => props.height};
`;

View File

@@ -1,128 +0,0 @@
import { Button, Drawer } from '@mui/material';
import { useState } from 'react';
import styled from 'styled-components';
import { theme } from '../../Data/theme';
import { BuyMeCoffee, KoFi } from '../../Icons/generated/Support';
import { Paragraph } from './TextComponents';
import LittleGuy from '../../Icons/generated/LittleGuy';
import { useAnalytics } from '../../Hooks/useAnalytics';
// import { ButtonBase } from '@mui/material';
const SupportContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 1rem;
margin: 16px 0;
`;
const SupportButton = styled.button`
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
border: none;
background-color: transparent;
cursor: pointer;
padding: 0;
margin: 0;
background-color: ${theme.palette.primary.main};
border-radius: 4px;
margin: 0 1rem;
padding: 0 1rem;
transition: background-color 0.2s ease-in-out;
box-shadow: 1px 2px 4px 0px rgba(0, 0, 0, 0.3);
&:hover {
background-color: ${theme.palette.primary.dark};
}
`;
export const SupportMe = () => {
const analytics = useAnalytics();
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const handleOpenBuyMeCoffee = () => {
analytics.trackEvent('click_bmc');
window.open('https://www.buymeacoffee.com/vikeo');
};
const handleOpenKoFi = () => {
analytics.trackEvent('click_kofi');
window.open('https://ko-fi.com/vikeo');
};
const toggleDrawer =
(open: boolean) => (event: React.KeyboardEvent | React.MouseEvent) => {
analytics.trackEvent('toggle_support_drawer');
if (
event.type === 'keydown' &&
((event as React.KeyboardEvent).key === 'Tab' ||
(event as React.KeyboardEvent).key === 'Shift')
) {
return;
}
setIsDrawerOpen(open);
};
return (
<>
<Button
onClick={toggleDrawer(true)}
size="small"
variant="contained"
style={{
position: 'absolute',
top: '1rem',
right: '1rem',
fontSize: '0.5rem',
}}
>
Nourish <br /> this guy {'->'}
</Button>
<LittleGuy
height={'4rem'}
width={'2.5rem'}
style={{
pointerEvents: 'none',
position: 'absolute',
top: '2.5rem',
right: '0',
color: theme.palette.text.primary,
}}
/>
<Drawer
anchor={'right'}
open={isDrawerOpen}
onClose={toggleDrawer(false)}
variant="temporary"
>
<SupportContainer>
<SupportButton onClick={handleOpenBuyMeCoffee}>
<BuyMeCoffee
height={'1.5rem'}
width={'1.5rem'}
style={{ marginRight: '0.5rem' }}
/>
<Paragraph style={{ fontSize: '0.7rem' }}>Buy him a tea</Paragraph>
</SupportButton>
<SupportButton onClick={handleOpenKoFi}>
<KoFi
height={'1.5rem'}
width={'1.5rem'}
style={{ marginRight: '0.5rem' }}
/>
<Paragraph style={{ fontSize: '0.7rem' }}>
Buy him a ko-fi
</Paragraph>
</SupportButton>
</SupportContainer>
</Drawer>
</>
);
};

View File

@@ -1,11 +1,6 @@
import styled from 'styled-components'; import { twc } from 'react-twc';
import { theme } from '../../Data/theme';
export const Paragraph = styled.p` export const Paragraph = twc.p`text-text-primary;`;
color: ${theme.palette.text.primary};
`;
// eslint-disable-next-line react-refresh/only-export-components // eslint-disable-next-line react-refresh/only-export-components
export const H1 = styled.h1` export const H1 = twc.h1`text-text-primary;`;
color: ${theme.palette.text.primary};
`;

View File

@@ -0,0 +1,29 @@
import { LabelText } from '../Views/StartMenu/StartMenu';
export const ToggleButton = ({
label,
checked,
onChange,
}: {
label?: string;
checked: boolean;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}) => {
return (
<div className="flex flex-col items-center">
{label && <LabelText>{label}</LabelText>}
<label className="inline-flex items-center cursor-pointer relative h-6 w-10">
<input
type="checkbox"
value=""
checked={checked}
onChange={onChange}
className="sr-only peer"
/>
<div className="relative mx-1 w-10 h-[0.875rem] bg-gray-900 rounded-full peer peer-checked:bg-primary-dark" />
<div className="absolute peer-checked:translate-x-full rtl:peer-checked:-translate-x-full bg-secondary-main peer-checked:bg-primary-main rounded-full h-5 w-5 transition-all" />
</label>
</div>
);
};

View File

@@ -1,455 +0,0 @@
import { Button, Checkbox } from '@mui/material';
import styled, { css } from 'styled-components';
import { Player, Rotation } from '../../Types/Player';
import { theme } from '../../Data/theme';
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
import { usePlayers } from '../../Hooks/usePlayers';
import {
PartnerTax,
Poison,
Energy,
Experience,
Exit,
FullscreenOff,
FullscreenOn,
Cross,
ResetGame,
} from '../../Icons/generated';
import { useRef } from 'react';
import { Spacer } from '../Misc/Spacer';
import { useSafeRotate } from '../../Hooks/useSafeRotate';
const SettingsContainer = styled.div<{
$rotation: Rotation;
}>`
display: flex;
flex-direction: row;
flex-wrap: wrap;
height: 100%;
width: 100%;
${(props) => {
if (
props.$rotation === Rotation.SideFlipped ||
props.$rotation === Rotation.Side
) {
return css`
flex-direction: column-reverse;
height: 100%;
width: 100%;
`;
}
}}
${(props) => {
if (props.$rotation === Rotation.Side) {
return css`
rotate: ${props.$rotation - 180}deg;
`;
} else if (props.$rotation === Rotation.SideFlipped) {
return css`
rotate: ${props.$rotation - 180}deg;
`;
}
}}
`;
const BetterRowContainer = styled.div`
display: flex;
flex-direction: column;
flex-grow: 1;
width: 100%;
height: 100%;
justify-content: end;
align-items: stretch;
`;
const TogglesSection = styled.div`
display: flex;
position: relative;
flex-direction: row;
gap: 0.5rem;
justify-content: space-evenly;
`;
const ButtonsSections = styled.div`
display: flex;
max-width: 100%;
gap: 1rem;
justify-content: space-between;
padding: 3% 3%;
align-items: center;
`;
const ColorPicker = styled.input`
position: absolute;
top: 5%;
left: 5%;
height: 8vmax;
width: 8vmax;
border: none;
outline: none;
cursor: pointer;
background-color: transparent;
user-select: none;
color: #ffffff;
`;
const CheckboxContainer = styled.div<{ $rotation: Rotation }>`
${(props) => {
if (
props.$rotation === Rotation.SideFlipped ||
props.$rotation === Rotation.Side
) {
return css`
/* rotate: ${props.$rotation - 180}deg; */
`;
}
}}
`;
const PlayerMenuWrapper = styled.div<{
$rotation: Rotation;
}>`
display: flex;
flex-direction: column;
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(20, 20, 20, 0.9);
align-items: center;
justify-content: center;
z-index: 2;
${(props) => {
if (
props.$rotation === Rotation.SideFlipped ||
props.$rotation === Rotation.Side
) {
return;
}
return css`
rotate: ${props.$rotation}deg;
`;
}};
`;
const CloseButton = styled.div<{
$rotation: Rotation;
}>`
position: absolute;
top: 15%;
right: 5%;
z-index: 9999;
border: none;
outline: none;
cursor: pointer;
background-color: transparent;
${(props) => {
if (props.$rotation === Rotation.Side) {
return css`
rotate: ${props.$rotation - 180}deg;
top: 5%;
right: auto;
left: 5%;
`;
} else if (props.$rotation === Rotation.SideFlipped) {
return css`
rotate: ${props.$rotation - 180}deg;
top: auto;
left: auto;
bottom: 5%;
right: 5%;
`;
}
}}
`;
type PlayerMenuProps = {
player: Player;
setShowPlayerMenu: (showPlayerMenu: boolean) => void;
};
const PlayerMenu = ({ player, setShowPlayerMenu }: PlayerMenuProps) => {
const settingsContainerRef = useRef<HTMLDivElement | null>(null);
const dialogRef = useRef<HTMLDialogElement | null>(null);
const { isSide } = useSafeRotate({
rotation: player.settings.rotation,
containerRef: settingsContainerRef,
});
const handleOnClick = () => {
setShowPlayerMenu(false);
};
const { fullscreen, wakeLock, goToStart } = useGlobalSettings();
const { updatePlayer, resetCurrentGame } = usePlayers();
const handleColorChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const updatedPlayer = { ...player, color: event.target.value };
updatePlayer(updatedPlayer);
};
const handleSettingsChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, checked } = event.target;
const updatedSettings = { ...player.settings, [name]: checked };
const updatedPlayer = { ...player, settings: updatedSettings };
updatePlayer(updatedPlayer);
};
const handleResetGame = () => {
resetCurrentGame();
setShowPlayerMenu(false);
};
const toggleFullscreen = () => {
if (fullscreen.isFullscreen) {
fullscreen.disableFullscreen();
} else {
fullscreen.enableFullscreen();
}
};
const buttonFontSize = isSide ? '1.5vmax' : '3vmin';
const iconSize = isSide ? '6vmin' : '3vmax';
const extraCountersSize = isSide ? '8vmin' : '4vmax';
const closeButtonSize = isSide ? '6vmin' : '3vmax';
return (
<PlayerMenuWrapper $rotation={player.settings.rotation}>
<CloseButton $rotation={player.settings.rotation}>
<Button
variant="text"
onClick={handleOnClick}
style={{
margin: 0,
padding: 0,
height: closeButtonSize,
width: closeButtonSize,
}}
>
<Cross size={closeButtonSize} />
</Button>
</CloseButton>
<SettingsContainer
$rotation={player.settings.rotation}
ref={settingsContainerRef}
>
<ColorPicker
type="color"
value={player.color}
onChange={handleColorChange}
role="button"
aria-label="Color picker"
/>
<BetterRowContainer>
<TogglesSection>
{player.settings.useCommanderDamage && (
<CheckboxContainer $rotation={player.settings.rotation}>
<Checkbox
name="usePartner"
checked={player.settings.usePartner}
icon={
<PartnerTax
size={extraCountersSize}
color="black"
stroke="white"
strokeWidth="30"
/>
}
checkedIcon={
<PartnerTax
size={extraCountersSize}
color={player.color}
stroke="white"
strokeWidth="30"
/>
}
onChange={handleSettingsChange}
role="checkbox"
aria-checked={player.settings.usePartner}
aria-label="Partner"
/>
</CheckboxContainer>
)}
<CheckboxContainer $rotation={player.settings.rotation}>
<Checkbox
name="usePoison"
checked={player.settings.usePoison}
icon={
<Poison
size={extraCountersSize}
color="black"
stroke="white"
strokeWidth="30"
/>
}
checkedIcon={
<Poison
size={extraCountersSize}
color={player.color}
stroke="white"
strokeWidth="30"
/>
}
onChange={handleSettingsChange}
role="checkbox"
aria-checked={player.settings.usePoison}
aria-label="Poison"
/>
</CheckboxContainer>
<CheckboxContainer $rotation={player.settings.rotation}>
<Checkbox
name="useEnergy"
checked={player.settings.useEnergy}
icon={
<Energy
size={extraCountersSize}
color="black"
stroke="white"
strokeWidth="15"
/>
}
checkedIcon={
<Energy
size={extraCountersSize}
color={player.color}
stroke="white"
strokeWidth="15"
/>
}
onChange={handleSettingsChange}
role="checkbox"
aria-checked={player.settings.useEnergy}
aria-label="Energy"
/>
</CheckboxContainer>
<CheckboxContainer $rotation={player.settings.rotation}>
<Checkbox
name="useExperience"
checked={player.settings.useExperience}
icon={
<Experience
size={extraCountersSize}
color="black"
stroke="white"
strokeWidth="15"
/>
}
checkedIcon={
<Experience
size={extraCountersSize}
color={player.color}
stroke="white"
strokeWidth="15"
/>
}
onChange={handleSettingsChange}
role="checkbox"
aria-checked={player.settings.useExperience}
aria-label="Experience"
/>
</CheckboxContainer>
</TogglesSection>
<Spacer height="1rem" />
<ButtonsSections>
<Button
variant="text"
style={{
cursor: 'pointer',
userSelect: 'none',
}}
onClick={goToStart}
aria-label="Back to start"
>
<Exit size={iconSize} style={{ rotate: '180deg' }} />
</Button>
<CheckboxContainer $rotation={player.settings.rotation}>
<Checkbox
name="fullscreen"
checked={document.fullscreenElement ? true : false}
icon={
<FullscreenOff
size={iconSize}
color={theme.palette.primary.main}
/>
}
checkedIcon={<FullscreenOn size={iconSize} />}
onChange={toggleFullscreen}
role="checkbox"
aria-checked={document.fullscreenElement ? true : false}
aria-label="Fullscreen"
/>
</CheckboxContainer>
<Button
variant={wakeLock.active ? 'contained' : 'outlined'}
style={{
cursor: 'pointer',
userSelect: 'none',
fontSize: buttonFontSize,
padding: '0 4px 0 4px',
}}
onClick={wakeLock.toggleWakeLock}
role="checkbox"
aria-checked={wakeLock.active}
aria-label="Keep awake"
>
Keep Awake
</Button>
<Button
style={{
cursor: 'pointer',
userSelect: 'none',
fontSize: buttonFontSize,
padding: '4px',
}}
onClick={() => dialogRef.current?.show()}
role="checkbox"
aria-checked={wakeLock.active}
aria-label="Reset Game"
>
<ResetGame size={iconSize} />
</Button>
</ButtonsSections>
</BetterRowContainer>
<dialog
ref={dialogRef}
style={{
zIndex: 9999,
background: theme.palette.background.default,
color: theme.palette.text.primary,
borderRadius: '1rem',
border: 'none',
position: 'absolute',
top: '10%',
}}
>
<h1>Reset Game?</h1>
<div style={{ display: 'flex', justifyContent: 'space-evenly' }}>
<Button
variant="contained"
onClick={() => dialogRef.current?.close()}
>
No
</Button>
<Button
variant="contained"
onClick={() => {
handleResetGame();
dialogRef.current?.close();
}}
>
Yes
</Button>
</div>
</dialog>
</SettingsContainer>
</PlayerMenuWrapper>
);
};
export default PlayerMenu;

View File

@@ -0,0 +1,490 @@
import { useRef } from 'react';
import { twc } from 'react-twc';
import { useAnalytics } from '../../Hooks/useAnalytics';
import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
import { usePlayers } from '../../Hooks/usePlayers';
import { useSafeRotate } from '../../Hooks/useSafeRotate';
import {
Cross,
Energy,
Exit,
Experience,
FullscreenOff,
FullscreenOn,
PartnerTax,
Poison,
ResetGame,
} from '../../Icons/generated';
import { Player, Rotation } from '../../Types/Player';
import { PreStartMode } from '../../Types/Settings';
import { RotationDivProps } from '../Buttons/CommanderDamage';
import { IconCheckbox } from '../Misc/IconCheckbox';
const PlayerMenuWrapper = twc.div`
flex
flex-col
absolute
w-full
h-full
bg-background-settings
backdrop-blur-[3px]
items-center
justify-center
z-[2]
webkit-user-select-none
transition-all
`;
const BetterRowContainer = twc.div`
flex
flex-col
flex-grow
w-full
h-full
justify-between
items-stretch
`;
const TogglesSection = twc.div`
flex
flex-row
flex-wrap
relative
h-full
justify-evenly
items-center
`;
const ButtonsSections = twc.div`
flex
max-w-full
justify-evenly
items-center
flex-wrap
h-full
mt-0
px-2
`;
const ColorPickerButton = twc.div`
h-[8vmax]
w-[8vmax]
relative
max-h-12
max-w-12
rounded-full
cursor-pointer
overflow-hidden
`;
const SettingsContainer = twc.div<RotationDivProps>((props) => [
'flex flex-wrap h-full w-full overflow-y-scroll',
props.$rotation === Rotation.SideFlipped || props.$rotation === Rotation.Side
? 'flex-col'
: 'flex-row',
]);
type PlayerMenuProps = {
player: Player;
setShowPlayerMenu: (showPlayerMenu: boolean) => void;
isShown: boolean;
};
const PlayerMenu = ({
player,
setShowPlayerMenu,
isShown,
}: PlayerMenuProps) => {
const settingsContainerRef = useRef<HTMLDivElement | null>(null);
const resetGameDialogRef = useRef<HTMLDialogElement | null>(null);
const endGameDialogRef = useRef<HTMLDialogElement | null>(null);
const { isSide } = useSafeRotate({
rotation: player.settings.rotation,
containerRef: settingsContainerRef,
});
const {
fullscreen,
wakeLock,
goToStart,
settings,
setPlaying,
setRandomizingPlayer,
saveCurrentGame,
initialGameSettings,
setPreStartCompleted,
} = useGlobalSettings();
const analytics = useAnalytics();
const { updatePlayer, resetCurrentGame, players } = usePlayers();
const handleColorChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const updatedPlayer = { ...player, color: event.target.value };
updatePlayer(updatedPlayer);
};
const handleSettingsChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, checked } = event.target;
const updatedSettings = { ...player.settings, [name]: checked };
const updatedPlayer = { ...player, settings: updatedSettings };
updatePlayer(updatedPlayer);
};
const handleResetGame = () => {
resetCurrentGame();
setShowPlayerMenu(false);
setPlaying(false);
if (settings.preStartMode === PreStartMode.RandomKing) {
setRandomizingPlayer(true);
setPreStartCompleted(false);
}
analytics.trackEvent('reset_game');
};
const handleGoToStart = () => {
saveCurrentGame({ players, initialGameSettings });
goToStart();
setRandomizingPlayer(true);
};
const toggleFullscreen = () => {
if (fullscreen.isFullscreen) {
fullscreen.disableFullscreen();
} else {
fullscreen.enableFullscreen();
}
};
const buttonFontSize = isSide ? '1.5vmax' : '3vmin';
const iconSize = isSide ? '6vmin' : '3vmax';
const extraCountersSize = isSide ? '8vmin' : '4vmax';
const calcRotation =
player.settings.rotation === Rotation.Side
? `${player.settings.rotation - 180}deg`
: player.settings.rotation === Rotation.SideFlipped
? `${player.settings.rotation - 180}deg`
: '';
return (
<PlayerMenuWrapper
//TODO: Fix hacky solution to rotation for SideFlipped
style={{
rotate:
player.settings.rotation === Rotation.SideFlipped ? `180deg` : '',
translate: isShown ? '' : player.isSide ? `-100%` : `0 -100%`,
}}
>
<SettingsContainer
$rotation={player.settings.rotation}
style={{
rotate: calcRotation,
}}
ref={settingsContainerRef}
>
<button
onClick={() => {
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]"
>
<Cross size={buttonFontSize} className="text-primary-main " />
</button>
<BetterRowContainer>
<TogglesSection>
<ColorPickerButton aria-label="Color picker">
<input
onChange={handleColorChange}
type="color"
className="size-[200%] absolute -left-2 -top-2"
value={player.color}
onClick={() => {
analytics.trackEvent('color_picker_opened', {
player: player.index,
});
}}
/>
</ColorPickerButton>
{player.settings.useCommanderDamage && (
<div className="flex flex-col items-center">
<IconCheckbox
name="usePartner"
checked={player.settings.usePartner}
icon={
<PartnerTax
size={extraCountersSize}
color="black"
stroke="white"
strokeWidth="30"
/>
}
checkedIcon={
<PartnerTax
size={extraCountersSize}
color={player.color}
stroke="white"
strokeWidth="30"
/>
}
onChange={(e) => {
analytics.trackEvent('toggle_partner', {
checked: e.target.checked,
});
handleSettingsChange(e);
}}
aria-checked={player.settings.usePartner}
aria-label="Partner"
/>
</div>
)}
<div>
<IconCheckbox
name="usePoison"
checked={player.settings.usePoison}
icon={
<Poison
size={extraCountersSize}
color="black"
stroke="white"
strokeWidth="30"
/>
}
checkedIcon={
<Poison
size={extraCountersSize}
color={player.color}
stroke="white"
strokeWidth="30"
/>
}
onChange={(e) => {
analytics.trackEvent('toggle_poison', {
checked: e.target.checked,
});
handleSettingsChange(e);
}}
aria-checked={player.settings.usePoison}
aria-label="Poison"
/>
</div>
<div>
<IconCheckbox
name="useEnergy"
checked={player.settings.useEnergy}
icon={
<Energy
size={extraCountersSize}
color="black"
stroke="white"
strokeWidth="15"
/>
}
checkedIcon={
<Energy
size={extraCountersSize}
color={player.color}
stroke="white"
strokeWidth="15"
/>
}
onChange={(e) => {
analytics.trackEvent('toggle_energy', {
checked: e.target.checked,
});
handleSettingsChange(e);
}}
aria-checked={player.settings.useEnergy}
aria-label="Energy"
/>
</div>
<div>
<IconCheckbox
name="useExperience"
checked={player.settings.useExperience}
icon={
<Experience
size={extraCountersSize}
color="black"
stroke="white"
strokeWidth="15"
/>
}
checkedIcon={
<Experience
size={extraCountersSize}
color={player.color}
stroke="white"
strokeWidth="15"
/>
}
onChange={(e) => {
analytics.trackEvent('toggle_experience', {
checked: e.target.checked,
});
handleSettingsChange(e);
}}
aria-checked={player.settings.useExperience}
aria-label="Experience"
/>
</div>
</TogglesSection>
<ButtonsSections>
<button
className="text-primary-main cursor-pointer webkit-user-select-none"
onClick={() => endGameDialogRef.current?.show()}
aria-label="Back to start"
>
<Exit size={iconSize} style={{ rotate: '180deg' }} />
</button>
<div
data-fullscreen={document.fullscreenElement ? true : false}
className="flex
data-[fullscreen=true]:bg-secondary-dark rounded-lg border border-transparent
data-[fullscreen=true]:border-primary-main"
>
<IconCheckbox
className="p-1"
name="fullscreen"
checked={document.fullscreenElement ? true : false}
icon={
<FullscreenOff
size={iconSize}
className="text-primary-main"
/>
}
checkedIcon={
<FullscreenOn size={iconSize} className="text-primary-main" />
}
onChange={toggleFullscreen}
aria-checked={document.fullscreenElement ? true : false}
aria-label="Fullscreen"
/>
</div>
<button
data-wake-lock-active={settings.keepAwake}
style={{
fontSize: buttonFontSize,
}}
className="text-primary-main px-1 webkit-user-select-none cursor-pointer
data-[wake-lock-active=true]:bg-secondary-dark rounded-lg border border-transparent
data-[wake-lock-active=true]:border-primary-main
"
onClick={() => {
wakeLock.toggleWakeLock();
}}
role="checkbox"
aria-checked={settings.keepAwake}
aria-label="Keep awake"
>
Keep Awake
</button>
<button
style={{
cursor: 'pointer',
userSelect: 'none',
fontSize: buttonFontSize,
padding: '2px',
}}
className="text-primary-main"
onClick={() => resetGameDialogRef.current?.show()}
role="checkbox"
aria-label="Reset Game"
>
<ResetGame size={iconSize} />
</button>
</ButtonsSections>
</BetterRowContainer>
<dialog
ref={resetGameDialogRef}
className="z-[999] size-full bg-background-settings overflow-y-scroll"
onClick={() => resetGameDialogRef.current?.close()}
>
<div className="flex size-full items-center justify-center">
<div className="flex flex-col justify-center p-4 gap-2 bg-background-default rounded-xl border-none">
<h1
className="text-center text-text-primary"
style={{ fontSize: extraCountersSize }}
>
Reset Game?
</h1>
<div className="flex justify-evenly gap-2">
<button
className="bg-primary-main border border-primary-dark text-text-primary rounded-lg flex-grow"
style={{ fontSize: iconSize }}
onClick={() => resetGameDialogRef.current?.close()}
>
No
</button>
<button
className="bg-primary-main border border-primary-dark text-text-primary rounded-lg flex-grow"
onClick={() => {
handleResetGame();
resetGameDialogRef.current?.close();
}}
style={{ fontSize: iconSize }}
>
Yes
</button>
</div>
</div>
</div>
</dialog>
<dialog
ref={endGameDialogRef}
className="z-[999] size-full bg-background-settings overflow-y-scroll"
onClick={() => endGameDialogRef.current?.close()}
>
<div className="flex size-full items-center justify-center">
<div className="flex flex-col justify-center p-4 gap-2 bg-background-default rounded-xl border-none">
<h1
className="text-center text-text-primary"
style={{ fontSize: extraCountersSize }}
>
Go to start?
</h1>
<div
style={{ fontSize: iconSize }}
className="text-center text-text-primary"
>
(Game will be saved)
</div>
<div className="flex justify-evenly gap-2">
<button
className="bg-primary-main border border-primary-dark text-text-primary rounded-lg flex-grow"
style={{ fontSize: iconSize }}
onClick={() => endGameDialogRef.current?.close()}
>
No
</button>
<button
className="bg-primary-main border border-primary-dark text-text-primary rounded-lg flex-grow"
onClick={() => {
handleGoToStart();
endGameDialogRef.current?.close();
}}
style={{ fontSize: iconSize }}
>
Yes
</button>
</div>
</div>
</div>
</dialog>
</SettingsContainer>
</PlayerMenuWrapper>
);
};
export default PlayerMenu;

View File

@@ -1,5 +1,8 @@
import LifeCounter from '../LifeCounter/LifeCounter'; import { twc } from 'react-twc';
import { usePlayers } from '../../Hooks/usePlayers';
import { Player as PlayerType } from '../../Types/Player'; import { Player as PlayerType } from '../../Types/Player';
import LifeCounter from '../LifeCounter/LifeCounter';
import { GridLayout } from '../Views/Play';
const getGridArea = (player: PlayerType) => { const getGridArea = (player: PlayerType) => {
switch (player.index) { switch (player.index) {
@@ -20,13 +23,16 @@ const getGridArea = (player: PlayerType) => {
} }
}; };
export const Player = (players: PlayerType[], gridClasses: string) => { const PlayersWrapper = twc.div`w-full h-full bg-black`;
export const Players = ({ gridLayout }: { gridLayout: GridLayout }) => {
const { players } = usePlayers();
return ( return (
<div className="w-full h-full bg-black"> <PlayersWrapper>
<div className={`grid w-full h-full gap-1 box-border ${gridClasses} `}> <div className={`grid w-full h-full gap-1 box-border ${gridLayout} `}>
{players.map((player) => { {players.map((player) => {
const gridArea = getGridArea(player); const gridArea = getGridArea(player);
return ( return (
<div <div
key={player.index} key={player.index}
@@ -42,6 +48,6 @@ export const Player = (players: PlayerType[], gridClasses: string) => {
); );
})} })}
</div> </div>
</div> </PlayersWrapper>
); );
}; };

View File

@@ -0,0 +1,202 @@
import { useEffect, useRef, useState } from 'react';
import { useGlobalSettings } from '../../../Hooks/useGlobalSettings';
import { usePlayers } from '../../../Hooks/usePlayers';
type TouchPoint = {
x: number;
y: number;
id: number;
};
const getOrientation = () => {
return window.matchMedia('(orientation: portrait)').matches
? 'portrait'
: 'landscape';
};
const ANIMATION_INTRO_LENGTH = 500;
const BEFORE_INTRO_TIMER_LENGTH = 100;
export const FingerGame = () => {
const { players } = usePlayers();
const aboutToStartTimerRef = useRef<NodeJS.Timeout | null>(null);
const selectingPlayerTimerRef = useRef<NodeJS.Timeout | null>(null);
const [touchPoints, setTouchPoints] = useState<TouchPoint[]>([]);
const [selectedTouchPoint, setSelectedTouchPoint] = useState<
TouchPoint | undefined
>();
const [timerStarted, setTimerStarted] = useState(false);
const { setPlaying, goToStart } = useGlobalSettings();
useEffect(() => {
//Start playing when someone is selected and any touch point is released
if (selectedTouchPoint && touchPoints.length !== players.length) {
aboutToStartTimerRef.current = setTimeout(() => {
setSelectedTouchPoint(undefined);
setPlaying(true);
}, ANIMATION_INTRO_LENGTH);
setTimerStarted(true);
return;
}
// If no touch point is selected, select one with a delay
if (touchPoints.length === players.length && !selectedTouchPoint) {
selectingPlayerTimerRef.current = setTimeout(() => {
const randomIndex = Math.floor(Math.random() * touchPoints.length);
const randomTouchPoint = touchPoints[randomIndex];
setSelectedTouchPoint(randomTouchPoint);
}, BEFORE_INTRO_TIMER_LENGTH);
return;
}
if (selectingPlayerTimerRef.current) {
clearTimeout(selectingPlayerTimerRef.current);
}
return () => {
if (aboutToStartTimerRef.current) {
clearTimeout(aboutToStartTimerRef.current);
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [touchPoints, players.length]);
const handleOnTouchStart = (e: React.TouchEvent) => {
if (selectedTouchPoint) {
return;
}
//Get the first touch point id that isn't already in the touchPoints array
const touch = Array.from(e.changedTouches).find(
(t) => !touchPoints.find((p) => p.id === t.identifier)
);
if (!touch) {
console.error('No touch found');
return;
}
let { clientX, clientY } = touch;
// Adjust coordinates for portrait mode
if (getOrientation() === 'portrait') {
const tempX = clientX;
clientX = clientY;
clientY = window.innerWidth - tempX;
}
const newTouchPoints = {
x: clientX,
y: clientY,
id: touch.identifier,
};
setTouchPoints([...touchPoints, newTouchPoints]);
};
const handleOnTouchEnd = (e: React.TouchEvent) => {
if (selectedTouchPoint) {
aboutToStartTimerRef.current = setTimeout(() => {
setSelectedTouchPoint(undefined);
setPlaying(true);
}, ANIMATION_INTRO_LENGTH);
setTimerStarted(true);
return;
}
// Get the touch point that was just released
const touch = e.changedTouches[e.changedTouches.length - 1];
// Get the index of the touch point that was just released
const index = touchPoints.findIndex((p) => p.id === touch.identifier);
// Remove the touch point that was just released
setTouchPoints([
...touchPoints.slice(0, index),
...touchPoints.slice(index + 1),
]);
};
return (
<div
className="absolute flex justify-center items-center w-full h-full portrait:h-[100dvw] portrait:w-[100dvh] z-50 bg-secondary-main overflow-hidden"
onTouchStart={handleOnTouchStart}
onTouchEnd={handleOnTouchEnd}
// FIXEME: This code is not performant, but updates a touch point's position when it moves
// onTouchMove={(e) => {
// e.preventDefault();
// // Get the touch point that was just moved
// const touch = Array.from(e.changedTouches).find((t) =>
// touchPoints.find((p) => p.id === t.identifier)
// );
// if (!touch) {
// console.error('No touch found');
// return;
// }
// let { clientX, clientY } = touch;
// // Adjust coordinates for portrait mode
// if (getOrientation() === 'portrait') {
// const tempX = clientX;
// clientX = clientY;
// clientY = window.innerWidth - tempX;
// }
// // Get the index of the touch point that was just moved
// const index = touchPoints.findIndex(
// (p) => p.id === touch.identifier
// );
// // Update the touch point that was just moved
// setTouchPoints([
// ...touchPoints.slice(0, index),
// { x: clientX, y: clientY, id: touch.identifier },
// ...touchPoints.slice(index + 1),
// ]);
// }}
>
<button
className="absolute flex top-4 left-4 rounded-lg px-2 py-1 justify-center bg-primary-main text-text-primary text-xs duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark"
onClick={goToStart}
>
<div className="text-xl leading-4">{'<'}&nbsp;</div>
Back
</button>
{touchPoints.length !== players.length && (
<div className="flex flex-col items-center text-[13vmin] whitespace-nowrap pointer-events-none webkit-user-select-none">
Waiting for fingers <br />
<div className="tabular-nums">
{touchPoints.length}/{players.length}
</div>
</div>
)}
{touchPoints.map((point, index) => (
<div
key={`touch-point-${index}`}
data-is-selected={selectedTouchPoint?.id === point.id}
data-unloading={timerStarted}
className="absolute rounded-full translate-x-[-50%] translate-y-[-50%] transition-all duration-500
h-[75px] w-[75px]
data-[unloading=false]:data-[is-selected=true]:h-[250px] data-[unloading=false]:data-[is-selected=true]:w-[250px]
data-[unloading=true]:h-[0px] data-[unloading=true]:w-[0px] data-[unloading=true]:duration-[400ms]
pointer-events-none
"
style={{
left: point.x,
top: point.y,
backgroundColor: players[index]?.color ?? 'red',
}}
/>
))}
</div>
);
};

View File

@@ -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 (
<div className="w-full h-full bg-black">
<div className={`grid w-full h-full gap-1 box-border ${gridLayout} `}>
{players.map((player) => {
const gridArea = getGridArea(player);
return (
<div
key={player.index}
className={`flex justify-center items-center align-middle ${gridArea}`}
>
<RoulettePlayerCard player={player} />
</div>
);
})}
</div>
</div>
);
};

View File

@@ -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<NodeJS.Timeout | null>(null);
const prevRandomIndexRef = useRef<number>(-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 (
<div
className="absolute flex justify-center items-center h-screen w-screen portrait:h-[100vw] portrait:w-[100vh] z-40 cursor-pointer text-5xl"
onClick={() => {
if (randomIntervalRef.current) {
clearInterval(randomIntervalRef.current);
randomIntervalRef.current = null;
}
setRandomizingPlayer(false);
setPreStartCompleted(true);
}}
>
<div className="absolute flex top-[30%] justify-center items-center px-8 py-4">
<div
className="absolute size-full blur-[3px] rounded-2xl opacity-90 saturate-150"
style={{
backgroundImage: `linear-gradient(60deg, ${gradientColors})`,
}}
/>
<p className="relative z-10 text-[5vmax]">PRESS TO SELECT PLAYER</p>
</div>
</div>
);
};

View File

@@ -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<NodeJS.Timeout | undefined>(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 (
<div className="relative flex flex-grow flex-col items-center w-full h-full overflow-hidden">
<div
className="flex absolute w-full h-full justify-center items-center pointer-events-none select-none webkit-user-select-none z-10"
style={{ backgroundColor: player.color }}
>
{player.isStartingPlayer && (
<DynamicText
style={{
rotate: `${calcTextRotation}deg`,
}}
>
<div className="flex flex-col justify-center items-center">
<Paragraph>👑</Paragraph>
</div>
</DynamicText>
)}
</div>
</div>
);
};

View File

@@ -0,0 +1,159 @@
import { useState } from 'react';
import { useGlobalSettings } from '../../../Hooks/useGlobalSettings';
const questions = [
'Who has the most siblings?',
'Who has the most pets?',
'Who has the most tattoos?',
'Who has the most piercings?',
'Who has the most expensive shoes?',
'Who has the most most amount of teeth?',
'Who has the least amount of teeth?',
'Who lives closest to the equator?',
'Who is the tallest person in the group?',
'Who is the shortest person in the group?',
'Who speaks the most languages?',
'Who has traveled to the most countries?',
'Who has the earliest birthday in the year?',
'Who has won the most awards or trophies?',
'Who is the best cook among you?',
'Who is the fastest runner?',
'Who has the most unique hobby?',
'Who is the biggest movie buff?',
'Who is the most tech-savvy?',
'Who is the best at solving puzzles?',
'Who has the most extensive music collection?',
'Who has the most impressive collection of books?',
'Who has the most experience in a particular sport or activity?',
'Who has the most interesting job or profession?',
'Who has the most artistic talent?',
'Who is the most organized person?',
'Who is the best at keeping secrets?',
'Who has the most fascinating family history?',
'Who has the most embarrassing childhood nickname?',
'Who has the most unusual talent or skill?',
'Who has the most interesting family tradition?',
'Who has the most impressive celebrity encounter?',
'Who has the most unusual phobia?',
'Who has the most adventurous spirit?',
'Who has the most unique item in their wallet/purse?',
'Who has the most daring fashion sense?',
'Who has the most impressive party trick?',
'Who has the most memorable encounter with a wild animal?',
'Who has the most adventurous palate?',
'Who has the most unusual collection?',
'Who has the most unique bucket list item?',
'Who has the most inspiring life motto or mantra?',
'Who is the most likely to break out into song or dance in public?',
'Who is the most likely to be found binge-watching TV shows?',
'Who is the biggest procrastinator?',
'Who is the most likely to cry during a movie?',
'Who is the most adventurous when it comes to trying new foods?',
"Who is the most likely to forget someone's birthday?",
'Who is the best at giving advice?',
'Who is the worst at giving advice?',
'Who is the most likely to be found reading a book at a party?',
'Who is the most likely to win in a game of charades?',
'Who is the most likely to get lost in their own neighborhood?',
'Who is the most sentimental?',
'Who is the most likely to become famous?',
'Who is the most likely to become a millionaire?',
'Who is the most likely to start their own business?',
'Who is the most likely to become president?',
'Who is the most likely to go viral on social media?',
'Who is the most likely to win a Nobel Prize?',
'Who is the most likely to be a superhero in disguise?',
'Who is the most likely to survive a zombie apocalypse?',
'Who is the most likely to believe in aliens?',
'Who is the most likely to spend all their money on something silly?',
'Who is the most likely to write a bestselling novel?',
'Who is the most likely to be a secret agent?',
'Who is the most likely to be a professional athlete?',
'Who is the most likely to win a game of trivia?',
'Who is the most likely to win the upcoming game?',
'Who is the most likely to win at a game of Pokémon TCG?',
'Who has the most valuable card in their collection?',
'Who is the best at building decks?',
'Who has won the most games?',
'Who has the largest collection of cards?',
'Who is the most knowledgeable about Magic the Gathering lore?',
'Who is the most strategic?',
'Who is the most likely to trade away their most valuable card for something silly?',
'Who is the most competitive?',
'Who would be the most creative when it comes to making up new Magic the Gathering rules?',
'Who is the most likely to organize a Magic the Gathering draft tournament?',
'Who is the most enthusiastic about opening booster packs?',
'Who has the most unique and unusual Magic the Gathering deck?',
'Who is the most likely to cosplay as their favorite Magic the Gathering character?',
'Who is the most likely to forget to bring their Magic the Gathering deck to a game night?',
'Who is the most generous when it comes to lending out their decks?',
'Who is the most likely to start their own Magic the Gathering YouTube channel?',
'Who is the most skilled at bluffing during a game of Magic the Gathering?',
'Who is the most likely to spend all their money on Magic the Gathering cards?',
'Who is the most likely to rage quit during a game of Magic the Gathering?',
'Who is the most likely to win in a Magic the Gathering trivia contest?',
'Who is the most likely to build a themed Magic the Gathering deck?',
'Who is the most likely to organize a Magic the Gathering cube draft?',
'Who is the most likely to teach new players how to play Magic the Gathering?',
'Who is the most likely to build a commander deck with a ridiculous theme?',
'Who is the most likely to collect foreign-language Magic the Gathering cards?',
'Who is the most likely to participate in a Magic the Gathering charity event?',
'Who is the most likely to cosplay as their Magic the Gathering commander?',
'Who is the most likely to organize a Magic the Gathering charity tournament?',
];
export const Trivia = () => {
const { setPlaying, goToStart } = useGlobalSettings();
const [randomQuestion, setRandomQuestion] = useState(
questions[Math.floor(Math.random() * questions.length)]
);
const setUniqueRandomQuestion = () => {
let newRandomQuestion =
questions[Math.floor(Math.random() * questions.length)];
while (newRandomQuestion === randomQuestion) {
newRandomQuestion =
questions[Math.floor(Math.random() * questions.length)];
}
setRandomQuestion(newRandomQuestion);
};
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"
onClick={() => setPlaying(true)}
>
<button
className="absolute flex top-4 left-4 rounded-lg px-2 py-1 justify-center bg-primary-main text-text-primary text-xs duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark"
onClick={goToStart}
>
<div className="text-xl leading-[0.80rem]">{'<'}&nbsp;</div>
Back
</button>
<button
className="absolute flex top-4 right-4 rounded-lg px-2 py-1 justify-center bg-primary-main text-text-primary text-xs duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-primary-dark"
onClick={(e) => {
e.stopPropagation();
setUniqueRandomQuestion();
}}
>
Reroll
</button>
<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">
{randomQuestion}
</div>
<div className="text-[8vmin]">{randomQuestion}</div>
</div>
<div className="text-[6vmin]">(Tap the screen to dismiss)</div>
</div>
</div>
);
};

View File

@@ -0,0 +1,35 @@
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';
import { Trivia } from './Games/Trivia';
export const PreStart = ({ gridLayout }: { gridLayout: GridLayout }) => {
const { settings, randomizingPlayer, goToStart } = useGlobalSettings();
if (settings.preStartMode === PreStartMode.RandomKing) {
if (!randomizingPlayer) {
return null;
}
return (
<>
<RandomKingSelectWrapper />
<RandomKingPlayers gridLayout={gridLayout} />
</>
);
}
if (settings.preStartMode === PreStartMode.FingerGame) {
return <FingerGame />;
}
if (settings.preStartMode === PreStartMode.Trivia) {
return <Trivia />;
}
goToStart();
return null;
};

View File

@@ -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 (
<div
className="z-20 flex absolute w-full h-full justify-center items-center select-none cursor-pointer webkit-user-select-none"
style={{
rotate: `${calcRotation}deg`,
backgroundImage:
!randomizingPlayer ||
(settings.preStartMode !== PreStartMode.None &&
settings.preStartMode !== PreStartMode.FingerGame)
? `radial-gradient(circle at center, ${player.color}, ${baseColors.primary.main})`
: 'none',
}}
onClick={() => {
setPlaying(true);
}}
>
<DynamicText
style={{
rotate: `${calcTextRotation}deg`,
}}
>
<div className="flex flex-col justify-center items-center">
<Paragraph>👑</Paragraph>
{(!randomizingPlayer ||
(settings.preStartMode !== PreStartMode.None &&
settings.preStartMode !== PreStartMode.FingerGame)) && (
<>
<Paragraph>You start!</Paragraph>
<Paragraph className="text-xl">(Press to hide)</Paragraph>
</>
)}
</div>
</DynamicText>
</div>
);
};

View File

@@ -1,73 +1,116 @@
import styled from 'styled-components'; import { useEffect } from 'react';
import { twc } from 'react-twc';
import { twGridTemplateAreas } from '../../../tailwind.config';
import { useGlobalSettings } from '../../Hooks/useGlobalSettings'; import { useGlobalSettings } from '../../Hooks/useGlobalSettings';
import { usePlayers } from '../../Hooks/usePlayers'; import { usePlayers } from '../../Hooks/usePlayers';
import { Orientation } from '../../Types/Settings'; import { Orientation, PreStartMode } from '../../Types/Settings';
import { Player } from '../Player/Player'; import { Players } from '../Players/Players';
import { PreStart } from '../PreStartGame/PreStart';
const MainWrapper = styled.div` const MainWrapper = twc.div`w-[100dvmax] h-[100dvmin] overflow-hidden, setPlayers`;
width: 100vmax;
height: 100vmin; type GridTemplateAreasKeys = keyof typeof twGridTemplateAreas;
width: 100dvmax;
height: 100dvmin; export type GridLayout = `grid-areas-${GridTemplateAreasKeys}`;
overflow: hidden;
`;
export const Play = () => { export const Play = () => {
const { players } = usePlayers(); const { players, setPlayers } = usePlayers();
const { initialGameSettings } = useGlobalSettings(); const { initialGameSettings, playing, settings, preStartCompleted } =
useGlobalSettings();
let Layout: JSX.Element; let gridLayout: GridLayout;
switch (players.length) { switch (players.length) {
case 1: case 1:
if (initialGameSettings?.orientation === Orientation.Portrait) { if (initialGameSettings?.orientation === Orientation.Portrait) {
Layout = Player(players, 'grid-areas-onePlayerPortrait'); gridLayout = 'grid-areas-onePlayerPortrait';
} }
Layout = Player(players, 'grid-areas-onePlayerLandscape'); gridLayout = 'grid-areas-onePlayerLandscape';
break; break;
case 2: case 2:
switch (initialGameSettings?.orientation) { switch (initialGameSettings?.orientation) {
case Orientation.Portrait: case Orientation.Portrait:
Layout = Player(players, 'grid-areas-twoPlayersOppositePortrait'); gridLayout = 'grid-areas-twoPlayersOppositePortrait';
break; break;
default: default:
case Orientation.Landscape: case Orientation.Landscape:
Layout = Player(players, 'grid-areas-twoPlayersSameSideLandscape'); gridLayout = 'grid-areas-twoPlayersSameSideLandscape';
break; break;
case Orientation.OppositeLandscape: case Orientation.OppositeLandscape:
Layout = Player(players, 'grid-areas-twoPlayersOppositeLandscape'); gridLayout = 'grid-areas-twoPlayersOppositeLandscape';
break; break;
} }
break; break;
case 3: case 3:
if (initialGameSettings?.orientation === Orientation.Portrait) { if (initialGameSettings?.orientation === Orientation.Portrait) {
Layout = Player(players, 'grid-areas-threePlayersSide'); gridLayout = 'grid-areas-threePlayersSide';
break; break;
} }
Layout = Player(players, 'grid-areas-threePlayers'); gridLayout = 'grid-areas-threePlayers';
break; break;
default: default:
case 4: case 4:
if (initialGameSettings?.orientation === Orientation.Portrait) { if (initialGameSettings?.orientation === Orientation.Portrait) {
Layout = Player(players, 'grid-areas-fourPlayerPortrait'); gridLayout = 'grid-areas-fourPlayerPortrait';
break; break;
} }
Layout = Player(players, 'grid-areas-fourPlayer'); gridLayout = 'grid-areas-fourPlayer';
break; break;
case 5: case 5:
if (initialGameSettings?.orientation === Orientation.Portrait) { if (initialGameSettings?.orientation === Orientation.Portrait) {
Layout = Player(players, 'grid-areas-fivePlayersSide'); gridLayout = 'grid-areas-fivePlayersSide';
break; break;
} }
Layout = Player(players, 'grid-areas-fivePlayers'); gridLayout = 'grid-areas-fivePlayers';
break; break;
case 6: case 6:
if (initialGameSettings?.orientation === Orientation.Portrait) { if (initialGameSettings?.orientation === Orientation.Portrait) {
Layout = Player(players, 'grid-areas-sixPlayersSide'); gridLayout = 'grid-areas-sixPlayersSide';
break; break;
} }
Layout = Player(players, 'grid-areas-sixPlayers'); gridLayout = 'grid-areas-sixPlayers';
break; break;
} }
return <MainWrapper>{Layout}</MainWrapper>; 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 (
players.length > 1 &&
!preStartCompleted &&
settings.preStartMode !== PreStartMode.None &&
!playing &&
settings.showStartingPlayer
) {
return (
<MainWrapper>
<PreStart gridLayout={gridLayout} />
</MainWrapper>
);
}
return (
<MainWrapper>
<Players gridLayout={gridLayout} />
</MainWrapper>
);
}; };

View File

@@ -1,28 +1,30 @@
import { FormControlLabel, Radio, RadioGroup } from '@mui/material'; import { twc } from 'react-twc';
import React from 'react';
import styled from 'styled-components';
import { theme } from '../../../Data/theme';
import { import {
FivePlayers, FivePlayers,
FivePlayersSide,
FourPlayers, FourPlayers,
FourPlayersSide, FourPlayersSide,
OnePlayerPortrait, OnePlayerPortrait,
SixPlayers, SixPlayers,
SixPlayersSide,
ThreePlayers, ThreePlayers,
ThreePlayersSide, ThreePlayersSide,
TwoPlayersOppositeLandscape, TwoPlayersOppositeLandscape,
TwoPlayersOppositePortrait, TwoPlayersOppositePortrait,
TwoPlayersSameSide, TwoPlayersSameSide,
} from '../../../Icons/generated/Layouts'; } from '../../../Icons/generated/Layouts';
import OnePlayerLandscape from '../../../Icons/generated/Layouts/OnePlayerLandscape'; import OnePlayerLandscape from '../../../Icons/generated/Layouts/OnePlayerLandscape';
import { Orientation } from '../../../Types/Settings'; import { Orientation } from '../../../Types/Settings';
const LayoutWrapper = styled.div` const LayoutsRadioGroup = twc.div`flex flex-row justify-center items-center gap-4 self-center w-full`;
flex-direction: row;
display: flex; const Label = twc.label`flex flex-row relative max-w-[118px] hover:bg-primary-main hover:bg-opacity-5 rounded-2xl cursor-pointer`;
justify-content: space-evenly;
`; const Input = twc.input`peer sr-only`;
const IconWrapper = twc(
'div'
)`peer-checked:fill-primary-main fill-primary-dark max-h-[200px] h-[40vmin] w-[21vmin]`;
type LayoutOptionsProps = { type LayoutOptionsProps = {
numberOfPlayers: number; numberOfPlayers: number;
@@ -30,366 +32,212 @@ type LayoutOptionsProps = {
onChange: (orientation: Orientation) => void; onChange: (orientation: Orientation) => void;
}; };
export const LayoutOptions: React.FC<LayoutOptionsProps> = ({ export const LayoutOptions = ({
numberOfPlayers, numberOfPlayers,
selectedOrientation, selectedOrientation,
onChange, onChange,
}) => { }: LayoutOptionsProps) => {
const iconHeight = '30vmin'; switch (numberOfPlayers) {
const iconWidth = '20vmin'; case 1:
return (
<LayoutsRadioGroup>
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Landscape}`}>
<Input
type="radio"
id={`${numberOfPlayers}p-${Orientation.Landscape}`}
checked={selectedOrientation === Orientation.Landscape}
value={Orientation.Landscape}
onChange={(e) => onChange(e.target.value as Orientation)}
/>
<IconWrapper>
<OnePlayerLandscape className="w-full h-full" />
</IconWrapper>
</Label>
const renderLayoutOptions = () => { <Label htmlFor={`${numberOfPlayers}p-${Orientation.Portrait}`}>
switch (numberOfPlayers) { <Input
case 1: type="radio"
return ( id={`${numberOfPlayers}p-${Orientation.Portrait}`}
<> checked={selectedOrientation === Orientation.Portrait}
<FormControlLabel
value={Orientation.Landscape}
control={
<Radio
icon={
<OnePlayerLandscape
height={iconHeight}
width={iconWidth}
fill={theme.palette.secondary.main}
/>
}
checkedIcon={
<OnePlayerLandscape
height={iconHeight}
width={iconWidth}
fill={theme.palette.primary.main}
/>
}
TouchRippleProps={{ style: { display: 'none' } }}
/>
}
label=""
/>
<FormControlLabel
value={Orientation.Portrait} value={Orientation.Portrait}
control={ onChange={(e) => onChange(e.target.value as Orientation)}
<Radio
icon={
<OnePlayerPortrait
height={iconHeight}
width={iconWidth}
fill={theme.palette.secondary.main}
/>
}
checkedIcon={
<OnePlayerPortrait
height={iconHeight}
width={iconWidth}
fill={theme.palette.primary.main}
/>
}
TouchRippleProps={{ style: { display: 'none' } }}
/>
}
label=""
/> />
</> <IconWrapper>
); <OnePlayerPortrait className="w-full h-full" />
case 2: </IconWrapper>
return ( </Label>
<> </LayoutsRadioGroup>
<FormControlLabel );
case 2:
return (
<LayoutsRadioGroup>
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Landscape}`}>
<Input
type="radio"
id={`${numberOfPlayers}p-${Orientation.Landscape}`}
checked={selectedOrientation === Orientation.Landscape}
value={Orientation.Landscape} value={Orientation.Landscape}
control={ onChange={(e) => onChange(e.target.value as Orientation)}
<Radio
icon={
<TwoPlayersSameSide
height={iconHeight}
width={iconWidth}
fill={theme.palette.secondary.main}
/>
}
checkedIcon={
<TwoPlayersSameSide
height={iconHeight}
width={iconWidth}
fill={theme.palette.primary.main}
/>
}
TouchRippleProps={{ style: { display: 'none' } }}
/>
}
label=""
/> />
<FormControlLabel <IconWrapper>
<TwoPlayersSameSide className="w-full h-full" />
</IconWrapper>
</Label>
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Portrait}`}>
<Input
type="radio"
id={`${numberOfPlayers}p-${Orientation.Portrait}`}
checked={selectedOrientation === Orientation.Portrait}
value={Orientation.Portrait} value={Orientation.Portrait}
control={ onChange={(e) => onChange(e.target.value as Orientation)}
<Radio
icon={
<TwoPlayersOppositePortrait
height={iconHeight}
width={iconWidth}
fill={theme.palette.secondary.main}
/>
}
checkedIcon={
<TwoPlayersOppositePortrait
height={iconHeight}
width={iconWidth}
fill={theme.palette.primary.main}
/>
}
TouchRippleProps={{ style: { display: 'none' } }}
/>
}
label=""
/> />
<FormControlLabel <IconWrapper>
<TwoPlayersOppositePortrait className="w-full h-full" />
</IconWrapper>
</Label>
<Label
htmlFor={`${numberOfPlayers}p-${Orientation.OppositeLandscape}`}
>
<Input
type="radio"
id={`${numberOfPlayers}p-${Orientation.OppositeLandscape}`}
checked={selectedOrientation === Orientation.OppositeLandscape}
value={Orientation.OppositeLandscape} value={Orientation.OppositeLandscape}
control={ onChange={(e) => onChange(e.target.value as Orientation)}
<Radio
icon={
<TwoPlayersOppositeLandscape
height={iconHeight}
width={iconWidth}
fill={theme.palette.secondary.main}
/>
}
checkedIcon={
<TwoPlayersOppositeLandscape
height={iconHeight}
width={iconWidth}
fill={theme.palette.primary.main}
/>
}
TouchRippleProps={{ style: { display: 'none' } }}
/>
}
label=""
/> />
</> <IconWrapper>
); <TwoPlayersOppositeLandscape className="w-full h-full" />
case 3: </IconWrapper>
return ( </Label>
<> </LayoutsRadioGroup>
<FormControlLabel );
case 3:
return (
<LayoutsRadioGroup>
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Landscape}`}>
<Input
type="radio"
id={`${numberOfPlayers}p-${Orientation.Landscape}`}
checked={selectedOrientation === Orientation.Landscape}
value={Orientation.Landscape} value={Orientation.Landscape}
control={ onChange={(e) => onChange(e.target.value as Orientation)}
<Radio
icon={
<ThreePlayers
height={iconHeight}
width={iconWidth}
fill={theme.palette.secondary.main}
/>
}
checkedIcon={
<ThreePlayers
height={iconHeight}
width={iconWidth}
fill={theme.palette.primary.main}
/>
}
TouchRippleProps={{ style: { display: 'none' } }}
/>
}
label=""
/> />
<FormControlLabel <IconWrapper>
<ThreePlayers className="w-full h-full" />
</IconWrapper>
</Label>
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Portrait}`}>
<Input
type="radio"
id={`${numberOfPlayers}p-${Orientation.Portrait}`}
checked={selectedOrientation === Orientation.Portrait}
value={Orientation.Portrait} value={Orientation.Portrait}
control={ onChange={(e) => onChange(e.target.value as Orientation)}
<Radio
icon={
<ThreePlayersSide
height={iconHeight}
width={iconWidth}
fill={theme.palette.secondary.main}
/>
}
checkedIcon={
<ThreePlayersSide
height={iconHeight}
width={iconWidth}
fill={theme.palette.primary.main}
/>
}
TouchRippleProps={{ style: { display: 'none' } }}
/>
}
label=""
/> />
</> <IconWrapper>
); <ThreePlayersSide className="w-full h-full" />
</IconWrapper>
</Label>
</LayoutsRadioGroup>
);
case 4: case 4:
return ( return (
<> <LayoutsRadioGroup>
<FormControlLabel <Label htmlFor={`${numberOfPlayers}p-${Orientation.Landscape}`}>
<Input
type="radio"
id={`${numberOfPlayers}p-${Orientation.Landscape}`}
checked={selectedOrientation === Orientation.Landscape}
value={Orientation.Landscape} value={Orientation.Landscape}
control={ onChange={(e) => onChange(e.target.value as Orientation)}
<Radio
icon={
<FourPlayers
height={iconHeight}
width={iconWidth}
fill={theme.palette.secondary.main}
/>
}
checkedIcon={
<FourPlayers
height={iconHeight}
width={iconWidth}
fill={theme.palette.primary.main}
/>
}
TouchRippleProps={{ style: { display: 'none' } }}
/>
}
label=""
/> />
<FormControlLabel <IconWrapper>
<FourPlayers className="w-full h-full" />
</IconWrapper>
</Label>
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Portrait}`}>
<Input
type="radio"
id={`${numberOfPlayers}p-${Orientation.Portrait}`}
checked={selectedOrientation === Orientation.Portrait}
value={Orientation.Portrait} value={Orientation.Portrait}
control={ onChange={(e) => onChange(e.target.value as Orientation)}
<Radio
icon={
<FourPlayersSide
height={iconHeight}
width={iconWidth}
fill={theme.palette.secondary.main}
/>
}
checkedIcon={
<FourPlayersSide
height={iconHeight}
width={iconWidth}
fill={theme.palette.primary.main}
/>
}
TouchRippleProps={{ style: { display: 'none' } }}
/>
}
label=""
/> />
</> <IconWrapper>
); <FourPlayersSide className="w-full h-full" />
</IconWrapper>
</Label>
</LayoutsRadioGroup>
);
case 5: case 5:
return ( return (
<> <LayoutsRadioGroup>
<FormControlLabel <Label htmlFor={`${numberOfPlayers}p-${Orientation.Landscape}`}>
<Input
type="radio"
id={`${numberOfPlayers}p-${Orientation.Landscape}`}
checked={selectedOrientation === Orientation.Landscape}
value={Orientation.Landscape} value={Orientation.Landscape}
control={ onChange={(e) => onChange(e.target.value as Orientation)}
<Radio
icon={
<FivePlayers
height={iconHeight}
width={iconWidth}
fill={theme.palette.secondary.main}
/>
}
checkedIcon={
<FivePlayers
height={iconHeight}
width={iconWidth}
fill={theme.palette.primary.main}
/>
}
TouchRippleProps={{ style: { display: 'none' } }}
/>
}
label=""
/> />
{/* <FormControlLabel <IconWrapper>
value={GridTemplateAreas.FivePlayersSide} <FivePlayers className="w-full h-full" />
control={ </IconWrapper>
<Radio </Label>
icon={
<FivePlayersSide
height={iconHeight}
width={iconWidth}
fill={theme.palette.secondary.main}
/>
}
checkedIcon={
<FivePlayersSide
height={iconHeight}
width={iconWidth}
fill={theme.palette.primary.main}
/>
}
TouchRippleProps={{ style: { display: 'none' } }}
/>
}
label=""
/> */}
</>
);
case 6: <Label htmlFor={`${numberOfPlayers}p-${Orientation.Portrait}`}>
return ( <Input
<> type="radio"
<FormControlLabel id={`${numberOfPlayers}p-${Orientation.Portrait}`}
checked={selectedOrientation === Orientation.Portrait}
value={Orientation.Portrait}
onChange={(e) => onChange(e.target.value as Orientation)}
/>
<IconWrapper>
<FivePlayersSide className="w-full h-full" />
</IconWrapper>
</Label>
</LayoutsRadioGroup>
);
case 6:
return (
<LayoutsRadioGroup>
<Label htmlFor={`${numberOfPlayers}p-${Orientation.Landscape}`}>
<Input
type="radio"
id={`${numberOfPlayers}p-${Orientation.Landscape}`}
checked={selectedOrientation === Orientation.Landscape}
value={Orientation.Landscape} value={Orientation.Landscape}
control={ onChange={(e) => onChange(e.target.value as Orientation)}
<Radio
icon={
<SixPlayers
height={iconHeight}
width={iconWidth}
fill={theme.palette.secondary.main}
/>
}
checkedIcon={
<SixPlayers
height={iconHeight}
width={iconWidth}
fill={theme.palette.primary.main}
/>
}
TouchRippleProps={{ style: { display: 'none' } }}
/>
}
label=""
/> />
{/* <FormControlLabel <IconWrapper>
value={GridTemplateAreas.SixPlayersSide} <SixPlayers className="w-full h-full" />
control={ </IconWrapper>
<Radio </Label>
icon={
<SixPlayersSide
height={iconHeight}
width={iconWidth}
fill={theme.palette.secondary.main}
/>
}
checkedIcon={
<SixPlayersSide
height={iconHeight}
width={iconWidth}
fill={theme.palette.primary.main}
/>
}
TouchRippleProps={{ style: { display: 'none' } }}
/>
}
label=""
/> */}
</>
);
default: <Label htmlFor={`${numberOfPlayers}p-${Orientation.Portrait}`}>
return null; <Input
} type="radio"
}; id={`${numberOfPlayers}p-${Orientation.Portrait}`}
checked={selectedOrientation === Orientation.Portrait}
value={Orientation.Portrait}
onChange={(e) => onChange(e.target.value as Orientation)}
/>
<IconWrapper>
<SixPlayersSide className="w-full h-full" />
</IconWrapper>
</Label>
</LayoutsRadioGroup>
);
return ( default:
<LayoutWrapper> return null;
<RadioGroup }
row
onChange={(_e, value) => {
onChange(value as Orientation);
}}
value={selectedOrientation}
style={{ justifyContent: 'center' }}
>
{renderLayoutOptions()}
</RadioGroup>
</LayoutWrapper>
);
}; };

View File

@@ -1,100 +1,51 @@
import { Button, FormControl, FormLabel, Switch } from '@mui/material'; import { useEffect, useRef, useState } from 'react';
import Slider from '@mui/material/Slider'; import { twc } from 'react-twc';
import { useEffect, useState } from 'react';
import styled from 'styled-components';
import { createInitialPlayers } from '../../../Data/getInitialPlayers'; import { createInitialPlayers } from '../../../Data/getInitialPlayers';
import { theme } from '../../../Data/theme';
import { useAnalytics } from '../../../Hooks/useAnalytics'; 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 { Cog, Info } from '../../../Icons/generated'; import { Cog, Info, Trinket } from '../../../Icons/generated';
import { InitialGameSettings, Orientation } from '../../../Types/Settings'; import {
import { InfoModal } from '../../Misc/InfoModal'; InitialGameSettings,
import { SettingsModal } from '../../Misc/SettingsModal'; Orientation,
import { Spacer } from '../../Misc/Spacer'; PreStartMode,
import { SupportMe } from '../../Misc/SupportMe'; defaultInitialGameSettings,
import { H1, Paragraph } from '../../Misc/TextComponents'; } from '../../../Types/Settings';
import { LayoutOptions } from './LayoutOptions'; import { LayoutOptions } from './LayoutOptions';
import { InfoDialog } from '../../Dialogs/InfoDialog';
import { SettingsDialog } from '../../Dialogs/SettingsDialog';
import { ToggleButton } from '../../Misc/ToggleButton';
const MainWrapper = styled.div` const commanderSettings: Pick<
width: 100dvw; InitialGameSettings,
height: fit-content; 'numberOfPlayers' | 'startingLifeTotal' | 'orientation'
padding-bottom: 58px; > = {
overflow: hidden; numberOfPlayers: 4,
align-items: center; startingLifeTotal: 40,
display: flex; orientation: Orientation.Landscape,
flex-direction: column; };
`;
const StartButtonFooter = styled.div` const standardSettings: Pick<
position: fixed; InitialGameSettings,
bottom: 1rem; 'numberOfPlayers' | 'startingLifeTotal' | 'orientation'
translate: -50%, -50%; > = {
z-index: 1; numberOfPlayers: 2,
`; startingLifeTotal: 20,
orientation: Orientation.Landscape,
};
const ToggleButtonsWrapper = styled.div` const MainWrapper = twc.div`h-fit w-full pb-24 overflow-hidden items-center flex flex-col min-[349px]:pb-10`;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
`;
const ToggleContainer = styled.div` 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`;
display: flex;
flex-direction: column;
align-items: center;
`;
const playerMarks = [ const SliderWrapper = twc.div`mx-8 relative`;
{
value: 1,
label: '1',
},
{
value: 2,
label: '2',
},
{
value: 3,
label: '3',
},
{
value: 4,
label: '4',
},
{
value: 5,
label: '5',
},
{
value: 6,
label: '6',
},
];
const healthMarks = [ const ToggleButtonsWrapper = twc.div`flex flex-row justify-between items-center`;
{
value: 20, export const LabelText = twc.div`text-md text-text-primary font-medium`;
label: '20',
}, let tracked = false;
{
value: 30,
label: '30',
},
{
value: 40,
label: '40',
},
{
value: 50,
label: '50',
},
{
value: 60,
label: '60',
},
];
const Start = () => { const Start = () => {
const { setPlayers } = usePlayers(); const { setPlayers } = usePlayers();
@@ -107,27 +58,50 @@ const Start = () => {
setInitialGameSettings, setInitialGameSettings,
settings, settings,
isPWA, isPWA,
setRandomizingPlayer,
version,
setPlaying,
savedGame,
saveCurrentGame,
} = useGlobalSettings(); } = useGlobalSettings();
const [openInfoModal, setOpenInfoModal] = useState(false); const infoDialogRef = useRef<HTMLDialogElement | null>(null);
const [openSettingsModal, setOpenSettingsModal] = useState(false); const settingsDialogRef = useRef<HTMLDialogElement | null>(null);
const [playerOptions, setPlayerOptions] = useState<InitialGameSettings>( const [playerOptions, setPlayerOptions] = useState<InitialGameSettings>(
initialGameSettings || { initialGameSettings || defaultInitialGameSettings
numberOfPlayers: 4,
startingLifeTotal: 40,
useCommanderDamage: true,
orientation: Orientation.Portrait,
gameFormat: 'commander',
}
); );
const doStartGame = () => { // Check for new version on mount
useEffect(() => {
if (!tracked) {
version.checkForNewVersion('start_menu');
tracked = true;
}
});
useEffect(() => {
setInitialGameSettings(playerOptions);
}, [playerOptions, setInitialGameSettings]);
useEffect(() => {
setPlayerOptions({
...playerOptions,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [playerOptions.numberOfPlayers]);
const doStartNewGame = () => {
if (!initialGameSettings) { if (!initialGameSettings) {
return; return;
} }
analytics.trackEvent('game_started', { ...initialGameSettings }); analytics.trackEvent('game_started', {
...initialGameSettings,
...settings,
isPWA,
});
try { try {
if (settings.goFullscreenOnStart) { if (settings.goFullscreenOnStart) {
@@ -143,182 +117,238 @@ const Start = () => {
setInitialGameSettings(initialGameSettings); setInitialGameSettings(initialGameSettings);
setPlayers(createInitialPlayers(initialGameSettings)); setPlayers(createInitialPlayers(initialGameSettings));
setRandomizingPlayer(settings.preStartMode === PreStartMode.RandomKing);
setShowPlay(true); setShowPlay(true);
localStorage.setItem('playing', 'false'); setPlaying(false);
localStorage.setItem('showPlay', 'true'); tracked = false;
}; };
useEffect(() => { const doResumeGame = () => {
setInitialGameSettings(playerOptions); if (!savedGame) {
}, [playerOptions, setInitialGameSettings]); return;
}
const valuetext = (value: number) => { analytics.trackEvent('game_resumed', {
return `${value}`; ...initialGameSettings,
}; ...settings,
isPWA,
useEffect(() => {
setPlayerOptions({
...playerOptions,
}); });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [playerOptions.numberOfPlayers]); 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);
tracked = false;
};
const openInfo = () => {
if (infoDialogRef.current) {
infoDialogRef.current.showModal();
}
};
const openSettings = () => {
if (settingsDialogRef.current) {
settingsDialogRef.current.showModal();
version.checkForNewVersion('settings');
}
};
return ( return (
<MainWrapper> <>
<Info <InfoDialog dialogRef={infoDialogRef} />
color={theme.palette.primary.light}
size="2rem"
style={{ position: 'absolute', top: '1rem', left: '1rem' }}
onClick={() => {
setOpenInfoModal(!openInfoModal);
}}
/>
<InfoModal <SettingsDialog dialogRef={settingsDialogRef} />
closeModal={() => { <div className="flex justify-center items-center w-screen">
setOpenInfoModal(false); <MainWrapper>
}} <Info
isOpen={openInfoModal} 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>
<SettingsModal <h1 className="relative flex flex-col text-3xl font-bold mt-6 mb-6 text-text-primary justify-center items-center">
closeModal={() => { <div className="flex flex-row items-center">Life Trinket</div>
setOpenSettingsModal(false); <div className="h-[1px] w-[120%] bg-common-white opacity-50" />
}} <div className="flex absolute text-xs font-medium -bottom-4">
isOpen={openSettingsModal} v{version.installedVersion}
/> </div>
</h1>
<SupportMe /> <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,
...standardSettings,
});
}}
/>
<H1>Life Trinket</H1> <div className="flex flex-nowrap text-nowrap relative justify-center items-start">
<FormControl focused={false} style={{ width: '80vw' }}> <button
<FormLabel>Number of Players</FormLabel> 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"
<Slider onClick={openSettings}
title="Number of Players" >
max={6} <span className="text-sm flex flex-row items-center text-text-primary">
min={1} <Cog />
aria-label="Custom marks" &nbsp;Game Settings
value={playerOptions?.numberOfPlayers ?? 4} </span>
getAriaValueText={valuetext} </button>
step={null}
marks={playerMarks}
onChange={(_e, value) => {
setPlayerOptions({
...playerOptions,
numberOfPlayers: value as number,
orientation: Orientation.Landscape,
});
}}
/>
<Spacer height="0.7rem" />
<FormLabel>Starting Health</FormLabel>
<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,
})
}
/>
<Spacer height="1rem" />
<ToggleButtonsWrapper> <div
<ToggleContainer> data-not-latest-version={
<FormLabel>Commander</FormLabel> !version.isLatest && !!version.remoteVersion
<Switch }
checked={ className="absolute flex justify-center text-text-primary text-xxs -bottom-5 bg-primary-dark px-2 rounded-md
playerOptions.useCommanderDamage ?? opacity-0 transition-all duration-200 delay-500
initialGameSettings?.useCommanderDamage ?? data-[not-latest-version=true]:opacity-100
true "
} >
onChange={(_e, value) => { <div className="absolute bg-primary-dark rotate-45 size-2 -top-[2px] z-0" />
if (value) { <span className="z-10">
v{version.remoteVersion} available!
</span>
</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) => {
setPlayerOptions({
...playerOptions,
numberOfPlayers: Number.parseInt(e.target.value),
orientation: Orientation.Landscape,
});
}}
/>
<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({ setPlayerOptions({
...playerOptions, ...playerOptions,
useCommanderDamage: value, orientation,
numberOfPlayers: 4,
startingLifeTotal: 40,
orientation: Orientation.Landscape,
}); });
return; }}
} />
setPlayerOptions({ </div>
...playerOptions, {!isPWA && (
useCommanderDamage: value, <p className="text-center text-xs text-text-primary w-11/12 mt-4">
numberOfPlayers: 2, If you're on iOS, this page works better if you{' '}
startingLifeTotal: 20, <strong>hide the toolbar</strong> or{' '}
orientation: Orientation.Landscape, <strong>add the app to your home screen</strong>.
}); </p>
}} )}
/> </div>
</ToggleContainer>
<Button
variant="contained"
style={{ height: '2rem' }}
onClick={() => {
setOpenSettingsModal(true);
}}
>
<Cog /> &nbsp; Other settings
</Button>
</ToggleButtonsWrapper>
<FormLabel>Layout</FormLabel> <StartButtonFooter>
{/* <LayoutOptions <button
numberOfPlayers={playerOptions.numberOfPlayers} 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"
gridAreas={playerOptions.gridAreas} onClick={doStartNewGame}
onChange={(gridAreas) => >
setPlayerOptions({ NEW GAME
...playerOptions, </button>
gridAreas,
//TODO fix the layout selection
orientation: Orientation.Portrait,
})
}
/> */}
<LayoutOptions
numberOfPlayers={playerOptions.numberOfPlayers}
selectedOrientation={playerOptions.orientation}
onChange={(orientation) => {
console.log('orientation', { orientation });
setPlayerOptions({
...playerOptions,
orientation,
});
}}
/>
</FormControl>
{!isPWA && ( {savedGame && (
<Paragraph <button
style={{ textAlign: 'center', maxWidth: '75%', fontSize: '0.7rem' }} 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]
>
If you're on iOS, this page works better if you{' '} duration-200 ease-in-out shadow-[1px_2px_4px_0px_rgba(0,0,0,0.3)] hover:bg-secondary-dark"
<strong>hide the toolbar</strong> or{' '} onClick={doResumeGame}
<strong>add the app to your home screen</strong>. >
</Paragraph> RESUME&nbsp;
)} <span className="text-xs">
({savedGame.players.length}&nbsp;
<StartButtonFooter> {savedGame.players.length > 1 ? 'players' : 'player'})
<Button </span>
size="large" </button>
variant="contained" )}
onClick={doStartGame} </StartButtonFooter>
style={{ width: '90dvw' }} </MainWrapper>
> </div>
START GAME </>
</Button>
</StartButtonFooter>
</MainWrapper>
); );
}; };

View File

@@ -1,5 +1,18 @@
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 = {
installedVersion: string;
isLatest: boolean;
checkForNewVersion: (source: 'settings' | 'start_menu') => Promise<void>;
remoteVersion?: string;
};
export type SavedGame = {
initialGameSettings: InitialGameSettings;
players: Player[];
} | null;
export type GlobalSettingsContextType = { export type GlobalSettingsContextType = {
fullscreen: { fullscreen: {
@@ -18,11 +31,20 @@ 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;
playing: boolean;
setPlaying: (playing: boolean) => void;
randomizingPlayer: boolean;
setRandomizingPlayer: (stopRandom: boolean) => void;
isPWA: boolean; isPWA: boolean;
preStartCompleted: boolean;
setPreStartCompleted: (completed: boolean) => void;
version: Version;
savedGame: SavedGame;
saveCurrentGame: (currentGame: SavedGame) => void;
}; };
export const GlobalSettingsContext = export const GlobalSettingsContext =

View File

@@ -7,6 +7,8 @@ export type PlayersContextType = {
updatePlayer: (updatedPlayer: Player) => void; updatePlayer: (updatedPlayer: Player) => void;
updateLifeTotal: (player: Player, updatedLifeTotal: number) => number; updateLifeTotal: (player: Player, updatedLifeTotal: number) => number;
resetCurrentGame: () => void; resetCurrentGame: () => void;
startingPlayerIndex: number;
setStartingPlayerIndex: (index: number) => void;
}; };
export const PlayersContext = createContext<PlayersContextType | null>(null); export const PlayersContext = createContext<PlayersContextType | null>(null);

View File

@@ -1,7 +1,7 @@
import { Player, Rotation } from '../Types/Player'; import { Player, Rotation } from '../Types/Player';
import { InitialGameSettings, Orientation } from '../Types/Settings'; import { InitialGameSettings, Orientation } from '../Types/Settings';
const presetColors = [ export const presetColors = [
'#F06292', // Light Pink '#F06292', // Light Pink
'#4DB6AC', // Teal '#4DB6AC', // Teal
'#FFA726', // Orange '#FFA726', // Orange
@@ -127,15 +127,15 @@ const getOrientationRotations = (
case Orientation.Portrait: case Orientation.Portrait:
switch (index) { switch (index) {
case 0: case 0:
return Rotation.Side; return Rotation.Flipped;
case 1: case 1:
return Rotation.Side; return Rotation.Flipped;
case 2: case 2:
return Rotation.SideFlipped; return Rotation.Side;
case 3: case 3:
return Rotation.SideFlipped; return Rotation.Normal;
case 4: case 4:
return Rotation.SideFlipped; return Rotation.Normal;
default: default:
return Rotation.Normal; return Rotation.Normal;
} }
@@ -163,17 +163,17 @@ const getOrientationRotations = (
case Orientation.Portrait: case Orientation.Portrait:
switch (index) { switch (index) {
case 0: case 0:
return Rotation.Side; return Rotation.SideFlipped;
case 1: case 1:
return Rotation.Side; return Rotation.Flipped;
case 2: case 2:
return Rotation.Side; return Rotation.Flipped;
case 3: case 3:
return Rotation.SideFlipped; return Rotation.Side;
case 4: case 4:
return Rotation.SideFlipped; return Rotation.Normal;
case 5: case 5:
return Rotation.SideFlipped; return Rotation.Normal;
default: default:
return Rotation.Normal; return Rotation.Normal;
} }
@@ -191,10 +191,8 @@ export const createInitialPlayers = ({
}: InitialGameSettings): Player[] => { }: InitialGameSettings): Player[] => {
const players: Player[] = []; const players: Player[] = [];
const availableColors = [...presetColors]; // Create a copy of the colors array const availableColors = [...presetColors]; // Create a copy of the colors array
const firstPlayerIndex = Math.floor(Math.random() * numberOfPlayers);
for (let i = 0; i <= numberOfPlayers - 1; i++) { for (let i = 0; i <= numberOfPlayers - 1; i++) {
const isStartingPlayer = i === firstPlayerIndex;
const colorIndex = Math.floor(Math.random() * availableColors.length); const colorIndex = Math.floor(Math.random() * availableColors.length);
const color = availableColors[colorIndex]; const color = availableColors[colorIndex];
@@ -224,11 +222,11 @@ export const createInitialPlayers = ({
usePoison: false, usePoison: false,
rotation, rotation,
}, },
isStartingPlayer,
showStartingPlayer: isStartingPlayer,
extraCounters: [], extraCounters: [],
commanderDamage, commanderDamage,
hasLost: false, hasLost: false,
isStartingPlayer: false,
isSide: rotation === Rotation.Side || rotation === Rotation.SideFlipped,
}; };
players.push(player); players.push(player);

View File

@@ -1,108 +0,0 @@
import { createTheme } from '@mui/material';
export const theme = createTheme({
palette: {
primary: {
main: '#7F9172',
},
secondary: {
main: '#5E714C',
},
background: {
default: '#495E35',
},
text: {
primary: '#F5F5F5',
secondary: '#b3b39b',
},
action: {
disabled: '#5E714C',
},
common: {
white: '#F9FFE3',
black: '#000000',
},
},
components: {
MuiFormLabel: {
styleOverrides: {
root: {
fontSize: '1rem',
color: '#F5F5F5',
},
},
},
MuiSlider: {
styleOverrides: {
markLabel: {
fontSize: '1rem',
color: '#F5F5F5',
},
valueLabel: {
display: 'none',
color: '#F5F5F5',
background: '#5E714C',
},
track: {
height: '0.7rem',
},
rail: {
height: '0.7rem',
},
mark: {
width: '0.5rem',
height: '0.5rem',
borderRadius: '50%',
display: 'none',
},
thumb: {
width: '1.3rem',
height: '1.3rem',
},
},
},
MuiFormControlLabel: {
styleOverrides: {
root: {
margin: '0',
},
},
},
MuiFormGroup: {
styleOverrides: {
root: {},
},
},
MuiDrawer: {
styleOverrides: {
paper: {
top: '1rem',
background: '#495E35',
height: 'auto',
borderRadius: '8px',
},
},
},
MuiBackdrop: {
styleOverrides: {
root: {
backgroundColor: 'rgba(0, 0, 0, 0.3)',
},
},
},
MuiButton: {
styleOverrides: {
root: {
textTransform: 'none',
},
},
},
MuiSwitch: {
styleOverrides: {
colorPrimary: {
color: '#5E714C',
},
},
},
},
});

View File

@@ -18,7 +18,17 @@ export const useAnalytics = () => {
eventName: string, eventName: string,
eventParams?: { [key: string]: unknown } eventParams?: { [key: string]: unknown }
) => { ) => {
logEvent(analytics, eventName, eventParams); if (process.env.NODE_ENV === 'development') {
console.info('Event not tracked:', { eventName, eventParams });
return;
}
const paramsWithVersion = {
...eventParams,
app_version: import.meta.env.VITE_APP_VERSION,
};
logEvent(analytics, eventName, paramsWithVersion);
}; };
return { trackEvent }; return { trackEvent };

View File

@@ -0,0 +1,52 @@
import { useEffect, useState } from 'react';
export interface OrientationState {
angle: number;
type: string;
}
const defaultState: OrientationState = {
angle: 0,
type: 'landscape-primary',
};
export default function useOrientation(
initialState: OrientationState = defaultState
) {
const [state, setState] = useState(initialState);
const [isLandscape, setIsLandscape] = useState(false);
useEffect(() => {
const screen = window.screen;
let mounted = true;
const onChange = () => {
if (mounted) {
const { orientation } = screen;
if (orientation) {
const { angle, type } = orientation;
setState({ angle, type });
if (type.includes('landscape')) {
setIsLandscape(true);
} else if (type.includes('portrait')) {
setIsLandscape(false);
}
} else if (window.orientation !== undefined) {
setState({
angle:
typeof window.orientation === 'number' ? window.orientation : 0,
type: '',
});
}
}
};
onChange();
return () => {
mounted = false;
};
}, [isLandscape]);
return { state, isLandscape };
}

View File

@@ -22,7 +22,7 @@ const Cog = ({
{...props} {...props}
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path d="M80.7 1c-4.6 1.4-9.4 5.7-11.6 10.3-1.6 3.4-2.1 6.2-2.1 12.8v8.5l-5.7-5.4C48.2 15 38.1 15 26.3 27.1 14.9 38.9 15.2 49.2 27.4 61l5.7 5.5-10.1.6c-9 .5-10.4.8-14.2 3.5-2.3 1.6-5.2 4.9-6.6 7.4-2.1 4-2.3 5.5-2 14 .4 11.1 2.4 15.4 9 19.8 3.5 2.2 5.3 2.7 13.4 3l9.4.4-5.2 5.7c-11.6 12.7-11.8 22.4-.5 34 5.7 5.8 11.5 9 16.3 9.1 7 0 9.7-1.3 16.9-8.2l7.3-6.8.4 9.9c.3 8.6.7 10.3 3 13.8 1.5 2.2 4.4 5.1 6.5 6.4 3.4 2.1 5 2.4 14.3 2.4s10.9-.3 14.3-2.4c2.1-1.3 5-4.1 6.5-6.3 2.3-3.5 2.7-5.3 3-13.5l.4-9.5 4.2 4c7.3 6.9 11.1 9.2 16.4 9.9 7 .8 11.7-1.1 18.6-7.6 12.7-12.1 12.9-22.2.5-34.7l-6.1-6.2 9.9-.4c8.9-.3 10.2-.6 14.2-3.3 6.2-4.2 8.5-9.1 8.9-19.2.5-10.7-2.4-17.7-9-21.9-3.8-2.5-5.5-2.9-14.2-3.2l-9.8-.4 6.1-6.2c12.2-12.4 12.2-22.4 0-34.3-11.6-11.2-20.5-11.2-33.4.2l-6.3 5.5-.4-9.4c-.3-8.1-.8-9.9-3-13.4-4.4-6.6-8.7-8.6-19.3-8.9-4.9-.1-10.3.2-11.8.7zm24.4 59.9c5.9 3 12.1 9.2 15 14.9 2.1 4 2.4 6.1 2.4 14.7 0 8.5-.3 10.7-2.3 14.5-3.2 6.1-8.7 11.6-14.7 14.8-4.5 2.4-6.1 2.7-15 2.7s-10.5-.3-15-2.7c-6-3.2-11.5-8.7-14.7-14.8-3.1-6-3.7-18.6-1.3-25.9 2.7-8.3 12.1-17.4 20.6-20 6-1.8 19.9-.8 25 1.8z" /> <path d="M80.7 1c-4.6 1.4-9.4 5.7-11.6 10.3-1.6 3.4-2.1 6.2-2.1 12.8v8.5l-5.7-5.4C48.2 15 38.1 15 26.3 27.1 14.9 38.9 15.2 49.2 27.4 61l5.7 5.5-10.1.6c-9 .5-10.4.8-14.2 3.5-2.3 1.6-5.2 4.9-6.6 7.4-2.1 4-2.3 5.5-2 14 .4 11.1 2.4 15.4 9 19.8 3.5 2.2 5.3 2.7 13.4 3l9.4.4-5.2 5.7c-11.6 12.7-11.8 22.4-.5 34 5.7 5.8 11.5 9 16.3 9.1 7 0 9.7-1.3 16.9-8.2l7.3-6.8.4 9.9c.3 8.6.7 10.3 3 13.8 1.5 2.2 4.4 5.1 6.5 6.4 3.4 2.1 5 2.4 14.3 2.4s10.9-.3 14.3-2.4c2.1-1.3 5-4.1 6.5-6.3 2.3-3.5 2.7-5.3 3-13.5l.4-9.5 4.2 4c7.3 6.9 11.1 9.2 16.4 9.9 7 .8 11.7-1.1 18.6-7.6 12.7-12.1 12.9-22.2.5-34.7l-6.1-6.2 9.9-.4c8.9-.3 10.2-.6 14.2-3.3 6.2-4.2 8.5-9.1 8.9-19.2.5-10.7-2.4-17.7-9-21.9-3.8-2.5-5.5-2.9-14.2-3.2l-9.8-.4 6.1-6.2c12.2-12.4 12.2-22.4 0-34.3-11.6-11.2-20.5-11.2-33.4.2l-6.3 5.5-.4-9.4c-.3-8.1-.8-9.9-3-13.4-4.4-6.6-8.7-8.6-19.3-8.9-4.9-.1-10.3.2-11.8.7m24.4 59.9c5.9 3 12.1 9.2 15 14.9 2.1 4 2.4 6.1 2.4 14.7 0 8.5-.3 10.7-2.3 14.5-3.2 6.1-8.7 11.6-14.7 14.8-4.5 2.4-6.1 2.7-15 2.7s-10.5-.3-15-2.7c-6-3.2-11.5-8.7-14.7-14.8-3.1-6-3.7-18.6-1.3-25.9 2.7-8.3 12.1-17.4 20.6-20 6-1.8 19.9-.8 25 1.8" />
</svg> </svg>
); );
}; };

View File

@@ -22,7 +22,7 @@ const CommanderTax = ({
{...props} {...props}
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path d="M256 23.9c-1.9.4-5.5 1.8-7.9 3.2-5.1 2.8-130.3 133.3-135.8 141.5-5.4 8.1-7.5 14.7-8.1 25.5-.5 11.3.7 17.6 5.2 26.9 3.7 7.8 13.8 18.4 21.1 22.1 2.8 1.4 20.4 7.5 39.3 13.4 18.8 6 33.7 11.2 33 11.6-.7.3-24.2 9.3-52.3 19.9-32.8 12.4-52.9 20.5-56.3 22.7-6 4-12 10.7-15.6 17.4-1.5 2.7-9.7 31.1-20.6 71.5C41.3 461.3 39.9 467 40.3 473c.9 12.3 7.2 21.7 18.5 27.2l6.7 3.3H262c190.8 0 196.7-.1 202-1.9 7.5-2.6 15.9-11 18.8-18.9 4.1-10.8 3.9-11.8-15.3-81.5-9.6-35-18.7-66.6-20.2-70.1-3.4-8.3-13-18.6-21-22.8-3.2-1.7-27.9-11.5-55-21.9-27.1-10.4-48.9-19.2-48.5-19.5.4-.4 15.1-5.3 32.7-10.8 17.6-5.6 34.5-11.4 37.5-12.9 11.4-5.6 23-20.5 25.9-33.2 2.7-11.5.7-27.5-4.7-37.3-3.6-6.7-5.2-8.5-73.7-80.2-45.1-47.2-61.4-63.7-65.1-65.7-5.3-2.8-13.7-4.1-19.4-2.9zm32.5 97.6c9.3 1.8 31.6 8.3 32.9 9.5 1.6 1.5-12.1 43.6-18.3 56.5-6 12.3-16.1 27.7-22.6 34.7-7.3 7.7-16.8 10.3-26.6 7.5-7.3-2.2-11.4-5.8-19.6-17.4-16.1-22.8-22.4-36.2-29.9-64.3-2.4-9-4.1-16.7-3.6-17 2.3-2.1 30.7-9.3 44.7-11.4 6.3-.9 35.2.3 43 1.9zM325.1 225l62.1-31.1-.7 3.3c-1 4.7-2.1 6.4-5.8 9.1-1.7 1.3-28.8 16-60.1 32.7-51.8 27.7-57.1 30.2-59.6 29.3-5.3-2-114.5-60.6-116.9-62.7-2.5-2.2-5.1-7.4-5.1-10.1 0-1.1 18.7 7.8 62 29.5l61.9 31 62.2-31zm-194 131.4c1.1 3 .1 11.2-8.7 69.9-5.4 36.7-10.2 66.3-10.5 65.9-.4-.4-3-8-5.8-16.9l-5.1-16.2 11-57.8c6.1-31.8 11.4-58.5 11.6-59.3.5-1.4 4.6 6.4 7.5 14.4zm280.8 44.2 11.2 58.1-5.2 16.6c-2.9 9.2-5.6 16.4-6 15.9-.3-.4-5-30.4-10.4-66.7l-9.8-66 3.9-8.7c2.2-4.8 4.2-8.4 4.5-8 .4.4 5.7 26.8 11.8 58.8z" /> <path d="M256 23.9c-1.9.4-5.5 1.8-7.9 3.2-5.1 2.8-130.3 133.3-135.8 141.5-5.4 8.1-7.5 14.7-8.1 25.5-.5 11.3.7 17.6 5.2 26.9 3.7 7.8 13.8 18.4 21.1 22.1 2.8 1.4 20.4 7.5 39.3 13.4 18.8 6 33.7 11.2 33 11.6-.7.3-24.2 9.3-52.3 19.9-32.8 12.4-52.9 20.5-56.3 22.7-6 4-12 10.7-15.6 17.4-1.5 2.7-9.7 31.1-20.6 71.5C41.3 461.3 39.9 467 40.3 473c.9 12.3 7.2 21.7 18.5 27.2l6.7 3.3H262c190.8 0 196.7-.1 202-1.9 7.5-2.6 15.9-11 18.8-18.9 4.1-10.8 3.9-11.8-15.3-81.5-9.6-35-18.7-66.6-20.2-70.1-3.4-8.3-13-18.6-21-22.8-3.2-1.7-27.9-11.5-55-21.9s-48.9-19.2-48.5-19.5c.4-.4 15.1-5.3 32.7-10.8 17.6-5.6 34.5-11.4 37.5-12.9 11.4-5.6 23-20.5 25.9-33.2 2.7-11.5.7-27.5-4.7-37.3-3.6-6.7-5.2-8.5-73.7-80.2-45.1-47.2-61.4-63.7-65.1-65.7-5.3-2.8-13.7-4.1-19.4-2.9m32.5 97.6c9.3 1.8 31.6 8.3 32.9 9.5 1.6 1.5-12.1 43.6-18.3 56.5-6 12.3-16.1 27.7-22.6 34.7-7.3 7.7-16.8 10.3-26.6 7.5-7.3-2.2-11.4-5.8-19.6-17.4-16.1-22.8-22.4-36.2-29.9-64.3-2.4-9-4.1-16.7-3.6-17 2.3-2.1 30.7-9.3 44.7-11.4 6.3-.9 35.2.3 43 1.9M325.1 225l62.1-31.1-.7 3.3c-1 4.7-2.1 6.4-5.8 9.1-1.7 1.3-28.8 16-60.1 32.7-51.8 27.7-57.1 30.2-59.6 29.3-5.3-2-114.5-60.6-116.9-62.7-2.5-2.2-5.1-7.4-5.1-10.1 0-1.1 18.7 7.8 62 29.5l61.9 31zm-194 131.4c1.1 3 .1 11.2-8.7 69.9-5.4 36.7-10.2 66.3-10.5 65.9-.4-.4-3-8-5.8-16.9l-5.1-16.2 11-57.8c6.1-31.8 11.4-58.5 11.6-59.3.5-1.4 4.6 6.4 7.5 14.4m280.8 44.2 11.2 58.1-5.2 16.6c-2.9 9.2-5.6 16.4-6 15.9-.3-.4-5-30.4-10.4-66.7l-9.8-66 3.9-8.7c2.2-4.8 4.2-8.4 4.5-8 .4.4 5.7 26.8 11.8 58.8" />
</svg> </svg>
); );
}; };

View File

@@ -22,7 +22,7 @@ const Cross = ({
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path <path
fill="currentColor" fill="currentColor"
d="M85.8 34.4c-5.3 1.9-9.2 5.3-30 26.3C35.9 80.8 33 84.8 33 93c0 2.5.9 6.4 1.9 8.6 1.2 2.7 27.7 32.1 74.5 82.7 39.9 43.3 72.6 79.1 72.6 79.7 0 .6-32.3 35.2-71.7 77-48.5 51.3-72.7 77.7-74.6 81.1-3.4 6.4-3.5 11.7-.3 18.4 3.3 6.8 42 45.5 48.6 48.4 6.4 2.9 12.7 2.7 18.8-.7 3.3-1.7 31.6-27.6 81.3-74.5 42-39.4 76.7-71.6 77.1-71.5.4.2 35.5 32.5 77.9 71.8 42.4 39.3 79.2 72.7 81.8 74.2 3.2 1.9 6 2.8 9.1 2.8 10.1 0 10.9-.6 34.9-24.3 14.2-14.1 23-23.7 24.2-26.2 2.4-5.1 2.4-12.8 0-18.1-1.2-2.6-27.1-30.8-74.5-81.2-40-42.5-72.6-77.6-72.4-78.1.2-.4 32.5-34.7 71.8-76.1 39.3-41.4 72.5-76.7 73.8-78.4 4.5-6.3 4.4-17.9-.1-24.2-5.1-7.2-43.3-46.7-46.9-48.5-2.1-1.1-5.8-2.2-8.2-2.5-9.6-1.4-5.3-5.2-158.3 138.2L262.2 183l-78.4-73.1c-49.6-46.5-79.8-73.9-82.3-75-4.7-2.2-10.5-2.3-15.7-.5z" d="M85.8 34.4c-5.3 1.9-9.2 5.3-30 26.3C35.9 80.8 33 84.8 33 93c0 2.5.9 6.4 1.9 8.6 1.2 2.7 27.7 32.1 74.5 82.7 39.9 43.3 72.6 79.1 72.6 79.7s-32.3 35.2-71.7 77c-48.5 51.3-72.7 77.7-74.6 81.1-3.4 6.4-3.5 11.7-.3 18.4 3.3 6.8 42 45.5 48.6 48.4 6.4 2.9 12.7 2.7 18.8-.7 3.3-1.7 31.6-27.6 81.3-74.5 42-39.4 76.7-71.6 77.1-71.5.4.2 35.5 32.5 77.9 71.8s79.2 72.7 81.8 74.2c3.2 1.9 6 2.8 9.1 2.8 10.1 0 10.9-.6 34.9-24.3 14.2-14.1 23-23.7 24.2-26.2 2.4-5.1 2.4-12.8 0-18.1-1.2-2.6-27.1-30.8-74.5-81.2-40-42.5-72.6-77.6-72.4-78.1.2-.4 32.5-34.7 71.8-76.1s72.5-76.7 73.8-78.4c4.5-6.3 4.4-17.9-.1-24.2-5.1-7.2-43.3-46.7-46.9-48.5-2.1-1.1-5.8-2.2-8.2-2.5-9.6-1.4-5.3-5.2-158.3 138.2L262.2 183l-78.4-73.1c-49.6-46.5-79.8-73.9-82.3-75-4.7-2.2-10.5-2.3-15.7-.5"
/> />
</svg> </svg>
); );

View File

@@ -22,7 +22,7 @@ const Energy = ({
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path <path
fill="currentColor" fill="currentColor"
d="M115 56.7c0 43.4-3.9 70.1-15.2 105.3-13.2 41.2-31 72.7-64.7 114.9-9.4 11.8-16.8 21.8-16.4 22.1.5.4 15.9 7.1 34.3 15 45.7 19.5 62.7 28.1 85.5 43.3 20.7 13.8 37.9 28 56 46.2 25.5 25.5 37.6 41.5 64.9 86.1l2.8 4.7 4.2-6.7c23.6-38.7 58.8-78.7 94.3-107.4 36.6-29.6 69.6-48.8 120.8-70.6 12.7-5.4 23.4-10.2 23.8-10.6.5-.4-6.1-9.5-14.6-20.1-18.3-23.1-25-32.4-34.6-48.3-22.9-38.1-38.1-82-44.6-128.6-1.1-7.4-1.8-22.4-2.2-41.8l-.6-30.3-2.6.5c-1.4.3-24.6 4.1-51.6 8.5-92 15-94.4 15-199.8-2.5-21.3-3.5-39-6.4-39.2-6.4-.3 0-.5 12-.5 26.7zm129 28.7c14.8 6.1 22.9 7.8 39.5 8.3 13.2.4 16.9.2 30.8-2.2 8.7-1.5 16-2.6 16.2-2.4.2.2-4 4-9.2 8.4-19.6 16.6-37.7 36.8-50.7 56.6-9.5 14.7-24.6 45.4-24.6 50.2 0 1 3.2 1.8 12.3 3 42.6 5.8 64 5.9 108 .6 9.3-1.1 17-1.9 17.2-1.7.2.2-7.6 9.3-17.2 20.3-28.6 32.6-40.9 48.8-55.3 72.5-10.5 17.4-14.4 26.9-26.6 64-6.3 19.5-13.9 42.7-16.8 51.5l-5.3 16-.9-28c-1.2-36.9-1-59 .5-70 2.3-17.3 7.5-35.6 15.1-53.8 4.5-10.5 3.9-11.7-6.9-14.6-22.9-6.2-71.6-4.5-114.5 3.9-8.3 1.6-15.1 2.8-15.3 2.6-.2-.1 3-4.1 7-8.7 31.2-35.8 55.4-78.7 69.6-123.3 2.1-6.6 6.4-22.4 9.6-34.9 4.8-18.9 6.1-22.8 7.4-22.3.9.2 5.4 2 10.1 4z" d="M115 56.7c0 43.4-3.9 70.1-15.2 105.3-13.2 41.2-31 72.7-64.7 114.9-9.4 11.8-16.8 21.8-16.4 22.1.5.4 15.9 7.1 34.3 15 45.7 19.5 62.7 28.1 85.5 43.3 20.7 13.8 37.9 28 56 46.2 25.5 25.5 37.6 41.5 64.9 86.1l2.8 4.7 4.2-6.7c23.6-38.7 58.8-78.7 94.3-107.4 36.6-29.6 69.6-48.8 120.8-70.6 12.7-5.4 23.4-10.2 23.8-10.6.5-.4-6.1-9.5-14.6-20.1-18.3-23.1-25-32.4-34.6-48.3-22.9-38.1-38.1-82-44.6-128.6-1.1-7.4-1.8-22.4-2.2-41.8l-.6-30.3-2.6.5c-1.4.3-24.6 4.1-51.6 8.5-92 15-94.4 15-199.8-2.5-21.3-3.5-39-6.4-39.2-6.4-.3 0-.5 12-.5 26.7m129 28.7c14.8 6.1 22.9 7.8 39.5 8.3 13.2.4 16.9.2 30.8-2.2 8.7-1.5 16-2.6 16.2-2.4s-4 4-9.2 8.4c-19.6 16.6-37.7 36.8-50.7 56.6-9.5 14.7-24.6 45.4-24.6 50.2 0 1 3.2 1.8 12.3 3 42.6 5.8 64 5.9 108 .6 9.3-1.1 17-1.9 17.2-1.7s-7.6 9.3-17.2 20.3c-28.6 32.6-40.9 48.8-55.3 72.5-10.5 17.4-14.4 26.9-26.6 64-6.3 19.5-13.9 42.7-16.8 51.5l-5.3 16-.9-28c-1.2-36.9-1-59 .5-70 2.3-17.3 7.5-35.6 15.1-53.8 4.5-10.5 3.9-11.7-6.9-14.6-22.9-6.2-71.6-4.5-114.5 3.9-8.3 1.6-15.1 2.8-15.3 2.6-.2-.1 3-4.1 7-8.7 31.2-35.8 55.4-78.7 69.6-123.3 2.1-6.6 6.4-22.4 9.6-34.9 4.8-18.9 6.1-22.8 7.4-22.3.9.2 5.4 2 10.1 4"
/> />
</svg> </svg>
); );

View File

@@ -21,8 +21,8 @@ const Exit = ({
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<g fill="currentColor"> <g fill="currentColor">
<path d="M149.3 6.5c-20.9 4.6-40.3 24.4-44.8 46-2.2 10.6-2.2 408.5 0 419 2.4 11.3 7.7 20.9 16.4 29.6 9 9 16.2 13.2 26.7 15.9 7.5 1.9 11.2 2 113.3 2 87.8 0 106.7-.3 112.4-1.5 20-4.2 38.2-21.9 44.4-43.1 1.5-5.2 1.7-12.9 2.1-68.7l.3-62.7h-30l-.3 60.7c-.3 59.8-.3 60.9-2.5 65.5-2.6 5.7-9.4 12.4-15.3 15.2-4.5 2.1-4.9 2.1-110.5 2.1h-106l-5.7-2.8c-3.2-1.6-7.5-4.8-9.7-7.3-7.7-8.8-7.1 9.5-6.9-215.9.3-199 .3-202.1 2.3-206 2.7-5.5 9.7-12.2 15.3-14.8l4.7-2.2h211l5.6 2.6c6.8 3.2 12.4 8.7 15.3 14.8 2 4.5 2.1 6.1 2.4 54.3l.3 49.8h30l-.3-51.8c-.4-45.1-.7-52.5-2.1-57.6-6.1-20.9-22.8-37.4-43.2-42.6-7.6-1.9-11-2-113.4-1.9-84.8 0-106.8.3-111.8 1.4z" /> <path d="M149.3 6.5c-20.9 4.6-40.3 24.4-44.8 46-2.2 10.6-2.2 408.5 0 419 2.4 11.3 7.7 20.9 16.4 29.6 9 9 16.2 13.2 26.7 15.9 7.5 1.9 11.2 2 113.3 2 87.8 0 106.7-.3 112.4-1.5 20-4.2 38.2-21.9 44.4-43.1 1.5-5.2 1.7-12.9 2.1-68.7l.3-62.7h-30l-.3 60.7c-.3 59.8-.3 60.9-2.5 65.5-2.6 5.7-9.4 12.4-15.3 15.2-4.5 2.1-4.9 2.1-110.5 2.1h-106l-5.7-2.8c-3.2-1.6-7.5-4.8-9.7-7.3-7.7-8.8-7.1 9.5-6.9-215.9.3-199 .3-202.1 2.3-206 2.7-5.5 9.7-12.2 15.3-14.8l4.7-2.2h211l5.6 2.6c6.8 3.2 12.4 8.7 15.3 14.8 2 4.5 2.1 6.1 2.4 54.3l.3 49.8h30l-.3-51.8c-.4-45.1-.7-52.5-2.1-57.6-6.1-20.9-22.8-37.4-43.2-42.6-7.6-1.9-11-2-113.4-1.9-84.8 0-106.8.3-111.8 1.4" />
<path d="M426 188.7c-.8.3-2.5 1.5-3.7 2.6-2.2 1.9-2.3 2.8-2.3 16.8 0 8.1-.3 15.4-.6 16.3-.6 1.4-9.5 1.6-91.3 1.6-56.9 0-91.9.4-94.2 1-1.9.6-4.6 2.2-6 3.6-2.3 2.5-2.4 3.2-2.7 19.1-.4 18 .2 21 4.7 24 2.4 1.7 8 1.8 95.6 2.1 72.2.2 93.1.5 93.7 1.5.4.6.8 7.8.8 15.9 0 8 .3 15.4.6 16.3 1.6 4.1 11.6 6.1 17 3.2 5.2-2.7 67.2-56.1 68.4-58.9.7-1.5 1-3.8.6-5.2-.7-2.9-65-57.5-69.9-59.3-3.4-1.2-8.2-1.5-10.7-.6z" /> <path d="M426 188.7c-.8.3-2.5 1.5-3.7 2.6-2.2 1.9-2.3 2.8-2.3 16.8 0 8.1-.3 15.4-.6 16.3-.6 1.4-9.5 1.6-91.3 1.6-56.9 0-91.9.4-94.2 1-1.9.6-4.6 2.2-6 3.6-2.3 2.5-2.4 3.2-2.7 19.1-.4 18 .2 21 4.7 24 2.4 1.7 8 1.8 95.6 2.1 72.2.2 93.1.5 93.7 1.5.4.6.8 7.8.8 15.9 0 8 .3 15.4.6 16.3 1.6 4.1 11.6 6.1 17 3.2 5.2-2.7 67.2-56.1 68.4-58.9.7-1.5 1-3.8.6-5.2-.7-2.9-65-57.5-69.9-59.3-3.4-1.2-8.2-1.5-10.7-.6" />
</g> </g>
</svg> </svg>
); );

View File

@@ -21,7 +21,7 @@ const Experience = ({
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<g fill="currentColor"> <g fill="currentColor">
<path d="M24.2 73.7c14 18.5 27.2 42.3 30.4 55.3 2.8 11.1 1.5 23.8-6.1 58.5-15 69-16.8 93.2-7.9 111.3 8.7 17.8 45 48.1 121.5 101.4 23.5 16.4 84.8 56.8 86.2 56.8.4 0 .6-64.2.5-142.6l-.3-142.6-7-2.2c-25.3-8-38.8-24.2-41.1-49-.8-8.6 1.4-20.7 5.2-29.4 1.3-3 2.1-5.9 1.7-6.3-.5-.4-9-4.6-19-9.3L170.1 67h-151l5.1 6.7zM334.3 76c-10.1 4.9-18.3 9.1-18.3 9.3 0 .3 1.1 2.9 2.4 5.8 3.7 8.5 5.1 15.7 5 25.9-.1 16.1-4.7 27.6-15 37.7-6.9 6.8-18.7 13.3-28.1 15.4l-5.3 1.2v142.9c0 78.5.3 142.8.8 142.8.4 0 11.8-7.3 25.4-16.3C409.9 369.3 469 323.6 482.5 300.6c9.9-16.9 8.4-41.8-6.6-111.1-8.5-39-9.6-50.6-5.9-62.6 4.3-14.5 13.5-31.3 27-49.5 3.9-5.2 7-9.6 7-9.9 0-.3-34.1-.5-75.7-.4l-75.8.1-18.2 8.8z" /> <path d="M24.2 73.7c14 18.5 27.2 42.3 30.4 55.3 2.8 11.1 1.5 23.8-6.1 58.5-15 69-16.8 93.2-7.9 111.3 8.7 17.8 45 48.1 121.5 101.4 23.5 16.4 84.8 56.8 86.2 56.8.4 0 .6-64.2.5-142.6l-.3-142.6-7-2.2c-25.3-8-38.8-24.2-41.1-49-.8-8.6 1.4-20.7 5.2-29.4 1.3-3 2.1-5.9 1.7-6.3-.5-.4-9-4.6-19-9.3L170.1 67h-151zM334.3 76c-10.1 4.9-18.3 9.1-18.3 9.3 0 .3 1.1 2.9 2.4 5.8 3.7 8.5 5.1 15.7 5 25.9-.1 16.1-4.7 27.6-15 37.7-6.9 6.8-18.7 13.3-28.1 15.4l-5.3 1.2v142.9c0 78.5.3 142.8.8 142.8.4 0 11.8-7.3 25.4-16.3C409.9 369.3 469 323.6 482.5 300.6c9.9-16.9 8.4-41.8-6.6-111.1-8.5-39-9.6-50.6-5.9-62.6 4.3-14.5 13.5-31.3 27-49.5 3.9-5.2 7-9.6 7-9.9s-34.1-.5-75.7-.4l-75.8.1z" />
</g> </g>
</svg> </svg>
); );

View File

@@ -21,7 +21,7 @@ const FullscreenOff = ({
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<g fill="currentColor"> <g fill="currentColor">
<path d="M29.3 20c-1.3.5-3.2 2.4-4.3 4.2-2 3.2-2 5-2 85.6V192l3.4 3.7c3.2 3.5 3.6 3.7 10 3.7 6.2 0 6.9-.3 9.9-3.2l3.2-3.2.2-69.8c.2-52.4.6-70.3 1.5-72 2.8-5.3 1.4-5.2 75.9-5.2h69.1l3.4-3.4c3.2-3.2 3.4-3.8 3.4-10.4 0-6.8-.1-7.1-3.5-10.1l-3.6-3.1-82.2.1c-46.4 0-83.2.4-84.4.9zM323.7 22.1c-5.5 4.2-5.8 16-.6 21.1l2.7 2.8h67.9c43.4 0 69.1.4 71.5 1 2 .6 4.6 2.2 5.8 3.7 2 2.5 2 3.6 2 71.5 0 75.9-.2 73.1 6.2 76.4 4.9 2.5 14.1 1.5 17.6-1.9l2.7-2.7.3-83.5.2-83.4-3.1-3.5-3.1-3.6H410c-82.9 0-83.8 0-86.3 2.1zM26.5 320.9c-1.1.5-2.9 1.9-4 3.1-2 2.2-2 3.8-2.3 85.6l-.2 83.3 3.1 3.5 3.1 3.6H110c82.9 0 83.8 0 86.3-2.1 5.5-4.2 5.8-16 .6-21.1l-2.7-2.8h-67.9c-43.4 0-69.1-.4-71.5-1-2-.6-4.6-2.2-5.8-3.7-2-2.5-2-3.6-2-71.5 0-75.6.2-73.1-6-76.3-3.1-1.6-11.2-1.9-14.5-.6zM477.2 323.4l-3.7 3.4-.5 71.1c-.6 79.2 0 73.4-7.6 75.1-2.1.5-34.3 1-71.5 1h-67.7l-3.1 3.5c-2.7 3.1-3.1 4.3-3.1 9.5s.4 6.4 3.1 9.5l3.1 3.5h168l2.9-2.9 2.9-2.9V326.8l-3.4-3.4c-3.1-3.1-3.9-3.4-9.5-3.4s-6.5.3-9.9 3.4z" /> <path d="M29.3 20c-1.3.5-3.2 2.4-4.3 4.2-2 3.2-2 5-2 85.6V192l3.4 3.7c3.2 3.5 3.6 3.7 10 3.7 6.2 0 6.9-.3 9.9-3.2l3.2-3.2.2-69.8c.2-52.4.6-70.3 1.5-72 2.8-5.3 1.4-5.2 75.9-5.2h69.1l3.4-3.4c3.2-3.2 3.4-3.8 3.4-10.4 0-6.8-.1-7.1-3.5-10.1l-3.6-3.1-82.2.1c-46.4 0-83.2.4-84.4.9M323.7 22.1c-5.5 4.2-5.8 16-.6 21.1l2.7 2.8h67.9c43.4 0 69.1.4 71.5 1 2 .6 4.6 2.2 5.8 3.7 2 2.5 2 3.6 2 71.5 0 75.9-.2 73.1 6.2 76.4 4.9 2.5 14.1 1.5 17.6-1.9l2.7-2.7.3-83.5.2-83.4-3.1-3.5-3.1-3.6H410c-82.9 0-83.8 0-86.3 2.1M26.5 320.9c-1.1.5-2.9 1.9-4 3.1-2 2.2-2 3.8-2.3 85.6l-.2 83.3 3.1 3.5 3.1 3.6H110c82.9 0 83.8 0 86.3-2.1 5.5-4.2 5.8-16 .6-21.1l-2.7-2.8h-67.9c-43.4 0-69.1-.4-71.5-1-2-.6-4.6-2.2-5.8-3.7-2-2.5-2-3.6-2-71.5 0-75.6.2-73.1-6-76.3-3.1-1.6-11.2-1.9-14.5-.6M477.2 323.4l-3.7 3.4-.5 71.1c-.6 79.2 0 73.4-7.6 75.1-2.1.5-34.3 1-71.5 1h-67.7l-3.1 3.5c-2.7 3.1-3.1 4.3-3.1 9.5s.4 6.4 3.1 9.5l3.1 3.5h168l2.9-2.9 2.9-2.9V326.8l-3.4-3.4c-3.1-3.1-3.9-3.4-9.5-3.4s-6.5.3-9.9 3.4" />
</g> </g>
</svg> </svg>
); );

View File

@@ -21,7 +21,7 @@ const FullscreenOn = ({
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<g fill="currentColor"> <g fill="currentColor">
<path d="M185.3 20c-1.8.4-4 1.4-4.9 2.1-4.3 3.6-4.4 4.5-4.4 76.3v68.5l-2.4 2.8-2.4 2.8-71.2.5-71.2.5-2.9 3.3c-2.6 2.9-2.9 4-2.9 9.7 0 5.7.3 6.8 2.9 9.7l2.9 3.3h168.4l2.9-3.3 2.9-3.2v-83.3c0-89.3.2-86-4.8-88.6-2.7-1.4-9.4-2-12.9-1.1zM328 20c-1.9.5-4.5 2-5.7 3.5l-2.3 2.6v167.1l3.4 3.4 3.4 3.4 83.7-.2 83.7-.3 3-3.4c2.8-3.2 3-3.7 2.6-10.2-.3-5.9-.8-7.3-3.1-9.7l-2.7-2.7-70.9-.5-70.9-.5-2.6-2.4-2.5-2.4-.3-70.1c-.3-64.9-.4-70.4-2-72.9-1-1.4-2.6-3-3.5-3.5-2.9-1.5-9.5-2.1-13.3-1.2zM26.4 321c-1.2.4-3.1 2.1-4.4 3.7-1.9 2.4-2.1 3.7-1.8 9.6.3 5.7.8 7.1 3.1 9.5l2.7 2.7 70.9.5 70.9.5 2.6 2.4 2.6 2.4v67.6c0 37.2.5 69.4 1 71.5.5 2.1 1.7 4.8 2.8 5.8 4.6 4.7 16.7 4.2 21-.7l2.2-2.6V326.8l-3.4-3.4-3.4-3.4-82.3.1c-45.3 0-83.4.4-84.5.9zM326.3 321c-1.3.5-3.2 2.4-4.3 4.2-2 3.2-2 5-2 85.6V493l3.4 3.7c3.2 3.5 3.6 3.7 10 3.7 6.2 0 6.9-.3 9.9-3.2l3.2-3.2.2-69.8c.2-52.4.6-70.3 1.5-72 2.8-5.3 1.4-5.2 75.9-5.2h69.1l3.4-3.4c3.2-3.2 3.4-3.8 3.4-10.4 0-6.8-.1-7.1-3.5-10.1l-3.6-3.1-82.2.1c-46.4 0-83.2.4-84.4.9z" /> <path d="M185.3 20c-1.8.4-4 1.4-4.9 2.1-4.3 3.6-4.4 4.5-4.4 76.3v68.5l-2.4 2.8-2.4 2.8-71.2.5-71.2.5-2.9 3.3c-2.6 2.9-2.9 4-2.9 9.7s.3 6.8 2.9 9.7l2.9 3.3h168.4l2.9-3.3 2.9-3.2v-83.3c0-89.3.2-86-4.8-88.6-2.7-1.4-9.4-2-12.9-1.1M328 20c-1.9.5-4.5 2-5.7 3.5l-2.3 2.6v167.1l3.4 3.4 3.4 3.4 83.7-.2 83.7-.3 3-3.4c2.8-3.2 3-3.7 2.6-10.2-.3-5.9-.8-7.3-3.1-9.7l-2.7-2.7-70.9-.5-70.9-.5-2.6-2.4-2.5-2.4-.3-70.1c-.3-64.9-.4-70.4-2-72.9-1-1.4-2.6-3-3.5-3.5-2.9-1.5-9.5-2.1-13.3-1.2M26.4 321c-1.2.4-3.1 2.1-4.4 3.7-1.9 2.4-2.1 3.7-1.8 9.6.3 5.7.8 7.1 3.1 9.5l2.7 2.7 70.9.5 70.9.5 2.6 2.4 2.6 2.4v67.6c0 37.2.5 69.4 1 71.5s1.7 4.8 2.8 5.8c4.6 4.7 16.7 4.2 21-.7l2.2-2.6V326.8l-3.4-3.4-3.4-3.4-82.3.1c-45.3 0-83.4.4-84.5.9M326.3 321c-1.3.5-3.2 2.4-4.3 4.2-2 3.2-2 5-2 85.6V493l3.4 3.7c3.2 3.5 3.6 3.7 10 3.7 6.2 0 6.9-.3 9.9-3.2l3.2-3.2.2-69.8c.2-52.4.6-70.3 1.5-72 2.8-5.3 1.4-5.2 75.9-5.2h69.1l3.4-3.4c3.2-3.2 3.4-3.8 3.4-10.4 0-6.8-.1-7.1-3.5-10.1l-3.6-3.1-82.2.1c-46.4 0-83.2.4-84.4.9" />
</g> </g>
</svg> </svg>
); );

View File

@@ -21,8 +21,8 @@ const Info = ({
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<g fill="currentColor"> <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-1zM294 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.9z" /> <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.2.1-1 .2-9.9.3-19.8.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-6zM252.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.6z" /> <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> </g>
</svg> </svg>
); );

View File

@@ -20,8 +20,8 @@ const FivePlayers = ({
{...props} {...props}
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zM396 269v221H57V409.3l11.3-.6c18.4-1 32.5-6 42.3-15C125.7 380 138.1 334 138.1 292c0-23.9-3.5-47.1-10.7-71-9.5-31.4-25.9-44.2-58.8-45.7l-11.9-.6.6-11.6c1.1-18.8 4.8-33.4 12.7-49.2C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48v221zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.2 6.9 1.5 19.8 1.5 68.3v59.7l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6v353.4l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6v59.7c0 65.6-.2 69-6.1 86.4-13.7 41-49.7 71.9-92.9 79.7-6.1 1.1-29.8 1.4-123.5 1.4h-116l-.3-638.8-.2-638.8 118.7.4 118.8.3 9 2.3zM396 692v192H57V805.3l11.3-.6c18.4-1 32.5-6 42.3-15C125.7 776 138.1 730 138.1 688c0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V500h339v192zm0 417.5V1326H285.3c-63.3 0-115-.5-120.7-1-32.5-3.2-63-20.2-83-46.5-15-19.7-24.6-47.5-24.6-71.6v-6.6l11.3-.6c18.4-1 32.5-6 42.3-15 15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V893h339v216.5z" /> <path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7M396 269v221H57v-80.7l11.3-.6q27.6-1.5 42.3-15C125.7 380 138.1 334 138.1 292c0-23.9-3.5-47.1-10.7-71-9.5-31.4-25.9-44.2-58.8-45.7l-11.9-.6.6-11.6c1.1-18.8 4.8-33.4 12.7-49.2C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.2 6.9 1.5 19.8 1.5 68.3v59.7l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6v353.4l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6v59.7c0 65.6-.2 69-6.1 86.4-13.7 41-49.7 71.9-92.9 79.7-6.1 1.1-29.8 1.4-123.5 1.4h-116l-.3-638.8-.2-638.8 118.7.4 118.8.3zM396 692v192H57v-78.7l11.3-.6q27.6-1.5 42.3-15C125.7 776 138.1 730 138.1 688c0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V500h339zm0 417.5V1326H285.3c-63.3 0-115-.5-120.7-1-32.5-3.2-63-20.2-83-46.5-15-19.7-24.6-47.5-24.6-71.6v-6.6l11.3-.6q27.6-1.5 42.3-15c15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V893h339z" />
<path d="M195 236.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM574.1 340.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM574.1 928.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM195 632.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM195 1027.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 4-19-1.3-36.4-15.6-50.7-11.5-11.6-24.1-16.8-40.2-16.6-4 0-9.7.7-12.7 1.5z" /> <path d="M195 236.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M574.1 340.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M574.1 928.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M195 632.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M195 1027.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 4-19-1.3-36.4-15.6-50.7-11.5-11.6-24.1-16.8-40.2-16.6-4 0-9.7.7-12.7 1.5" />
</svg> </svg>
); );
}; };

View File

@@ -20,8 +20,8 @@ const FivePlayersSide = ({
{...props} {...props}
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zM396 285.5V523H57V418.3l9.8-.6c17.2-1.2 31.4-6.3 40.8-15 8.8-8 16-24.7 21.9-51.2 7.6-33.9 7.6-68.2-.1-102-5.7-25.6-11.3-39-19.9-48.3-9.3-10.1-23.7-15.9-42.5-17.1l-9.5-.6.1-15c.1-21.7 3.3-36.1 12.1-54 14.1-28.4 39.2-50.1 70-60.5 17.7-5.9 15.3-5.8 141.1-5.9L396 48v237.5zM651.5 50.9c36 9.4 63.5 30.9 79.4 62 9.1 17.9 13.1 35 13.1 56.6v13.2l-9.4.7c-18 1.2-33.9 7.2-42.3 15.9-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.1 10.1 22.9 15.1 41.4 16.1l10.2.6V523H405V47.9l118.8.4 118.7.3 9 2.3zM395.8 767.2l.2 234.8H57V872.3l11.8-.6c18.9-1 32.8-5.9 42.8-15C126.7 843 139.1 797 139.1 755c0-23.9-3.5-47.1-10.7-71-9.5-31.5-25.9-44.2-59.1-45.7l-12.3-.6V532l169.3.2 169.2.3.3 234.7zM744 584.4v52.3l-9.4.7c-18 1.2-33.9 7.2-42.3 15.9-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.1 10.1 22.9 15.1 41.4 16.1l10.2.6V1002H405V532h339v52.4zm0 530c0 113.4.1 111.6-6.1 130-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.6-10.3c-.7-11.8-4-24.6-8.3-32.3-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-19.1 9-28.7 25.4-30.4 52l-.7 10-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-4.9-18.3-5-19.6-5-127.7V1011h687v103.4z" /> <path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7M396 285.5V523H57V418.3l9.8-.6c17.2-1.2 31.4-6.3 40.8-15 8.8-8 16-24.7 21.9-51.2 7.6-33.9 7.6-68.2-.1-102-5.7-25.6-11.3-39-19.9-48.3-9.3-10.1-23.7-15.9-42.5-17.1l-9.5-.6.1-15c.1-21.7 3.3-36.1 12.1-54 14.1-28.4 39.2-50.1 70-60.5 17.7-5.9 15.3-5.8 141.1-5.9L396 48zM651.5 50.9c36 9.4 63.5 30.9 79.4 62 9.1 17.9 13.1 35 13.1 56.6v13.2l-9.4.7c-18 1.2-33.9 7.2-42.3 15.9-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.1 10.1 22.9 15.1 41.4 16.1l10.2.6V523H405V47.9l118.8.4 118.7.3zM395.8 767.2l.2 234.8H57V872.3l11.8-.6c18.9-1 32.8-5.9 42.8-15C126.7 843 139.1 797 139.1 755c0-23.9-3.5-47.1-10.7-71-9.5-31.5-25.9-44.2-59.1-45.7l-12.3-.6V532l169.3.2 169.2.3zM744 584.4v52.3l-9.4.7c-18 1.2-33.9 7.2-42.3 15.9-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.1 10.1 22.9 15.1 41.4 16.1l10.2.6V1002H405V532h339zm0 530c0 113.4.1 111.6-6.1 130-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.6-10.3c-.7-11.8-4-24.6-8.3-32.3-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-19.1 9-28.7 25.4-30.4 52l-.7 10-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-4.9-18.3-5-19.6-5-127.7V1011h687z" />
<path d="M192 245.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM578.1 247.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM196 699.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM578.1 701.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM388.3 1121.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7z" /> <path d="M192 245.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M578.1 247.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M196 699.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M578.1 701.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M388.3 1121.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7" />
</svg> </svg>
); );
}; };

View File

@@ -20,8 +20,8 @@ const FourPlayers = ({
{...props} {...props}
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zM396 365v317H57V511.3l11.3-.6c18.4-1 32.5-6 42.3-15C125.7 482 138.1 436 138.1 394c0-23.9-3.5-47.1-10.7-71-9.4-31.4-25.9-44.2-58.7-45.7l-11.8-.6.4-62.6c.3-59.6.4-63 2.5-71.6 2.8-11.9 5.1-18.4 10.2-28.6C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48v317zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.2 6.9 1.5 19.8 1.5 68.3v59.7l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6V682H405V47.9l118.8.4 118.7.3 9 2.3zM396 1009v317H285.3c-63.3 0-115-.5-120.7-1-32.5-3.2-63-20.2-83-46.5-7.7-10.1-16.6-27.7-19.6-38.6-4.6-17.2-5-22.8-5-83.5v-57.1l11.3-.6c18.4-1 32.5-6 42.3-15 15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V692h339v317zm348-231.2v85.9l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6v59.7c0 65.6-.2 69-6.1 86.4-13.7 41-49.7 71.9-92.9 79.7-6.1 1.1-29.8 1.4-123.5 1.4h-116l-.3-316.8L405 692h339v85.8z" /> <path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7M396 365v317H57V511.3l11.3-.6q27.6-1.5 42.3-15C125.7 482 138.1 436 138.1 394c0-23.9-3.5-47.1-10.7-71-9.4-31.4-25.9-44.2-58.7-45.7l-11.8-.6.4-62.6c.3-59.6.4-63 2.5-71.6 2.8-11.9 5.1-18.4 10.2-28.6C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.2 6.9 1.5 19.8 1.5 68.3v59.7l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6V682H405V47.9l118.8.4 118.7.3zM396 1009v317H285.3c-63.3 0-115-.5-120.7-1-32.5-3.2-63-20.2-83-46.5-7.7-10.1-16.6-27.7-19.6-38.6-4.6-17.2-5-22.8-5-83.5v-57.1l11.3-.6q27.6-1.5 42.3-15c15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V692h339zm348-231.2v85.9l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6v59.7c0 65.6-.2 69-6.1 86.4-13.7 41-49.7 71.9-92.9 79.7-6.1 1.1-29.8 1.4-123.5 1.4h-116l-.3-316.8L405 692h339z" />
<path d="M195 338.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM574.1 340.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM195 926.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM574.1 928.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2z" /> <path d="M195 338.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M574.1 340.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M195 926.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M574.1 928.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2" />
</svg> </svg>
); );
}; };

View File

@@ -20,8 +20,8 @@ const FourPlayersSide = ({
{...props} {...props}
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zm124 41.2c.1 21 5.7 36.8 16.7 47.4 14.3 13.6 60.7 25.4 100.3 25.4 23.7 0 42.4-2.7 66.7-9.7 23.4-6.7 35.7-15 42.3-28.8 5-10.6 6.6-17.6 7.2-32.9l.6-13.8 62.1.3c61.6.3 62.2.3 71.6 2.7 48.7 12.2 83.1 48.6 91.5 96.8 1.3 7.3 1.5 26.3 1.5 124.9V389l-343.2-.2-343.3-.3V271c0-109.1.1-118.1 1.8-126 9.2-43.6 38.7-77 80.4-91 16.6-5.5 20.3-5.8 84.8-5.9l59-.1v12.3zM396 687v289H57V805.3l12.8-.6c19.8-.9 33.6-5.6 43.8-15C128.7 776 141.1 730 141.1 688c0-23.9-3.5-47.1-10.7-71-9.6-31.9-26-44.4-60.1-45.7l-13.3-.6V398h339v289zm348-202.8v86.3h-12.3c-14.9.1-24.8 2.2-35.5 7.5-13.1 6.6-19.9 15.8-26.6 36-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 11.1 11 23.1 15.3 45.4 16.1l14.2.6V976H405V398h339v86.2zm0 617.2c0 127.7.1 124.4-6.1 143-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.7-8.6c-.8-10.3-4.3-23-8.2-30-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-18.5 8.7-28.1 24.5-30.3 49.6l-.8 8.4-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-5-18.4-5-18.6-5-140.7V985h687v116.4z" /> <path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7m124 41.2c.1 21 5.7 36.8 16.7 47.4 14.3 13.6 60.7 25.4 100.3 25.4 23.7 0 42.4-2.7 66.7-9.7 23.4-6.7 35.7-15 42.3-28.8 5-10.6 6.6-17.6 7.2-32.9l.6-13.8 62.1.3c61.6.3 62.2.3 71.6 2.7 48.7 12.2 83.1 48.6 91.5 96.8 1.3 7.3 1.5 26.3 1.5 124.9V389l-343.2-.2-343.3-.3V271c0-109.1.1-118.1 1.8-126 9.2-43.6 38.7-77 80.4-91 16.6-5.5 20.3-5.8 84.8-5.9l59-.1zM396 687v289H57V805.3l12.8-.6c19.8-.9 33.6-5.6 43.8-15C128.7 776 141.1 730 141.1 688c0-23.9-3.5-47.1-10.7-71-9.6-31.9-26-44.4-60.1-45.7l-13.3-.6V398h339zm348-202.8v86.3h-12.3c-14.9.1-24.8 2.2-35.5 7.5-13.1 6.6-19.9 15.8-26.6 36-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 11.1 11 23.1 15.3 45.4 16.1l14.2.6V976H405V398h339zm0 617.2c0 127.7.1 124.4-6.1 143-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.7-8.6c-.8-10.3-4.3-23-8.2-30-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-18.5 8.7-28.1 24.5-30.3 49.6l-.8 8.4-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-5-18.4-5-18.6-5-140.7V985h687z" />
<path d="M391.8 148c-16.5 3-30 12.2-39.1 26.7-7.9 12.6-10 31-5.2 46.1 4.5 14.4 15.5 26.9 29.1 33.4 22.1 10.4 46.1 6.2 63.5-11.1 11.5-11.5 16.9-24.2 16.9-39.9 0-23.4-16.1-45.5-38.6-53.1-7.1-2.3-19.8-3.4-26.6-2.1zM198 632.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM570.1 634.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM388.3 1125.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7z" /> <path d="M391.8 148c-16.5 3-30 12.2-39.1 26.7-7.9 12.6-10 31-5.2 46.1 4.5 14.4 15.5 26.9 29.1 33.4 22.1 10.4 46.1 6.2 63.5-11.1 11.5-11.5 16.9-24.2 16.9-39.9 0-23.4-16.1-45.5-38.6-53.1-7.1-2.3-19.8-3.4-26.6-2.1M198 632.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M570.1 634.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M388.3 1125.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7" />
</svg> </svg>
); );
}; };

View File

@@ -20,8 +20,8 @@ const OnePlayerLandscape = ({
{...props} {...props}
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zm492 31.8c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.5 1.5 73.4 1.5 539.3 0 474.6-.2 531.7-1.5 539.6-8.5 48.4-45 85.9-94.5 97.1-6.9 1.6-26.1 1.7-239 2-154.7.3-235.3 0-243-.7-20.8-1.8-38.3-8-55.8-19.7-28.1-18.7-46.6-48.2-51.8-82.8-1.1-7.4-1.4-45.9-1.4-213.6V804.3l9.8-.6c17.2-1.2 31.4-6.3 40.8-15 8.8-8 16-24.7 21.9-51.2 7.6-33.9 7.6-68.2-.1-102-5.7-25.6-11.3-39-19.9-48.3-9.3-10.1-23.7-15.9-42.5-17.1l-9.5-.6v-208c0-196.2.1-208.5 1.8-216.5 9.2-43.6 38.7-77 80.4-91 18.5-6.2 3.5-5.8 264.3-5.6l238.5.1 9 2.4z" /> <path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7m492 31.8c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.5 1.5 73.4 1.5 539.3 0 474.6-.2 531.7-1.5 539.6-8.5 48.4-45 85.9-94.5 97.1-6.9 1.6-26.1 1.7-239 2-154.7.3-235.3 0-243-.7-20.8-1.8-38.3-8-55.8-19.7-28.1-18.7-46.6-48.2-51.8-82.8-1.1-7.4-1.4-45.9-1.4-213.6V804.3l9.8-.6c17.2-1.2 31.4-6.3 40.8-15 8.8-8 16-24.7 21.9-51.2 7.6-33.9 7.6-68.2-.1-102-5.7-25.6-11.3-39-19.9-48.3-9.3-10.1-23.7-15.9-42.5-17.1l-9.5-.6v-208c0-196.2.1-208.5 1.8-216.5 9.2-43.6 38.7-77 80.4-91 18.5-6.2 3.5-5.8 264.3-5.6l238.5.1z" />
<path d="M192 631.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5z" /> <path d="M192 631.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5" />
</svg> </svg>
); );
}; };

View File

@@ -20,8 +20,8 @@ const OnePlayerPortrait = ({
{...props} {...props}
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zm492 31.8c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.5 1.5 73.4 1.5 539.3 0 474.6-.2 531.7-1.5 539.6-8.5 48.4-45.4 86.3-94.5 97-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.7-9c-.8-10.6-4.3-23.4-8.2-30.6-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-18.6 8.8-28.2 24.8-30.4 50.2l-.7 8.8-53 .3c-30.9.2-57.8-.2-64.5-.8-56.5-5-101.1-48.5-108-105.2-.8-6.8-1-151.9-.8-538.8l.3-529.5 2.2-9c2.9-12 5.2-18.5 10.3-28.6C83.9 86 109.2 64.3 139.7 54c18.5-6.2 3.5-5.8 264.3-5.6l238.5.1 9 2.4z" /> <path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7m492 31.8c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.5 1.5 73.4 1.5 539.3 0 474.6-.2 531.7-1.5 539.6-8.5 48.4-45.4 86.3-94.5 97-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.7-9c-.8-10.6-4.3-23.4-8.2-30.6-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-18.6 8.8-28.2 24.8-30.4 50.2l-.7 8.8-53 .3c-30.9.2-57.8-.2-64.5-.8-56.5-5-101.1-48.5-108-105.2-.8-6.8-1-151.9-.8-538.8l.3-529.5 2.2-9c2.9-12 5.2-18.5 10.3-28.6C83.9 86 109.2 64.3 139.7 54c18.5-6.2 3.5-5.8 264.3-5.6l238.5.1z" />
<path d="M388.3 1124.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7z" /> <path d="M388.3 1124.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7" />
</svg> </svg>
); );
}; };

View File

@@ -20,8 +20,8 @@ const SixPlayers = ({
{...props} {...props}
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zM396 266.5V485l-169.2-.2-169.3-.3-.3-37.6-.2-37.6 11.2-.6c18.5-1 32.6-6 42.4-15C125.7 380 138.1 334 138.1 292c0-23.9-3.5-47.1-10.7-71-9.5-31.4-25.9-44.2-58.8-45.7l-11.9-.6.6-11.6c1.1-18.8 4.8-33.4 12.7-49.2C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48v218.5zM651.5 50.9c36 9.4 63.5 30.9 79.4 62C739.3 129.4 744 148 744 165v8.7l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6V485l-169.2-.2-169.3-.3-.3-218.3-.2-218.3 118.7.4 118.8.3 9 2.3zM396 687v193H57V805.3l11.3-.6c18.4-1 32.5-6 42.3-15C125.7 776 138.1 730 138.1 688c0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V494h339v193zm348-155.2v37.9l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6V880H405V494h339v37.8zm-348 575.7V1326H285.3c-63.3 0-115-.5-120.7-1-32.5-3.2-63-20.2-83-46.5-15-19.7-24.6-47.5-24.6-71.6v-6.6l11.3-.6c18.4-1 32.5-6 42.3-15 15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V889h339v218.5zm348-180.7v37.9l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6v9.2c0 55.8-42.5 105.3-99 115.6-6.1 1.1-29.8 1.4-123.5 1.4h-116l-.3-217c-.1-119.4 0-217.6.3-218.3.3-.9 35.2-1.2 169.5-1.2h169v37.8z" /> <path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7M396 266.5V485l-169.2-.2-169.3-.3-.3-37.6-.2-37.6 11.2-.6c18.5-1 32.6-6 42.4-15C125.7 380 138.1 334 138.1 292c0-23.9-3.5-47.1-10.7-71-9.5-31.4-25.9-44.2-58.8-45.7l-11.9-.6.6-11.6c1.1-18.8 4.8-33.4 12.7-49.2C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48zM651.5 50.9c36 9.4 63.5 30.9 79.4 62C739.3 129.4 744 148 744 165v8.7l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6V485l-169.2-.2-169.3-.3-.3-218.3-.2-218.3 118.7.4 118.8.3zM396 687v193H57v-74.7l11.3-.6q27.6-1.5 42.3-15C125.7 776 138.1 730 138.1 688c0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V494h339zm348-155.2v37.9l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6V880H405V494h339zm-348 575.7V1326H285.3c-63.3 0-115-.5-120.7-1-32.5-3.2-63-20.2-83-46.5-15-19.7-24.6-47.5-24.6-71.6v-6.6l11.3-.6q27.6-1.5 42.3-15c15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V889h339zm348-180.7v37.9l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6v9.2c0 55.8-42.5 105.3-99 115.6-6.1 1.1-29.8 1.4-123.5 1.4h-116l-.3-217c-.1-119.4 0-217.6.3-218.3.3-.9 35.2-1.2 169.5-1.2h169z" />
<path d="M195 236.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM574.1 238.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM195 632.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM574.1 634.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM195 1027.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 4-19-1.3-36.4-15.6-50.7-11.5-11.6-24.1-16.8-40.2-16.6-4 0-9.7.7-12.7 1.5zM574.1 1029.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2z" /> <path d="M195 236.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M574.1 238.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M195 632.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M574.1 634.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M195 1027.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 4-19-1.3-36.4-15.6-50.7-11.5-11.6-24.1-16.8-40.2-16.6-4 0-9.7.7-12.7 1.5M574.1 1029.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2" />
</svg> </svg>
); );
}; };

View File

@@ -20,8 +20,8 @@ const SixPlayersSide = ({
{...props} {...props}
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zm124 42.1c0 22 5.5 37.7 16.7 48.5 14.3 13.6 60.7 25.4 100.3 25.4 23.7 0 42.4-2.7 66.7-9.7 23.4-6.7 35.7-15 42.3-28.8 5.2-10.9 6.6-17.5 7.2-33.9l.6-14.8 62.1.3c61.6.3 62.2.3 71.6 2.7 48.7 12.2 83.1 48.6 91.5 96.8 1.2 7 1.5 20.5 1.5 73.9V287H56.9l.4-67.8c.3-64.8.4-68.1 2.5-76.7 2.8-11.9 5.1-18.4 10.2-28.6C83.9 86 109.2 64.3 139.7 54c16.6-5.5 20.3-5.8 84.8-5.9l59-.1v13.2zM396 489v193H57v-42.7l9.8-.6c17.2-1.2 31.4-6.3 40.8-15 8.8-8 16-24.7 21.9-51.2 7.6-33.9 7.6-68.2-.1-102-5.7-25.6-11.3-39-19.9-48.3-9.3-10.1-23.7-15.9-42.5-17.1l-9.5-.6-.3-54.3L57 296h339v193zm348-139.1v53.8l-9.4.7c-18 1.2-33.9 7.2-42.3 15.9-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.1 10.1 22.9 15.1 41.4 16.1l10.2.6V682H405V296h339v53.9zM395.8 880.2l-.3 188.3-169.2.3-169.3.2V985.3l11.8-.6c18.9-1 32.8-5.9 42.8-15C126.7 956 139.1 910 139.1 868c0-23.9-3.5-47.1-10.7-71-9.5-31.5-25.9-44.2-59.1-45.7l-12.3-.6V692h339l-.2 188.2zM744 720.9v28.8l-9.4.7c-18 1.2-33.9 7.2-42.3 15.9-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.1 10.1 22.9 15.1 41.4 16.1l10.2.6V1069H405V692h339v28.9zm0 427c0 76.6-.1 78.7-6.1 96.5-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.6-10.3c-.7-11.8-4-24.6-8.3-32.3-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-19.1 9-28.7 25.4-30.4 52l-.7 10-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-4.7-17.6-5-22.1-5-94.2V1078h687v69.9z" /> <path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7m124 42.1c0 22 5.5 37.7 16.7 48.5 14.3 13.6 60.7 25.4 100.3 25.4 23.7 0 42.4-2.7 66.7-9.7 23.4-6.7 35.7-15 42.3-28.8 5.2-10.9 6.6-17.5 7.2-33.9l.6-14.8 62.1.3c61.6.3 62.2.3 71.6 2.7 48.7 12.2 83.1 48.6 91.5 96.8 1.2 7 1.5 20.5 1.5 73.9V287H56.9l.4-67.8c.3-64.8.4-68.1 2.5-76.7 2.8-11.9 5.1-18.4 10.2-28.6C83.9 86 109.2 64.3 139.7 54c16.6-5.5 20.3-5.8 84.8-5.9l59-.1zM396 489v193H57v-42.7l9.8-.6c17.2-1.2 31.4-6.3 40.8-15 8.8-8 16-24.7 21.9-51.2 7.6-33.9 7.6-68.2-.1-102-5.7-25.6-11.3-39-19.9-48.3-9.3-10.1-23.7-15.9-42.5-17.1l-9.5-.6-.3-54.3L57 296h339zm348-139.1v53.8l-9.4.7c-18 1.2-33.9 7.2-42.3 15.9-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.1 10.1 22.9 15.1 41.4 16.1l10.2.6V682H405V296h339zM395.8 880.2l-.3 188.3-169.2.3-169.3.2v-83.7l11.8-.6c18.9-1 32.8-5.9 42.8-15C126.7 956 139.1 910 139.1 868c0-23.9-3.5-47.1-10.7-71-9.5-31.5-25.9-44.2-59.1-45.7l-12.3-.6V692h339zM744 720.9v28.8l-9.4.7c-18 1.2-33.9 7.2-42.3 15.9-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.1 10.1 22.9 15.1 41.4 16.1l10.2.6v84.7H405V692h339zm0 427c0 76.6-.1 78.7-6.1 96.5-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.6-10.3c-.7-11.8-4-24.6-8.3-32.3-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-19.1 9-28.7 25.4-30.4 52l-.7 10-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-4.7-17.6-5-22.1-5-94.2V1078h687z" />
<path d="M391.8 150c-16.5 3-30 12.2-39.1 26.7-7.9 12.6-10 31-5.2 46.1 4.5 14.4 15.5 26.9 29.1 33.4 22.1 10.4 46.1 6.2 63.5-11.1 11.5-11.5 16.9-24.2 16.9-39.9 0-23.4-16.1-45.5-38.6-53.1-7.1-2.3-19.8-3.4-26.6-2.1zM192 466.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM578.1 468.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM196 812.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM578.1 814.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM388.3 1121.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7z" /> <path d="M391.8 150c-16.5 3-30 12.2-39.1 26.7-7.9 12.6-10 31-5.2 46.1 4.5 14.4 15.5 26.9 29.1 33.4 22.1 10.4 46.1 6.2 63.5-11.1 11.5-11.5 16.9-24.2 16.9-39.9 0-23.4-16.1-45.5-38.6-53.1-7.1-2.3-19.8-3.4-26.6-2.1M192 466.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M578.1 468.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M196 812.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M578.1 814.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M388.3 1121.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7" />
</svg> </svg>
); );
}; };

View File

@@ -20,8 +20,8 @@ const ThreePlayers = ({
{...props} {...props}
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zM396 365v317H57V511.3l11.3-.6c18.4-1 32.5-6 42.3-15C125.7 482 138.1 436 138.1 394c0-23.9-3.5-47.1-10.7-71-9.4-31.4-25.9-44.2-58.7-45.7l-11.8-.6.4-62.6c.3-59.6.4-63 2.5-71.6 2.8-11.9 5.1-18.4 10.2-28.6C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48v317zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.4 1.5 36.6 1.5 215.3v206.7l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6V1011c0 227.3.3 214.2-6.1 233.4-13.4 40-47 69.6-89.9 79.3-6.9 1.6-26.1 1.7-239 2-154.7.3-235.3 0-243-.7-20.8-1.8-38.3-8-55.8-19.7-28.1-18.7-46.6-48.2-51.8-82.8-1.1-6.8-1.4-23-1.4-66.1v-57.1l11.3-.6c18.4-1 32.5-6 42.3-15 15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V691h339v634h9V47.9l118.8.4 118.7.3 9 2.3z" /> <path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7M396 365v317H57V511.3l11.3-.6q27.6-1.5 42.3-15C125.7 482 138.1 436 138.1 394c0-23.9-3.5-47.1-10.7-71-9.4-31.4-25.9-44.2-58.7-45.7l-11.8-.6.4-62.6c.3-59.6.4-63 2.5-71.6 2.8-11.9 5.1-18.4 10.2-28.6C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.4 1.5 36.6 1.5 215.3v206.7l-11.2.6c-13.6.8-24.6 3.4-33.7 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.6 10.6 23 15.2 43.4 16.1l12.2.6V1011c0 227.3.3 214.2-6.1 233.4-13.4 40-47 69.6-89.9 79.3-6.9 1.6-26.1 1.7-239 2-154.7.3-235.3 0-243-.7-20.8-1.8-38.3-8-55.8-19.7-28.1-18.7-46.6-48.2-51.8-82.8-1.1-6.8-1.4-23-1.4-66.1v-57.1l11.3-.6q27.6-1.5 42.3-15c15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.6-45.7l-11.8-.6V691h339v634h9V47.9l118.8.4 118.7.3z" />
<path d="M195 338.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM574.1 634.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM195 926.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5z" /> <path d="M195 338.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M574.1 634.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M195 926.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5" />
</svg> </svg>
); );
}; };

View File

@@ -20,8 +20,8 @@ const ThreePlayersSide = ({
{...props} {...props}
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zM397 471.5V895H57V592.3l9.8-.6c17.2-1.2 31.4-6.3 40.8-15 8.8-8 16-24.7 21.9-51.2 7.6-33.9 7.6-68.2-.1-102-5.7-25.6-11.3-39-19.9-48.3-9.3-10.1-23.7-15.9-42.5-17.1l-9.5-.6v-102c0-94.2.1-102.6 1.8-110.5 9.2-43.6 38.7-77 80.4-91 17.7-5.9 15.2-5.8 141.6-5.9L397 48v423.5zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.2 1.5 24.4 1.5 108.8v100.2l-8.2.8c-17.1 1.4-32.5 7.5-40.5 15.8-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 9.7 9.7 22.6 14.9 39.9 16.1l8.7.6V895H407V47.9l117.8.4 117.7.3 9 2.3zm92.5 1010c0 172.4.3 164.6-6.1 183.5-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.7-8.6c-.8-10.3-4.3-23-8.2-30-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-18.5 8.7-28.1 24.5-30.3 49.6l-.8 8.4-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-5.1-18.8-5-15.5-5-181.2V904h687v156.9z" /> <path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7M397 471.5V895H57V592.3l9.8-.6c17.2-1.2 31.4-6.3 40.8-15 8.8-8 16-24.7 21.9-51.2 7.6-33.9 7.6-68.2-.1-102-5.7-25.6-11.3-39-19.9-48.3-9.3-10.1-23.7-15.9-42.5-17.1l-9.5-.6v-102c0-94.2.1-102.6 1.8-110.5 9.2-43.6 38.7-77 80.4-91 17.7-5.9 15.2-5.8 141.6-5.9L397 48zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.2 1.5 24.4 1.5 108.8v100.2l-8.2.8c-17.1 1.4-32.5 7.5-40.5 15.8-5.5 5.6-10.5 15-14.7 27.7-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 9.7 9.7 22.6 14.9 39.9 16.1l8.7.6V895H407V47.9l117.8.4 117.7.3zm92.5 1010c0 172.4.3 164.6-6.1 183.5-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.7-8.6c-.8-10.3-4.3-23-8.2-30-8.3-14.9-24-23.3-57.9-30.9-23.8-5.4-54.3-7.2-76-4.6-21.2 2.5-48 9.3-60.9 15.5-18.5 8.7-28.1 24.5-30.3 49.6l-.8 8.4-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-5.1-18.8-5-15.5-5-181.2V904h687z" />
<path d="M192 419.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM581.1 421.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2zM388.3 1125.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7z" /> <path d="M192 419.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M581.1 421.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2M388.3 1125.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7" />
</svg> </svg>
); );
}; };

View File

@@ -20,8 +20,8 @@ const TwoPlayersOppositeLandscape = ({
{...props} {...props}
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zM396 687v639H285.3c-63.3 0-115-.5-120.7-1-32.5-3.2-63-20.2-83-46.5-7.7-10.1-16.6-27.7-19.6-38.6-5.1-19-5-11.7-5-230.5V805.3l11.3-.6c18.4-1 32.5-6 42.3-15C125.7 776 138.1 730 138.1 688c0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.7-45.7l-11.7-.6.3-209.6.3-209.6 2.1-9c2.9-11.9 5.2-18.4 10.3-28.6C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48v639zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.4 1.5 36.5 1.5 214.8v206.2l-11.7.6c-14 .7-24.9 3.3-34.2 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.7 10.7 23.1 15.2 43.9 16.1l12.7.6v207.2c0 227.9.3 214.7-6.1 233.9-13.7 41-49.7 71.9-92.9 79.7-6.1 1.1-29.8 1.4-123.5 1.4h-116l-.3-638.8-.2-638.8 118.7.4 118.8.3 9 2.3z" /> <path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7M396 687v639H285.3c-63.3 0-115-.5-120.7-1-32.5-3.2-63-20.2-83-46.5-7.7-10.1-16.6-27.7-19.6-38.6-5.1-19-5-11.7-5-230.5V805.3l11.3-.6q27.6-1.5 42.3-15C125.7 776 138.1 730 138.1 688c0-23.9-3.5-47.1-10.7-71-9.4-31.3-25.9-44.2-58.7-45.7l-11.7-.6.3-209.6.3-209.6 2.1-9c2.9-11.9 5.2-18.4 10.3-28.6C83.9 86 109.2 64.3 139.7 54c17.7-5.9 15.3-5.8 141.1-5.9L396 48zM651.5 50.9c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.4 1.5 36.5 1.5 214.8v206.2l-11.7.6c-14 .7-24.9 3.3-34.2 8.2-12.2 6.4-19 15.9-25.5 35.5-13.2 40-15.3 83.2-6 124.8 5.8 25.6 11.8 39.8 20.8 48.8 10.7 10.7 23.1 15.2 43.9 16.1l12.7.6v207.2c0 227.9.3 214.7-6.1 233.9-13.7 41-49.7 71.9-92.9 79.7-6.1 1.1-29.8 1.4-123.5 1.4h-116l-.3-638.8-.2-638.8 118.7.4 118.8.3z" />
<path d="M195 632.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM573.1 633.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2z" /> <path d="M195 632.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M573.1 633.5c-14.3 4.6-27 15.7-33.3 29.1-10.4 22.2-6.2 46.1 11.1 63.5 11.5 11.5 24.2 16.9 39.9 16.9 23.6 0 45.3-15.9 53.3-39 3.1-9.2 3.2-23.7.1-33.7-3.9-12.7-12.9-24.1-24.9-31.6-12.5-7.9-31.4-10-46.2-5.2" />
</svg> </svg>
); );
}; };

View File

@@ -20,8 +20,8 @@ const TwoPlayersOppositePortrait = ({
{...props} {...props}
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zm123.9 39.6c1.2 20.3 6.6 34.2 16.8 44 10.3 9.8 39.8 19.5 72.3 23.8 13.9 1.8 40.1 2.1 53 .5 21.2-2.5 48-9.3 60.9-15.5 19.3-9.2 28.8-25.6 30.3-52.3l.6-11.3 62.1.3c61.6.3 62.2.3 71.6 2.7 48.7 12.2 83.1 48.6 91.5 96.8 1.3 7.4 1.5 43.1 1.5 272.9V685H57l.3-266.8.3-266.7 2.1-9c2.9-12 5.2-18.4 10.3-28.6C83.9 86 109.2 64.3 139.7 54c16.6-5.5 20.4-5.8 84.4-5.9l58.6-.1.7 10.7zM744 955.9c0 288.1.4 269.2-6.1 288.5-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.6-11.3c-1.5-27-10.8-43.5-29.8-52.6-13.9-6.7-36.6-12.7-59.4-15.7-13.9-1.8-40-2.1-53-.5-21.2 2.5-48 9.3-60.9 15.5-19.4 9.2-28.8 25.7-30.5 53l-.6 11-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-5.2-19.2-5-7.3-5-286.2V694h687v261.9z" /> <path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7m123.9 39.6c1.2 20.3 6.6 34.2 16.8 44 10.3 9.8 39.8 19.5 72.3 23.8 13.9 1.8 40.1 2.1 53 .5 21.2-2.5 48-9.3 60.9-15.5 19.3-9.2 28.8-25.6 30.3-52.3l.6-11.3 62.1.3c61.6.3 62.2.3 71.6 2.7 48.7 12.2 83.1 48.6 91.5 96.8 1.3 7.4 1.5 43.1 1.5 272.9V685H57l.3-266.8.3-266.7 2.1-9c2.9-12 5.2-18.4 10.3-28.6C83.9 86 109.2 64.3 139.7 54c16.6-5.5 20.4-5.8 84.4-5.9l58.6-.1zM744 955.9c0 288.1.4 269.2-6.1 288.5-13.4 40-47.4 70-89.9 79.2-6.3 1.4-17 1.7-68.6 2.1l-61.1.4-.6-11.3c-1.5-27-10.8-43.5-29.8-52.6-13.9-6.7-36.6-12.7-59.4-15.7-13.9-1.8-40-2.1-53-.5-21.2 2.5-48 9.3-60.9 15.5-19.4 9.2-28.8 25.7-30.5 53l-.6 11-53 .3c-57.6.3-70.7-.4-85.4-4.3-24.3-6.6-47.8-22.5-63.5-43-7.7-10.1-16.6-27.7-19.6-38.6-5.2-19.2-5-7.3-5-286.2V694h687z" />
<path d="M391.8 143c-16.5 3-30 12.2-39.1 26.7-7.9 12.6-10 31-5.2 46.1 4.5 14.4 15.5 26.9 29.1 33.4 22.1 10.4 46.1 6.2 63.5-11.1 11.5-11.5 16.9-24.2 16.9-39.9 0-23.4-16.1-45.5-38.6-53.1-7.1-2.3-19.8-3.4-26.6-2.1zM388.3 1119.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7z" /> <path d="M391.8 143c-16.5 3-30 12.2-39.1 26.7-7.9 12.6-10 31-5.2 46.1 4.5 14.4 15.5 26.9 29.1 33.4 22.1 10.4 46.1 6.2 63.5-11.1 11.5-11.5 16.9-24.2 16.9-39.9 0-23.4-16.1-45.5-38.6-53.1-7.1-2.3-19.8-3.4-26.6-2.1M388.3 1119.5c-10.8 2.3-19 7-27.6 15.6-11.4 11.4-16.7 23.9-16.7 39.7 0 23.6 15.9 45.3 39 53.3 9.2 3.1 23.7 3.2 33.7.1 12.7-3.9 24.1-12.9 31.6-24.9 7.9-12.6 10-31 5.2-46.1-8.6-27.2-37.2-43.8-65.2-37.7" />
</svg> </svg>
); );
}; };

View File

@@ -20,8 +20,8 @@ const TwoPlayersSameSide = ({
{...props} {...props}
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7zm492 31.8c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.4 1.5 42.9 1.5 271.4V682H57V511.3l11.8-.6c18.9-1 32.8-5.9 42.8-15C126.7 482 139.1 436 139.1 394c0-23.9-3.5-47.1-10.7-71-9.5-31.5-25.9-44.2-59.2-45.7l-12.3-.6.4-62.6c.3-59.6.4-63 2.5-71.6 2.8-11.9 5.1-18.4 10.2-28.6C83.9 86 109.2 64.3 139.7 54c18.5-6.2 3.5-5.8 264.3-5.6l238.5.1 9 2.4zm92.5 904c0 289.2.4 270.2-6.1 289.5-13.4 40-47 69.6-89.9 79.3-6.9 1.6-26.1 1.7-239 2-154.7.3-235.3 0-243-.7-20.8-1.8-38.3-8-55.8-19.7-28.1-18.7-46.6-48.2-51.8-82.8-1.1-6.8-1.4-23.1-1.4-67.1v-58.1l11.8-.6c18.9-1 32.8-5.9 42.8-15 15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.5-31.5-25.9-44.2-59.1-45.7l-12.3-.6V692h687v262.9z" /> <path d="M159.5 19.1c-17.6 1.8-34.7 7.2-52.2 16.4-14.4 7.5-25 15.3-36.8 27-20.3 20.4-33.6 45.2-40.7 76l-2.3 10-.3 531.5c-.2 382 0 534.3.8 541.5 2.2 20.1 7.2 36.4 16.5 54.3 7.3 14 15.2 24.6 26.7 36.1 20.1 20.1 41 31.8 70.8 39.8l10.5 2.8h497l11.7-3.2c27-7.4 47-18.5 66.9-37.1 21.5-20.2 37-48.2 43.6-79.2 1.7-8.1 1.8-30.9 1.8-548 0-518.6-.1-539.8-1.8-548-12-55.7-49.8-97.4-103.5-114.4-20.5-6.4-.6-6-262.7-6.2-130.9-.1-241.6.2-246 .7m492 31.8c5 1.3 13.2 4.1 18.4 6.2 38.5 15.7 65.4 49.3 72.6 90.6 1.3 7.4 1.5 42.9 1.5 271.4V682H57V511.3l11.8-.6c18.9-1 32.8-5.9 42.8-15C126.7 482 139.1 436 139.1 394c0-23.9-3.5-47.1-10.7-71-9.5-31.5-25.9-44.2-59.2-45.7l-12.3-.6.4-62.6c.3-59.6.4-63 2.5-71.6 2.8-11.9 5.1-18.4 10.2-28.6C83.9 86 109.2 64.3 139.7 54c18.5-6.2 3.5-5.8 264.3-5.6l238.5.1zm92.5 904c0 289.2.4 270.2-6.1 289.5-13.4 40-47 69.6-89.9 79.3-6.9 1.6-26.1 1.7-239 2-154.7.3-235.3 0-243-.7-20.8-1.8-38.3-8-55.8-19.7-28.1-18.7-46.6-48.2-51.8-82.8-1.1-6.8-1.4-23.1-1.4-67.1v-58.1l11.8-.6c18.9-1 32.8-5.9 42.8-15 15.1-13.7 27.5-59.7 27.5-101.7 0-23.9-3.5-47.1-10.7-71-9.5-31.5-25.9-44.2-59.1-45.7l-12.3-.6V692h687z" />
<path d="M196 338.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5zM196 924.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5z" /> <path d="M196 338.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5M196 924.6c-18.6 4.9-33.7 19-40.1 37.4-1.7 5-2.2 8.9-2.3 16.5-.1 12.8 2.5 21.5 9.4 31.8 10.7 16 27 24.7 46.4 24.7 27.2 0 49.5-18 55.1-44.6 1.8-8.7 1.8-14.1 0-22.6-4.4-21.2-22.5-39.4-43.3-43.7-7.2-1.5-18.5-1.3-25.2.5" />
</svg> </svg>
); );
}; };

View File

@@ -21,8 +21,8 @@ const LittleGuy = ({
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<g fill="currentColor"> <g fill="currentColor">
<path d="M233.8 9.7c-18.1 17.5-25.6 32.5-25.8 51.5 0 5.9.7 7.8 3.1 7.8 2.8 0 3.6-2.6 4.1-13.2.4-8.9.7-10.2 4.7-18.3 3.6-7.4 6.2-10.7 15.7-20.5C241.9 10.6 247 4.6 247 3.7c0-1.2-1.9-2.7-3.6-2.7-.3 0-4.6 3.9-9.6 8.7zM36.5 54.7c-8.4.6-31.6 4.3-33.6 5.4-2.3 1.2-2.5 4.5-.3 5.3.9.3 1.9.4 2.3.2.3-.2 7.2-1.4 15.1-2.6 12.9-2 17.1-2.2 37.5-1.6 24.9.6 31.9 1.5 52.6 6.9 16.2 4.3 29 8.8 42.3 15.1 8.7 4 10.2 4.5 11.4 3.2 2.3-2.3.8-4.3-5.5-7.4C141.9 70.9 111 61 90.5 57.5c-7.8-1.4-41.4-3.7-47-3.3-1.1.1-4.2.3-7 .5zM205.8 87.6c-16.9 3-35.3 12.3-44.8 22.7-4.5 4.8-9.9 16.6-11.6 25.4-2.6 12.8-.2 40.8 4.7 56.2 8.1 25.4 34.3 55.1 55.6 63.1 12.4 4.6 17.3 5.5 31.3 5.4 7.9 0 14.6-.5 16.3-1.2 2.5-1.1 2.7-1.5 2.7-7.6v-6.5l-4.2 1.6c-6.1 2.3-24.2 2.2-32.7-.1-13.5-3.7-26.2-12.2-36.9-24.5-16.9-19.7-22.9-34.2-25.1-60.6-1.4-17.8 0-27.4 5.6-37.6 3.6-6.8 9.5-11.8 19.5-16.7 21.8-10.7 42.8-11.5 66.3-2.3l7.5 2.9V94.2l-8-2.7c-16.2-5.4-31.1-6.7-46.2-3.9z" /> <path d="M233.8 9.7c-18.1 17.5-25.6 32.5-25.8 51.5 0 5.9.7 7.8 3.1 7.8 2.8 0 3.6-2.6 4.1-13.2.4-8.9.7-10.2 4.7-18.3 3.6-7.4 6.2-10.7 15.7-20.5C241.9 10.6 247 4.6 247 3.7c0-1.2-1.9-2.7-3.6-2.7-.3 0-4.6 3.9-9.6 8.7M36.5 54.7c-8.4.6-31.6 4.3-33.6 5.4-2.3 1.2-2.5 4.5-.3 5.3.9.3 1.9.4 2.3.2.3-.2 7.2-1.4 15.1-2.6 12.9-2 17.1-2.2 37.5-1.6 24.9.6 31.9 1.5 52.6 6.9 16.2 4.3 29 8.8 42.3 15.1 8.7 4 10.2 4.5 11.4 3.2 2.3-2.3.8-4.3-5.5-7.4C141.9 70.9 111 61 90.5 57.5c-7.8-1.4-41.4-3.7-47-3.3-1.1.1-4.2.3-7 .5M205.8 87.6c-16.9 3-35.3 12.3-44.8 22.7-4.5 4.8-9.9 16.6-11.6 25.4-2.6 12.8-.2 40.8 4.7 56.2 8.1 25.4 34.3 55.1 55.6 63.1 12.4 4.6 17.3 5.5 31.3 5.4 7.9 0 14.6-.5 16.3-1.2 2.5-1.1 2.7-1.5 2.7-7.6v-6.5l-4.2 1.6c-6.1 2.3-24.2 2.2-32.7-.1-13.5-3.7-26.2-12.2-36.9-24.5-16.9-19.7-22.9-34.2-25.1-60.6-1.4-17.8 0-27.4 5.6-37.6 3.6-6.8 9.5-11.8 19.5-16.7 21.8-10.7 42.8-11.5 66.3-2.3l7.5 2.9V94.2l-8-2.7c-16.2-5.4-31.1-6.7-46.2-3.9" />
<path d="M222.5 132.5c-4.8 4.7-1.9 11.5 4.9 11.5 6.4 0 9-7.9 4-11.9-3.5-2.7-5.9-2.6-8.9.4zM184.5 152.5c-3 3-3.1 5.4-.4 8.9 4 5 11.9 2.4 11.9-4 0-6.8-6.8-9.7-11.5-4.9zM255.8 171.8c-4.7 9.7-13.3 20.3-19.5 23.8-5.9 3.3-7.3 5-7.3 8.6 0 3.6 3.2 6.8 6.8 6.8 4.1 0 12-5.1 18.5-11.8l5.7-6.1v-14.5c0-8-.1-14.6-.2-14.5-.2 0-2 3.5-4 7.7z" /> <path d="M222.5 132.5c-4.8 4.7-1.9 11.5 4.9 11.5 6.4 0 9-7.9 4-11.9-3.5-2.7-5.9-2.6-8.9.4M184.5 152.5c-3 3-3.1 5.4-.4 8.9 4 5 11.9 2.4 11.9-4 0-6.8-6.8-9.7-11.5-4.9M255.8 171.8c-4.7 9.7-13.3 20.3-19.5 23.8-5.9 3.3-7.3 5-7.3 8.6s3.2 6.8 6.8 6.8c4.1 0 12-5.1 18.5-11.8l5.7-6.1v-14.5c0-8-.1-14.6-.2-14.5-.2 0-2 3.5-4 7.7" />
</g> </g>
</svg> </svg>
); );

View File

@@ -22,7 +22,7 @@ const Logo = ({
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path <path
d="M0-196.791c108.629 0 196.791 88.162 196.791 196.791 0 108.629-88.162 196.791-196.791 196.791-108.629 0-196.791-88.162-196.791-196.791 0-108.629 88.162-196.791 196.791-196.791z" d="M0-196.791c108.629 0 196.791 88.162 196.791 196.791S108.63 196.791 0 196.791-196.791 108.63-196.791 0-108.63-196.791 0-196.791z"
style={{ style={{
stroke: '#590713', stroke: '#590713',
strokeWidth: 0, strokeWidth: 0,
@@ -39,7 +39,7 @@ const Logo = ({
/> />
<path <path
strokeLinecap="round" strokeLinecap="round"
d="M-17.706-50.704s-4.378 12.973.446 25.583c2.124 5.55 12.255 17.109 17.107 36.364 4.852 19.255 19.679 39.46 19.679 39.46" d="M-17.706-50.704s-4.378 12.973.446 25.583c2.124 5.55 12.255 17.109 17.107 36.364s19.679 39.46 19.679 39.46"
style={{ style={{
stroke: '#000', stroke: '#000',
strokeWidth: 4, strokeWidth: 4,
@@ -52,12 +52,12 @@ const Logo = ({
fillRule: 'nonzero', fillRule: 'nonzero',
opacity: 1, opacity: 1,
}} }}
transform="rotate(1 -17129.538 10072.475) scale(2.11279)" transform="rotate(1 -17129.538 10072.475)scale(2.11279)"
/> />
<path <path
fill="none" fill="none"
strokeLinecap="round" strokeLinecap="round"
d="M50.163 56.172S31.68 34 24.46 3.6C17.239-26.8 13.467-35.306 2.42-47.373c-11.046-12.066-26.224-12.189-25.26 2.534.966 14.722 12.84 12.598 14.365 1.93 1.526-10.667-21.8-10.696-32.304 1.854C-53.573-25.77-49.57-2.79-49.57-2.79" d="M50.163 56.172S31.68 34 24.46 3.6 13.467-35.306 2.42-47.373c-11.046-12.066-26.224-12.189-25.26 2.534.966 14.722 12.84 12.598 14.365 1.93 1.526-10.667-21.8-10.696-32.304 1.854C-53.573-25.77-49.57-2.79-49.57-2.79"
style={{ style={{
stroke: '#000', stroke: '#000',
strokeWidth: 6, strokeWidth: 6,
@@ -76,7 +76,7 @@ const Logo = ({
<path <path
fill="none" fill="none"
strokeLinecap="round" strokeLinecap="round"
d="M-61.541-71.832s39.965 62.644 41.684 89.093c1.718 26.45 7.326 33.328 17.516 44.667 10.19 11.339 36.055 16.684 32.478-4.845-3.578-21.53-29.514-8.071-16.735 4.07 12.779 12.143 30.514 9.462 39.88-2.368 6.188-7.814 9.732-26.682 7.67-41.524-1.059-7.627-14.72-28.434-14.72-28.434" d="M-61.541-71.832s39.965 62.644 41.684 89.093 7.326 33.328 17.516 44.667 36.055 16.684 32.478-4.845c-3.578-21.53-29.514-8.071-16.735 4.07 12.779 12.143 30.514 9.462 39.88-2.368 6.188-7.814 9.732-26.682 7.67-41.524-1.059-7.627-14.72-28.434-14.72-28.434"
style={{ style={{
stroke: '#000', stroke: '#000',
strokeWidth: 6, strokeWidth: 6,
@@ -95,7 +95,7 @@ const Logo = ({
<path <path
fill="none" fill="none"
strokeLinecap="round" strokeLinecap="round"
d="M-26.889-69.574S-9.544-51.826-3.454-23.78c6.09 28.046 24.518 40.828 27.8 61.071 3.284 20.244 3.494 23.63 0 32.283" d="M-26.889-69.574S-9.544-51.826-3.454-23.78s24.518 40.828 27.8 61.071c3.284 20.244 3.494 23.63 0 32.283"
style={{ style={{
stroke: '#000', stroke: '#000',
strokeWidth: 6, strokeWidth: 6,
@@ -114,7 +114,7 @@ const Logo = ({
<path <path
fill="none" fill="none"
strokeLinecap="round" strokeLinecap="round"
d="M30.481-85.77S31.897-63.74-.082-1.032C-32.06 61.673-30.478 85.769-30.478 85.769" d="M30.481-85.77S31.897-63.74-.082-1.032s-30.396 86.802-30.396 86.802"
style={{ style={{
stroke: '#000', stroke: '#000',
strokeWidth: 6, strokeWidth: 6,
@@ -133,7 +133,7 @@ const Logo = ({
<path <path
fill="none" fill="none"
strokeLinecap="round" strokeLinecap="round"
d="M-30.017 97.228S-.607 52.493 6.833 6.645c7.44-45.849 23.184-103.873 23.184-103.873" d="M-30.017 97.228S-.607 52.493 6.833 6.645 30.016-97.228 30.016-97.228"
style={{ style={{
stroke: '#000', stroke: '#000',
strokeWidth: 6, strokeWidth: 6,
@@ -152,7 +152,7 @@ const Logo = ({
<path <path
fill="none" fill="none"
strokeLinecap="round" strokeLinecap="round"
d="M-24.23-79.242s-7.454 19.782-.69 39.5c2.979 8.68 17.988 27.06 24.437 57.033 6.45 29.973 28.24 61.951 28.24 61.951" d="M-24.23-79.242s-7.454 19.782-.69 39.5c2.979 8.68 17.988 27.06 24.437 57.033s28.24 61.951 28.24 61.951"
style={{ style={{
stroke: '#000', stroke: '#000',
strokeWidth: 1, strokeWidth: 1,
@@ -166,11 +166,11 @@ const Logo = ({
fillRule: 'nonzero', fillRule: 'nonzero',
opacity: 1, opacity: 1,
}} }}
transform="rotate(-1.005 17359.092 -9769.972) scale(1.36839)" transform="rotate(-1.005 17359.092 -9769.972)scale(1.36839)"
/> />
<path <path
strokeLinecap="round" strokeLinecap="round"
d="M-45.63-48.464C-71.24-37.592-84.397-27.166-91.043-20.02c-4.07 4.376-3.966 4.338-5.24 6.898a4.617 4.617 0 0 0 .396 4.766c.894 1.234.96 1.326 2.899 3.595C-83.146 6.756-56.46 32.52-4.814 47.45c44.828 12.96 74.528 12.437 89.557 10.58 2.68-.332 2.726-.3 4.041-.593a10.178 10.178 0 0 0 7.957-9.425c.047-.93.022-1.057-.024-2.821-.34-12.824-3.372-35.645-18.26-58.984-22.56-35.37-46.732-40.492-46.732-40.492S.657-68.114-45.63-48.464z" d="M-45.63-48.464C-71.24-37.592-84.397-27.166-91.043-20.02c-4.07 4.376-3.966 4.338-5.24 6.898a4.62 4.62 0 0 0 .396 4.766c.894 1.234.96 1.326 2.899 3.595C-83.146 6.756-56.46 32.52-4.814 47.45c44.828 12.96 74.528 12.437 89.557 10.58 2.68-.332 2.726-.3 4.041-.593a10.18 10.18 0 0 0 7.957-9.425c.047-.93.022-1.057-.024-2.821-.34-12.824-3.372-35.645-18.26-58.984-22.56-35.37-46.732-40.492-46.732-40.492S.657-68.114-45.63-48.464z"
style={{ style={{
stroke: '#000', stroke: '#000',
strokeWidth: 6, strokeWidth: 6,
@@ -225,7 +225,7 @@ const Logo = ({
/> />
<path <path
strokeLinecap="round" strokeLinecap="round"
d="M-90.654-77.091S-74.41-28.509-9.055-2.309C56.299 23.89 100.897 9.078 100.897 9.078S61.451 101.65-37.09 70.793C-135.63 39.936-90.654-77.09-90.654-77.09z" d="M-90.654-77.091S-74.41-28.509-9.055-2.309C56.299 23.89 100.897 9.078 100.897 9.078S61.451 101.65-37.09 70.793-90.654-77.09-90.654-77.09z"
style={{ style={{
stroke: '#000', stroke: '#000',
strokeWidth: 6, strokeWidth: 6,

View File

@@ -22,7 +22,7 @@ const PartnerTax = ({
{...props} {...props}
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path d="M196.7 79.1c-2.1.5-5 1.5-6.5 2.3-3.3 1.8-17.6 16.3-59.3 60.1-60 63.1-60.2 63.3-62.2 78.5-1.7 13.3 1.4 24.2 10 34.7 6.5 7.9 10.6 9.9 40.8 19.8 29.9 9.8 33 11 30.5 11.9-.8.3-8 3.1-16 6.1-8 3.1-19.7 7.5-26 9.9-20.7 7.9-40.3 15.7-45 18.1-2.6 1.3-7.2 5.1-10.2 8.4-6.6 7.1-7.2 8.7-17.9 47.6-19.1 69.1-21.3 78.3-20.3 84.8 1.6 9.6 7.1 16.9 16.3 21.7l4 2 233.6-.2 233.7-.3 3.3-3.7c5.2-5.6 5.3-7.6 1.6-22.3-5.9-23.2-13.2-47.9-15.3-51.3-3.3-5.3-7.3-7.5-30-16.3-12-4.6-21.8-8.6-21.8-8.9 0-.3 6.6-2.7 14.8-5.4 16.2-5.3 19.9-7.5 23.8-14.3 3.4-5.6 3.7-13.7.8-20-1.9-4.2-32.4-37.2-52.7-57.1-9-8.8-9.6-9.2-13.8-9.2-2.4.1-5.4.6-6.5 1.3-1.8 1-17.1 16.5-46 46.6l-7.2 7.5-3.3-3.5c-7.4-7.7-11.5-9.6-79.6-35.9-9-3.5-16.3-6.7-16.3-7.1 0-.4 12.5-4.8 27.8-9.8 16.3-5.3 29.6-10.2 32.2-11.9 6-3.8 13.3-11.8 16.3-17.8 7-13.9 5.4-33.5-3.8-45.3-7.2-9.1-103-109.8-110.2-115.7-5.8-4.8-13.2-6.7-19.6-5.3zm34.8 84.4c5.5 1.4 12.2 3.3 14.9 4.2l4.8 1.5-.6 4.6c-1 7.4-11.3 36.9-16.3 46.8-2.5 5.1-7.6 13.4-11.3 18.5-5.4 7.6-7.7 9.9-12 12.1-6.8 3.6-12.8 3.7-19.7.3-5.8-2.9-9.3-6.8-18.2-20.5-8.2-12.5-13.2-23.1-17.5-37.1-5.1-17-6.4-23.8-4.6-24.9.8-.5 7.9-2.5 15.9-4.4 8.1-1.9 15.5-3.8 16.6-4 1.1-.3 10.1-.3 20-.1 15.1.4 19.6.8 28 3zm-76.1 88.4 46.9 23.5 51.8-25.9c28.5-14.3 52.1-25.7 52.4-25.3.8.8-1.8 7-3.6 8.4-3.9 3.1-93.1 50.5-98.6 52.5-3.1 1-4.2.6-19-7.1-23.6-12.4-66.7-35.6-75.5-40.7-7.6-4.4-11.8-8.5-11.8-11.6 0-2.4 4.5-.3 57.4 26.2zM418.5 318c12 1.2 19.5 3.5 19.5 5.9 0 3.7-7.2 22.7-11 29.2-5.4 9.1-9.9 12.9-15.2 12.9-3.7 0-4.7-.6-8.7-4.8-5.3-5.5-12-17.7-15-27.6-3.2-10.5-3.1-11.3 3.2-13.1 12.1-3.5 15-3.8 27.2-2.5zM88.6 354.5c1.4 3.2 2.4 7.5 2.4 10.1 0 6-14 102.3-15.7 107.8-.5 1.8-.6 1.8-1.5.1-1.4-2.4-7.8-23.3-7.8-25.4-.1-3.8 18.5-98.1 19.2-98.1.5 0 2 2.5 3.4 5.5zm239.8 43.3c5.3 26.8 9.6 49.4 9.6 50.1 0 2.1-9.2 28.1-9.9 28.1-.4 0-1.9-7.8-3.4-17.3-1.4-9.4-5.4-34.7-8.7-56l-6-38.8 3.4-7.4c1.9-4.1 3.9-7.5 4.4-7.5.5 0 5.3 21.9 10.6 48.8zm136.5-43.3c-3 2.9-50.1 27.5-52.5 27.5-1.1 0-8.6-3.6-16.9-8.1-8.2-4.4-18.9-10.1-23.7-12.6-8-4.2-10.8-6.7-9.3-8.2.3-.3 11.7 5 25.3 11.8l24.7 12.3 27-13.5c25.2-12.6 31.2-14.8 25.4-9.2zm-86.6 25.1c4.3 1.4 7.7 2.8 7.5 3-.2.2-3.4 1.5-7.1 2.9l-6.6 2.5-1.6-5.2c-.8-2.8-1.5-5.3-1.5-5.5 0-.6 1.7-.2 9.3 2.3zm100.1 62.8 4.5 23.4-2.3 6.9c-2.9 8.4-2.5 9.8-7.6-24.5-3.7-25.3-3.8-26.5-2.2-29.8 1.5-3.2 1.7-3.3 2.4-1.5.4 1.1 2.7 12.6 5.2 25.5z" /> <path d="M196.7 79.1c-2.1.5-5 1.5-6.5 2.3-3.3 1.8-17.6 16.3-59.3 60.1-60 63.1-60.2 63.3-62.2 78.5-1.7 13.3 1.4 24.2 10 34.7 6.5 7.9 10.6 9.9 40.8 19.8 29.9 9.8 33 11 30.5 11.9-.8.3-8 3.1-16 6.1-8 3.1-19.7 7.5-26 9.9-20.7 7.9-40.3 15.7-45 18.1-2.6 1.3-7.2 5.1-10.2 8.4-6.6 7.1-7.2 8.7-17.9 47.6-19.1 69.1-21.3 78.3-20.3 84.8 1.6 9.6 7.1 16.9 16.3 21.7l4 2 233.6-.2 233.7-.3 3.3-3.7c5.2-5.6 5.3-7.6 1.6-22.3-5.9-23.2-13.2-47.9-15.3-51.3-3.3-5.3-7.3-7.5-30-16.3-12-4.6-21.8-8.6-21.8-8.9s6.6-2.7 14.8-5.4c16.2-5.3 19.9-7.5 23.8-14.3 3.4-5.6 3.7-13.7.8-20-1.9-4.2-32.4-37.2-52.7-57.1-9-8.8-9.6-9.2-13.8-9.2-2.4.1-5.4.6-6.5 1.3-1.8 1-17.1 16.5-46 46.6l-7.2 7.5-3.3-3.5c-7.4-7.7-11.5-9.6-79.6-35.9-9-3.5-16.3-6.7-16.3-7.1s12.5-4.8 27.8-9.8c16.3-5.3 29.6-10.2 32.2-11.9 6-3.8 13.3-11.8 16.3-17.8 7-13.9 5.4-33.5-3.8-45.3-7.2-9.1-103-109.8-110.2-115.7-5.8-4.8-13.2-6.7-19.6-5.3m34.8 84.4c5.5 1.4 12.2 3.3 14.9 4.2l4.8 1.5-.6 4.6c-1 7.4-11.3 36.9-16.3 46.8-2.5 5.1-7.6 13.4-11.3 18.5-5.4 7.6-7.7 9.9-12 12.1-6.8 3.6-12.8 3.7-19.7.3-5.8-2.9-9.3-6.8-18.2-20.5-8.2-12.5-13.2-23.1-17.5-37.1-5.1-17-6.4-23.8-4.6-24.9.8-.5 7.9-2.5 15.9-4.4 8.1-1.9 15.5-3.8 16.6-4 1.1-.3 10.1-.3 20-.1 15.1.4 19.6.8 28 3m-76.1 88.4 46.9 23.5 51.8-25.9c28.5-14.3 52.1-25.7 52.4-25.3.8.8-1.8 7-3.6 8.4-3.9 3.1-93.1 50.5-98.6 52.5-3.1 1-4.2.6-19-7.1-23.6-12.4-66.7-35.6-75.5-40.7-7.6-4.4-11.8-8.5-11.8-11.6 0-2.4 4.5-.3 57.4 26.2M418.5 318c12 1.2 19.5 3.5 19.5 5.9 0 3.7-7.2 22.7-11 29.2-5.4 9.1-9.9 12.9-15.2 12.9-3.7 0-4.7-.6-8.7-4.8-5.3-5.5-12-17.7-15-27.6-3.2-10.5-3.1-11.3 3.2-13.1 12.1-3.5 15-3.8 27.2-2.5M88.6 354.5c1.4 3.2 2.4 7.5 2.4 10.1 0 6-14 102.3-15.7 107.8-.5 1.8-.6 1.8-1.5.1-1.4-2.4-7.8-23.3-7.8-25.4-.1-3.8 18.5-98.1 19.2-98.1.5 0 2 2.5 3.4 5.5m239.8 43.3c5.3 26.8 9.6 49.4 9.6 50.1 0 2.1-9.2 28.1-9.9 28.1-.4 0-1.9-7.8-3.4-17.3-1.4-9.4-5.4-34.7-8.7-56l-6-38.8 3.4-7.4c1.9-4.1 3.9-7.5 4.4-7.5s5.3 21.9 10.6 48.8m136.5-43.3c-3 2.9-50.1 27.5-52.5 27.5-1.1 0-8.6-3.6-16.9-8.1-8.2-4.4-18.9-10.1-23.7-12.6-8-4.2-10.8-6.7-9.3-8.2.3-.3 11.7 5 25.3 11.8l24.7 12.3 27-13.5c25.2-12.6 31.2-14.8 25.4-9.2m-86.6 25.1c4.3 1.4 7.7 2.8 7.5 3s-3.4 1.5-7.1 2.9l-6.6 2.5-1.6-5.2c-.8-2.8-1.5-5.3-1.5-5.5 0-.6 1.7-.2 9.3 2.3m100.1 62.8 4.5 23.4-2.3 6.9c-2.9 8.4-2.5 9.8-7.6-24.5-3.7-25.3-3.8-26.5-2.2-29.8 1.5-3.2 1.7-3.3 2.4-1.5.4 1.1 2.7 12.6 5.2 25.5" />
</svg> </svg>
); );
}; };

View File

@@ -23,7 +23,7 @@ const Poison = ({
{...props} {...props}
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path d="M261.6 32.1 228 57.2v60.5l-6.7 1.2c-16.1 2.9-39.8 10.8-54.6 18.2-9.7 4.9-27.4 17-36.5 24.9-24.3 21.3-40.6 49-46.3 79-1.6 8.3-1.6 33.5 0 42 4.3 22.8 16.3 46.9 32.3 64.6 26 28.9 60 47.5 104 56.9l7.8 1.6.2 55.8.3 55.7 34-25.2 34-25.2.5-30.8.5-30.8 10.5-2.2c27-5.6 54.2-17.8 75.5-33.8 77.9-58.4 77.4-158.4-1.2-216.5-22.5-16.7-46.6-27.2-77-33.7l-8.3-1.7V62.3c0-30.8-.4-55.3-.9-55.3s-16 11.3-34.5 25.1zM228 262v89l-2.7-.6c-5.3-1.3-14.8-5.1-23.8-9.7-31-15.5-50.5-41.7-53.4-71.4-2.5-25.1 6.9-48.5 27-67.8 9.4-9 15.7-13.4 27.6-19.4 7.8-3.9 21.4-9 24.1-9.1.9 0 1.2 18.5 1.2 89zm76.1-86.9c33.2 10.8 58.9 34.5 68.6 63.4 2.5 7.4 2.7 9.2 2.7 23.5 0 14.2-.3 16.2-2.8 23.5-6.7 19.6-18.7 35-37.3 47.8-7.1 4.9-25.9 14-33 16l-5.3 1.5v-88.9c0-48.9.2-88.9.3-88.9.2 0 3.3.9 6.8 2.1z" /> <path d="M261.6 32.1 228 57.2v60.5l-6.7 1.2c-16.1 2.9-39.8 10.8-54.6 18.2-9.7 4.9-27.4 17-36.5 24.9-24.3 21.3-40.6 49-46.3 79-1.6 8.3-1.6 33.5 0 42 4.3 22.8 16.3 46.9 32.3 64.6 26 28.9 60 47.5 104 56.9l7.8 1.6.2 55.8.3 55.7 34-25.2 34-25.2.5-30.8.5-30.8 10.5-2.2c27-5.6 54.2-17.8 75.5-33.8 77.9-58.4 77.4-158.4-1.2-216.5-22.5-16.7-46.6-27.2-77-33.7l-8.3-1.7V62.3c0-30.8-.4-55.3-.9-55.3s-16 11.3-34.5 25.1M228 262v89l-2.7-.6c-5.3-1.3-14.8-5.1-23.8-9.7-31-15.5-50.5-41.7-53.4-71.4-2.5-25.1 6.9-48.5 27-67.8 9.4-9 15.7-13.4 27.6-19.4 7.8-3.9 21.4-9 24.1-9.1.9 0 1.2 18.5 1.2 89m76.1-86.9c33.2 10.8 58.9 34.5 68.6 63.4 2.5 7.4 2.7 9.2 2.7 23.5 0 14.2-.3 16.2-2.8 23.5-6.7 19.6-18.7 35-37.3 47.8-7.1 4.9-25.9 14-33 16l-5.3 1.5v-88.9c0-48.9.2-88.9.3-88.9.2 0 3.3.9 6.8 2.1" />
</svg> </svg>
); );
}; };

View File

@@ -22,7 +22,7 @@ const ResetGame = ({
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path <path
fill="currentColor" fill="currentColor"
d="M129.3 50.7C105.7 81.6 95 95.8 82.6 113c-14.3 19.7-16.7 25.2-14.6 33.2 2.5 9.4 9 14.2 21.3 15.8 8.2 1.1 116.4 3.7 117.2 2.8.3-.3-3.3-8.8-8-18.8s-8.8-19.3-9.1-20.5c-.5-2.1-.1-2.3 8.3-3.4 4.9-.6 20.3-1.6 34.2-2.2 65.4-2.7 102.7 6.4 133.2 32.4 31.4 26.8 48.8 75.9 44.9 126.8-6.7 87.6-61.2 133.4-155.3 130.6-45.4-1.3-78.1-13.8-103.7-39.6-23.5-23.8-39-60.5-41.9-99.4-.9-12-2-15.4-6.7-20-5.2-5.2-7.9-5.7-29.3-5.6-24 .1-27.9 1.2-32.1 9.5-1.6 3-2 5.9-2 13.9 0 35.9 12.4 76.8 32.8 108 34.1 52.5 88.1 87.3 152.2 98.2 16.3 2.8 57.2 2.5 73.5-.5 46.1-8.4 84.5-28.3 116.4-60.3 42.5-42.7 65-104 60.3-164.1-4.2-52.8-25-98.7-61.2-134.8-38.8-38.8-87.2-59.7-145-62.6-24.6-1.2-73.1 2.8-99.1 8.2-2.5.5-2.9-.2-10.9-17.1-4.6-9.6-8.7-17.5-9.1-17.5-.3 0-9.1 11.1-19.6 24.7z" d="M129.3 50.7C105.7 81.6 95 95.8 82.6 113c-14.3 19.7-16.7 25.2-14.6 33.2 2.5 9.4 9 14.2 21.3 15.8 8.2 1.1 116.4 3.7 117.2 2.8.3-.3-3.3-8.8-8-18.8s-8.8-19.3-9.1-20.5c-.5-2.1-.1-2.3 8.3-3.4 4.9-.6 20.3-1.6 34.2-2.2 65.4-2.7 102.7 6.4 133.2 32.4 31.4 26.8 48.8 75.9 44.9 126.8-6.7 87.6-61.2 133.4-155.3 130.6-45.4-1.3-78.1-13.8-103.7-39.6-23.5-23.8-39-60.5-41.9-99.4-.9-12-2-15.4-6.7-20-5.2-5.2-7.9-5.7-29.3-5.6-24 .1-27.9 1.2-32.1 9.5-1.6 3-2 5.9-2 13.9 0 35.9 12.4 76.8 32.8 108 34.1 52.5 88.1 87.3 152.2 98.2 16.3 2.8 57.2 2.5 73.5-.5 46.1-8.4 84.5-28.3 116.4-60.3 42.5-42.7 65-104 60.3-164.1-4.2-52.8-25-98.7-61.2-134.8-38.8-38.8-87.2-59.7-145-62.6-24.6-1.2-73.1 2.8-99.1 8.2-2.5.5-2.9-.2-10.9-17.1-4.6-9.6-8.7-17.5-9.1-17.5-.3 0-9.1 11.1-19.6 24.7"
/> />
</svg> </svg>
); );

View File

@@ -21,8 +21,8 @@ const Skull = ({
> >
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<g fill="currentColor"> <g fill="currentColor">
<path d="M237.2 37.1c-30.6 2.9-64.8 15-89.4 31.6-12.2 8.2-31.7 23.4-38.3 30-15.2 14.9-24.6 33.8-27.5 55.1-4.5 32.9 10.8 64.5 38.7 79.8 16.3 8.9 22.3 12.7 28.1 18 15 13.5 23.5 31.4 24.9 52.3.8 11.5 3.2 17.9 8.9 23.1 8.6 7.9 4.4 7.5 73.4 7.5 60.6 0 61.6 0 66-2.1 10-4.8 14.7-12.3 16-25.7 1.3-12.5 1.8-15.2 4.2-22.7 3.9-12.1 11.9-24.4 21.2-32.7 5.5-4.9 8.1-6.5 25.3-16 16.4-9.1 26.1-19.1 33.9-34.8 5.6-11.4 7.4-18.5 8.1-30.8 1.5-26.6-9.6-54.2-29.4-72.7-8.8-8.3-27.3-22.7-38.3-29.8-35.8-23.3-82.1-34.3-125.8-30.1zm-46 72c15 3.2 26.2 13.8 28.7 27.3 1.7 9.2-2.6 18.2-14 29.7-14.1 14.1-28.2 21.9-40.9 22.6-6.6.4-7.9.2-11-1.9-7.5-5-10.9-17.2-11-39 0-14.7 1.9-20.6 8.9-27.6 10.1-10 24.7-14.2 39.3-11.1zm145 0c11.6 2.5 22.7 10.8 26.9 20.2 2.2 4.8 2.4 6.4 2.3 20.7-.1 22.2-3.1 32.2-11 37.1-2.9 1.8-4.6 2.1-10.8 1.7-6.2-.3-8.6-1-15.9-4.6-15.2-7.5-30.9-21.8-36.5-33.2-11.3-23.1 15.6-48.1 45-41.9zm-73.5 102.8c4.7 3.4 11.8 13.7 13.3 19.3 2 7.3-2 13.6-11.5 17.9-12.1 5.6-29.5-2.7-29.5-14.1 0-6.8 7.2-19 13.9-23.6 4.8-3.2 8.9-3.1 13.8.5z" /> <path d="M237.2 37.1c-30.6 2.9-64.8 15-89.4 31.6-12.2 8.2-31.7 23.4-38.3 30-15.2 14.9-24.6 33.8-27.5 55.1-4.5 32.9 10.8 64.5 38.7 79.8 16.3 8.9 22.3 12.7 28.1 18 15 13.5 23.5 31.4 24.9 52.3.8 11.5 3.2 17.9 8.9 23.1 8.6 7.9 4.4 7.5 73.4 7.5 60.6 0 61.6 0 66-2.1 10-4.8 14.7-12.3 16-25.7 1.3-12.5 1.8-15.2 4.2-22.7 3.9-12.1 11.9-24.4 21.2-32.7 5.5-4.9 8.1-6.5 25.3-16 16.4-9.1 26.1-19.1 33.9-34.8 5.6-11.4 7.4-18.5 8.1-30.8 1.5-26.6-9.6-54.2-29.4-72.7-8.8-8.3-27.3-22.7-38.3-29.8-35.8-23.3-82.1-34.3-125.8-30.1m-46 72c15 3.2 26.2 13.8 28.7 27.3 1.7 9.2-2.6 18.2-14 29.7-14.1 14.1-28.2 21.9-40.9 22.6-6.6.4-7.9.2-11-1.9-7.5-5-10.9-17.2-11-39 0-14.7 1.9-20.6 8.9-27.6 10.1-10 24.7-14.2 39.3-11.1m145 0c11.6 2.5 22.7 10.8 26.9 20.2 2.2 4.8 2.4 6.4 2.3 20.7-.1 22.2-3.1 32.2-11 37.1-2.9 1.8-4.6 2.1-10.8 1.7-6.2-.3-8.6-1-15.9-4.6-15.2-7.5-30.9-21.8-36.5-33.2-11.3-23.1 15.6-48.1 45-41.9m-73.5 102.8c4.7 3.4 11.8 13.7 13.3 19.3 2 7.3-2 13.6-11.5 17.9-12.1 5.6-29.5-2.7-29.5-14.1 0-6.8 7.2-19 13.9-23.6 4.8-3.2 8.9-3.1 13.8.5" />
<path d="M56.8 304.7c-1.4 1-3.6 3.1-4.9 4.6-5.1 6.6-8.2 25.8-4.7 29.3 2.9 2.9 19.8 7.1 67.8 16.9 36.1 7.4 79 17.5 79 18.7 0 .4-.6.8-1.4.8-2.3 0-73.5 23.6-95.6 31.7-20.8 7.6-40.7 16.4-44.8 19.9-1.6 1.3-2.7 4.1-3.8 9.4-2.9 14.3.2 20.8 12.5 25.5 6.3 2.4 7.3 2.5 11.7 1.4 6.2-1.6 18.9-6.3 48.4-18.2 56.7-22.9 114.7-42.6 140.7-47.7l5.8-1.2 24.5 8.5c13.5 4.7 45.2 16 70.5 25.2 78.6 28.6 95.9 34 101.1 31.6 3.2-1.4 5.4-6.7 5.4-12.8 0-9.6-3.3-16.3-10.8-22-4.8-3.6-8.7-4.9-27.7-8.8-7.1-1.5-15.9-3.8-19.5-5.2-3.6-1.3-17.1-6.5-30-11.5-12.9-4.9-30.4-11.4-38.8-14.4-8.4-2.9-15.8-5.7-16.4-6-.6-.4 4.2-2.2 10.8-4.1 11.6-3.3 27-7.7 58.3-16.9 17.6-5.2 48.9-16.6 54.1-19.6 8.8-5.2 14.8-13.3 17.4-23.3 1.3-5.3 1.4-6.2.1-8.2-2-3-5.3-3.7-13.4-2.9-19.7 1.8-89.3 19.4-175.4 44l-29.8 8.6-18.2-5.1c-20.2-5.6-63.5-18.8-91.2-27.9-18.1-5.9-52.3-16.5-62.5-19.4-9.6-2.7-16.1-3-19.2-.9z" /> <path d="M56.8 304.7c-1.4 1-3.6 3.1-4.9 4.6-5.1 6.6-8.2 25.8-4.7 29.3 2.9 2.9 19.8 7.1 67.8 16.9 36.1 7.4 79 17.5 79 18.7 0 .4-.6.8-1.4.8-2.3 0-73.5 23.6-95.6 31.7-20.8 7.6-40.7 16.4-44.8 19.9-1.6 1.3-2.7 4.1-3.8 9.4-2.9 14.3.2 20.8 12.5 25.5 6.3 2.4 7.3 2.5 11.7 1.4 6.2-1.6 18.9-6.3 48.4-18.2 56.7-22.9 114.7-42.6 140.7-47.7l5.8-1.2 24.5 8.5c13.5 4.7 45.2 16 70.5 25.2 78.6 28.6 95.9 34 101.1 31.6 3.2-1.4 5.4-6.7 5.4-12.8 0-9.6-3.3-16.3-10.8-22-4.8-3.6-8.7-4.9-27.7-8.8-7.1-1.5-15.9-3.8-19.5-5.2-3.6-1.3-17.1-6.5-30-11.5-12.9-4.9-30.4-11.4-38.8-14.4-8.4-2.9-15.8-5.7-16.4-6-.6-.4 4.2-2.2 10.8-4.1 11.6-3.3 27-7.7 58.3-16.9 17.6-5.2 48.9-16.6 54.1-19.6 8.8-5.2 14.8-13.3 17.4-23.3 1.3-5.3 1.4-6.2.1-8.2-2-3-5.3-3.7-13.4-2.9-19.7 1.8-89.3 19.4-175.4 44l-29.8 8.6-18.2-5.1c-20.2-5.6-63.5-18.8-91.2-27.9-18.1-5.9-52.3-16.5-62.5-19.4-9.6-2.7-16.1-3-19.2-.9" />
</g> </g>
</svg> </svg>
); );

View File

@@ -23,23 +23,23 @@ const BuyMeCoffee = ({
{title ? <title id={titleId}>{title}</title> : null} {title ? <title id={titleId}>{title}</title> : null}
<path <path
fill="#0D0C22" fill="#0D0C22"
d="m791.109 297.518-.878-.516-2.03-.619a4.833 4.833 0 0 0 2.908 1.135ZM803.896 388.891l-.98.275.98-.275ZM791.484 297.377a1.773 1.773 0 0 1-.366-.087 1.42 1.42 0 0 0 0 .244.738.738 0 0 0 .366-.157Z" d="m791.109 297.518-.878-.516-2.03-.619a4.83 4.83 0 0 0 2.908 1.135M803.896 388.891l-.98.275zM791.484 297.377a1.8 1.8 0 0 1-.366-.087 1.4 1.4 0 0 0 0 .244.74.74 0 0 0 .366-.157"
/> />
<path <path
fill="#0D0C22" fill="#0D0C22"
d="M791.113 297.529h.131v-.082l-.131.082ZM803.111 388.726l1.48-.843.551-.31.499-.533a8.447 8.447 0 0 0-2.53 1.686ZM793.669 299.515l-1.446-1.377-.98-.533a4.077 4.077 0 0 0 2.426 1.91ZM430.019 1186.18a7.55 7.55 0 0 0-2.943 2.27l.912-.58c.62-.57 1.497-1.24 2.031-1.69ZM641.187 1144.63c0-1.3-.636-1.06-.482 3.58 0-.37.155-.75.224-1.11.086-.83.155-1.64.258-2.47ZM619.284 1186.18a7.546 7.546 0 0 0-2.942 2.27l.912-.58c.619-.57 1.497-1.24 2.03-1.69ZM281.304 1196.06a6.299 6.299 0 0 0-3.097-1.45c.929.45 1.858.9 2.477 1.24l.62.21ZM247.841 1164.01a9.895 9.895 0 0 0-1.222-3.85c.474 1.23.87 2.5 1.187 3.78l.035.07Z" d="M791.113 297.529h.131v-.082zM803.111 388.726l1.48-.843.551-.31.499-.533a8.5 8.5 0 0 0-2.53 1.686M793.669 299.515l-1.446-1.377-.98-.533a4.08 4.08 0 0 0 2.426 1.91M430.019 1186.18a7.55 7.55 0 0 0-2.943 2.27l.912-.58c.62-.57 1.497-1.24 2.031-1.69M641.187 1144.63c0-1.3-.636-1.06-.482 3.58 0-.37.155-.75.224-1.11.086-.83.155-1.64.258-2.47M619.284 1186.18a7.55 7.55 0 0 0-2.942 2.27l.912-.58c.619-.57 1.497-1.24 2.03-1.69M281.304 1196.06a6.3 6.3 0 0 0-3.097-1.45c.929.45 1.858.9 2.477 1.24zM247.841 1164.01a9.9 9.9 0 0 0-1.222-3.85c.474 1.23.87 2.5 1.187 3.78z"
/> />
<path <path
fill="#FD0" fill="#FD0"
d="M472.623 590.836c-45.941 19.667-98.077 41.966-165.647 41.966a313.577 313.577 0 0 1-83.623-11.528l46.733 479.806a80.166 80.166 0 0 0 25.593 52.38 80.18 80.18 0 0 0 54.313 21.19s66.262 3.44 88.373 3.44c23.796 0 95.151-3.44 95.151-3.44 20.12 0 39.503-7.57 54.303-21.2a80.149 80.149 0 0 0 25.587-52.37l50.053-530.204c-22.368-7.639-44.943-12.715-70.391-12.715-44.014-.017-79.477 15.142-120.445 32.675Z" d="M472.623 590.836c-45.941 19.667-98.077 41.966-165.647 41.966a313.6 313.6 0 0 1-83.623-11.528l46.733 479.806a80.17 80.17 0 0 0 25.593 52.38 80.18 80.18 0 0 0 54.313 21.19s66.262 3.44 88.373 3.44c23.796 0 95.151-3.44 95.151-3.44 20.12 0 39.503-7.57 54.303-21.2a80.15 80.15 0 0 0 25.587-52.37l50.053-530.204c-22.368-7.639-44.943-12.715-70.391-12.715-44.014-.017-79.477 15.142-120.445 32.675"
/> />
<path <path
fill="#0D0C22" fill="#0D0C22"
d="m78.689 386.132.79.74.517.31a7.95 7.95 0 0 0-1.307-1.05Z" d="m78.689 386.132.79.74.517.31a8 8 0 0 0-1.307-1.05"
/> />
<path <path
fill="#0D0C22" fill="#0D0C22"
d="m879.567 341.849-7.037-35.497c-6.315-31.849-20.648-61.943-53.34-73.454-10.479-3.683-22.369-5.265-30.404-12.888-8.035-7.622-10.41-19.46-12.268-30.438-3.442-20.149-6.676-40.315-10.204-60.429-3.045-17.293-5.454-36.719-13.386-52.583-10.324-21.302-31.746-33.76-53.048-42.001a305.493 305.493 0 0 0-33.363-10.324C613.297 10.195 557.342 5.033 502.591 2.09a1376.266 1376.266 0 0 0-197.169 3.27c-48.797 4.439-100.193 9.807-146.564 26.687-16.948 6.177-34.413 13.593-47.3 26.687-15.813 16.088-20.975 40.969-9.43 61.031 8.208 14.247 22.111 24.313 36.857 30.972a298.948 298.948 0 0 0 59.844 19.478c57.297 12.664 116.642 17.636 175.178 19.753a1333.99 1333.99 0 0 0 194.433-6.35 1106.995 1106.995 0 0 0 47.817-6.314c18.738-2.874 30.765-27.376 25.242-44.445-6.608-20.406-24.365-28.321-44.444-25.241-2.96.464-5.902.894-8.862 1.324l-2.133.31c-6.803.861-13.605 1.663-20.407 2.409a1083.055 1083.055 0 0 1-42.259 3.717c-31.626 2.202-63.337 3.217-95.031 3.269-31.144 0-62.305-.878-93.38-2.925a1187.382 1187.382 0 0 1-42.431-3.545 1019.94 1019.94 0 0 1-19.219-2.168l-6.092-.774-1.324-.189-6.315-.912c-12.905-1.945-25.81-4.181-38.577-6.883a5.8 5.8 0 0 1 0-11.322h.241c11.064-2.357 22.213-4.37 33.397-6.125 3.729-.585 7.468-1.159 11.219-1.72h.103c7.003-.465 14.041-1.722 21.009-2.547A1336.183 1336.183 0 0 1 469.538 73.1c29.577.86 59.138 2.599 88.578 5.593a979.44 979.44 0 0 1 18.927 2.116c2.409.293 4.835.636 7.262.93l4.886.705a678.058 678.058 0 0 1 42.517 7.725c20.889 4.543 47.714 6.022 57.005 28.907 2.96 7.261 4.302 15.331 5.936 22.953l2.082 9.722c.055.174.095.353.121.533 4.921 22.942 9.848 45.884 14.78 68.826a12.608 12.608 0 0 1-2.006 9.871 12.612 12.612 0 0 1-8.593 5.254h-.138l-3.011.413-2.976.395c-9.43 1.228-18.87 2.375-28.322 3.442a1829.308 1829.308 0 0 1-55.938 5.506 1957.204 1957.204 0 0 1-111.55 6.074c-18.984.504-37.963.74-56.936.705a1975.736 1975.736 0 0 1-225.989-13.146c-8.122-.963-16.243-1.996-24.365-3.045 6.298.809-4.577-.62-6.779-.929a1447.245 1447.245 0 0 1-15.486-2.254c-17.327-2.599-34.55-5.799-51.843-8.604-20.906-3.441-40.9-1.72-59.81 8.604a86.994 86.994 0 0 0-36.012 37.338c-8.156 16.862-10.582 35.221-14.23 53.34C4 342.193-1.678 361.688.473 380.288 5.1 420.431 33.165 453.054 73.53 460.35c37.975 6.882 76.156 12.457 114.44 17.206a2114.804 2114.804 0 0 0 489.988 2.822 25.814 25.814 0 0 1 21.003 7.339 25.806 25.806 0 0 1 7.491 20.948l-3.82 37.132-23.091 225.077a178840.62 178840.62 0 0 1-31.126 302.866c-2.203 21.84-2.512 44.36-6.659 65.94-6.539 33.93-29.509 54.77-63.027 62.39a439.172 439.172 0 0 1-93.569 10.94c-34.912.19-69.806-1.36-104.718-1.17-37.27.21-82.918-3.23-111.687-30.97-25.277-24.36-28.77-62.51-32.211-95.5-4.588-43.67-9.136-87.331-13.645-130.989l-25.293-242.766-16.363-157.077c-.276-2.598-.551-5.162-.809-7.778-1.962-18.737-15.228-37.079-36.134-36.133-17.894.791-38.232 16.002-36.133 36.133l12.13 116.454 25.087 240.89a378681.11 378681.11 0 0 1 21.388 205.306c1.377 13.11 2.667 26.26 4.112 39.37 7.864 71.65 62.58 110.26 130.339 121.13 39.575 6.37 80.113 7.68 120.273 8.33 51.482.83 103.48 2.81 154.118-6.52 75.038-13.77 131.337-63.87 139.372-141.59 2.295-22.44 4.589-44.88 6.883-67.33 7.628-74.241 15.245-148.487 22.85-222.739l24.881-242.61 11.408-111.188a25.812 25.812 0 0 1 20.785-22.696c21.456-4.181 41.967-11.322 57.229-27.651 24.295-25.998 29.13-59.895 20.544-94.067ZM72.43 365.835c.327-.155-.275 2.649-.533 3.957-.052-1.979.051-3.734.533-3.957Zm2.082 16.105c.172-.121.688.568 1.222 1.394-.809-.758-1.325-1.325-1.24-1.394h.018Zm2.048 2.701c.74 1.256 1.135 2.048 0 0Zm4.112 3.338h.103c0 .121.19.241.258.362a2.666 2.666 0 0 0-.378-.362h.017Zm720.124-4.99c-7.708 7.33-19.323 10.737-30.8 12.441-128.704 19.099-259.283 28.769-389.399 24.502-93.121-3.183-185.261-13.525-277.453-26.55-9.034-1.273-18.824-2.925-25.036-9.584-11.7-12.561-5.953-37.854-2.908-53.03 2.788-13.903 8.122-32.434 24.657-34.413 25.81-3.028 55.783 7.863 81.318 11.735a1539.798 1539.798 0 0 0 92.57 11.27c132.18 12.045 266.58 10.169 398.175-7.45a1661.346 1661.346 0 0 0 71.699-11.236c21.216-3.803 44.737-10.943 57.556 11.029 8.792 14.97 9.962 34.998 8.603 51.912a28.942 28.942 0 0 1-8.999 19.374h.017Z" d="m879.567 341.849-7.037-35.497c-6.315-31.849-20.648-61.943-53.34-73.454-10.479-3.683-22.369-5.265-30.404-12.888-8.035-7.622-10.41-19.46-12.268-30.438-3.442-20.149-6.676-40.315-10.204-60.429-3.045-17.293-5.454-36.719-13.386-52.583-10.324-21.302-31.746-33.76-53.048-42.001a306 306 0 0 0-33.363-10.324C613.297 10.195 557.342 5.033 502.591 2.09a1376 1376 0 0 0-197.169 3.27c-48.797 4.439-100.193 9.807-146.564 26.687-16.948 6.177-34.413 13.593-47.3 26.687-15.813 16.088-20.975 40.969-9.43 61.031 8.208 14.247 22.111 24.313 36.857 30.972a299 299 0 0 0 59.844 19.478c57.297 12.664 116.642 17.636 175.178 19.753a1334 1334 0 0 0 194.433-6.35 1107 1107 0 0 0 47.817-6.314c18.738-2.874 30.765-27.376 25.242-44.445-6.608-20.406-24.365-28.321-44.444-25.241-2.96.464-5.902.894-8.862 1.324l-2.133.31q-10.204 1.29-20.407 2.409a1083 1083 0 0 1-42.259 3.717c-31.626 2.202-63.337 3.217-95.031 3.269-31.144 0-62.305-.878-93.38-2.925a1187 1187 0 0 1-42.431-3.545 1020 1020 0 0 1-19.219-2.168l-6.092-.774-1.324-.189-6.315-.912c-12.905-1.945-25.81-4.181-38.577-6.883a5.8 5.8 0 0 1 0-11.322h.241c11.064-2.357 22.213-4.37 33.397-6.125q5.593-.878 11.219-1.72h.103c7.003-.465 14.041-1.722 21.009-2.547A1336 1336 0 0 1 469.538 73.1c29.577.86 59.138 2.599 88.578 5.593a979 979 0 0 1 18.927 2.116c2.409.293 4.835.636 7.262.93l4.886.705a678 678 0 0 1 42.517 7.725c20.889 4.543 47.714 6.022 57.005 28.907 2.96 7.261 4.302 15.331 5.936 22.953l2.082 9.722q.082.262.121.533 7.382 34.413 14.78 68.826a12.6 12.6 0 0 1-2.006 9.871 12.61 12.61 0 0 1-8.593 5.254h-.138l-3.011.413-2.976.395q-14.144 1.842-28.322 3.442a1829 1829 0 0 1-55.938 5.506 1957 1957 0 0 1-111.55 6.074q-28.476.757-56.936.705a1976 1976 0 0 1-225.989-13.146c-8.122-.963-16.243-1.996-24.365-3.045 6.298.809-4.577-.62-6.779-.929a1447 1447 0 0 1-15.486-2.254c-17.327-2.599-34.55-5.799-51.843-8.604-20.906-3.441-40.9-1.72-59.81 8.604a87 87 0 0 0-36.012 37.338c-8.156 16.862-10.582 35.221-14.23 53.34C4 342.193-1.678 361.688.473 380.288 5.1 420.431 33.165 453.054 73.53 460.35c37.975 6.882 76.156 12.457 114.44 17.206a2114.8 2114.8 0 0 0 489.988 2.822 25.81 25.81 0 0 1 21.003 7.339 25.8 25.8 0 0 1 7.491 20.948l-3.82 37.132-23.091 225.077a178841 178841 0 0 1-31.126 302.866c-2.203 21.84-2.512 44.36-6.659 65.94-6.539 33.93-29.509 54.77-63.027 62.39a439 439 0 0 1-93.569 10.94c-34.912.19-69.806-1.36-104.718-1.17-37.27.21-82.918-3.23-111.687-30.97-25.277-24.36-28.77-62.51-32.211-95.5q-6.882-65.504-13.645-130.989l-25.293-242.766-16.363-157.077c-.276-2.598-.551-5.162-.809-7.778-1.962-18.737-15.228-37.079-36.134-36.133-17.894.791-38.232 16.002-36.133 36.133l12.13 116.454 25.087 240.89a378681 378681 0 0 1 21.388 205.306c1.377 13.11 2.667 26.26 4.112 39.37 7.864 71.65 62.58 110.26 130.339 121.13 39.575 6.37 80.113 7.68 120.273 8.33 51.482.83 103.48 2.81 154.118-6.52 75.038-13.77 131.337-63.87 139.372-141.59 2.295-22.44 4.589-44.88 6.883-67.33q11.442-111.361 22.85-222.739l24.881-242.61 11.408-111.188a25.812 25.812 0 0 1 20.785-22.696c21.456-4.181 41.967-11.322 57.229-27.651 24.295-25.998 29.13-59.895 20.544-94.067M72.43 365.835c.327-.155-.275 2.649-.533 3.957-.052-1.979.051-3.734.533-3.957m2.082 16.105c.172-.121.688.568 1.222 1.394-.809-.758-1.325-1.325-1.24-1.394zm2.048 2.701c.74 1.256 1.135 2.048 0 0m4.112 3.338h.103c0 .121.19.241.258.362a2.7 2.7 0 0 0-.378-.362zm720.124-4.99c-7.708 7.33-19.323 10.737-30.8 12.441-128.704 19.099-259.283 28.769-389.399 24.502-93.121-3.183-185.261-13.525-277.453-26.55-9.034-1.273-18.824-2.925-25.036-9.584-11.7-12.561-5.953-37.854-2.908-53.03 2.788-13.903 8.122-32.434 24.657-34.413 25.81-3.028 55.783 7.863 81.318 11.735a1540 1540 0 0 0 92.57 11.27c132.18 12.045 266.58 10.169 398.175-7.45a1661 1661 0 0 0 71.699-11.236c21.216-3.803 44.737-10.943 57.556 11.029 8.792 14.97 9.962 34.998 8.603 51.912a28.94 28.94 0 0 1-8.999 19.374z"
/> />
</svg> </svg>
); );

View File

@@ -31,14 +31,14 @@ const KoFi = ({
<use x={97} y={83} href="#a" /> <use x={97} y={83} href="#a" />
<path <path
fillRule="evenodd" fillRule="evenodd"
d="m521.5 52.4 410 .2 12.5 2.1c56.6 9.6 99.9 28 143.5 61.2 11.7 8.9 33.5 30.2 42.5 41.6 31 39.2 50.6 86.9 59.2 143.8 3 19.5 3.2 65 .5 83.2-7.3 48.4-24.3 89.7-52.3 127.3-10.5 13.9-35.9 39.8-49.4 50.2-52.3 40.3-116.3 63.2-188.8 67.6-10.4.6-11.3.8-11.6 2.8-.3 1.1-1.1 7.5-2 14.1-7.1 54-32.5 98.7-70.7 124.1-16.3 10.9-38.6 20.3-58.8 25-9.9 2.3-102.7 3.2-354.1 3.6-230.9.3-217.6.6-239.5-4.9-41.1-10.2-73.8-34.9-91.3-69-10.6-20.6-14.9-39-16.2-68.3-1.8-38.7-3.2-261.9-2.7-402 .5-138.6.6-141.7 2.6-149.2 7.2-27.3 27.3-47.5 52.6-52.8 2.2-.4 188.5-.7 414-.6zm-4.6 57.6H117.3l-2 2.2c-4.9 5.4-4.7-1.7-5.1 148.8-.4 142.7 1.1 364 2.8 395.5.7 13.5 1.4 18.8 3.5 26 8.7 29.8 29 48 63.2 56.7l8.8 2.3 216-.3c118.8-.2 243.9-.7 278-1.1l62-.8 8.5-2.8c40.5-13.2 64.2-41.6 73.5-87.9 2.5-12.7 4.5-32.6 4.5-45.1 0-13.1 5-22.4 15.1-28l5.4-3 28-.6c16.4-.4 33-1.4 40-2.3 64.9-8.9 116.3-33.4 156-74.7 32.8-34.1 52.5-77.3 57.6-126.8 1.5-14.3.6-46.8-1.6-61.1-8.5-55.1-29.1-98-62.6-130.6-11.8-11.4-17.7-16-33.9-26.7-33.1-21.6-66.1-33.5-106-38.2-10.8-1.3-67.3-1.5-412.1-1.5z" d="m521.5 52.4 410 .2 12.5 2.1c56.6 9.6 99.9 28 143.5 61.2 11.7 8.9 33.5 30.2 42.5 41.6 31 39.2 50.6 86.9 59.2 143.8 3 19.5 3.2 65 .5 83.2-7.3 48.4-24.3 89.7-52.3 127.3-10.5 13.9-35.9 39.8-49.4 50.2-52.3 40.3-116.3 63.2-188.8 67.6-10.4.6-11.3.8-11.6 2.8-.3 1.1-1.1 7.5-2 14.1-7.1 54-32.5 98.7-70.7 124.1-16.3 10.9-38.6 20.3-58.8 25-9.9 2.3-102.7 3.2-354.1 3.6-230.9.3-217.6.6-239.5-4.9-41.1-10.2-73.8-34.9-91.3-69-10.6-20.6-14.9-39-16.2-68.3-1.8-38.7-3.2-261.9-2.7-402 .5-138.6.6-141.7 2.6-149.2 7.2-27.3 27.3-47.5 52.6-52.8 2.2-.4 188.5-.7 414-.6m-4.6 57.6H117.3l-2 2.2c-4.9 5.4-4.7-1.7-5.1 148.8-.4 142.7 1.1 364 2.8 395.5.7 13.5 1.4 18.8 3.5 26 8.7 29.8 29 48 63.2 56.7l8.8 2.3 216-.3c118.8-.2 243.9-.7 278-1.1l62-.8 8.5-2.8c40.5-13.2 64.2-41.6 73.5-87.9 2.5-12.7 4.5-32.6 4.5-45.1 0-13.1 5-22.4 15.1-28l5.4-3 28-.6c16.4-.4 33-1.4 40-2.3 64.9-8.9 116.3-33.4 156-74.7 32.8-34.1 52.5-77.3 57.6-126.8 1.5-14.3.6-46.8-1.6-61.1-8.5-55.1-29.1-98-62.6-130.6-11.8-11.4-17.7-16-33.9-26.7-33.1-21.6-66.1-33.5-106-38.2-10.8-1.3-67.3-1.5-412.1-1.5"
/> />
<path <path
fillRule="evenodd" fillRule="evenodd"
d="M939.5 185.4c33.5 6.1 61.7 21.9 83.2 46.5 17.6 20.1 27.6 40.3 33.4 67 4.2 19.7 3.7 56.3-1.3 79.1-10.6 49.7-36.5 83.1-81.3 105-15.9 7.8-27.3 11.6-38.6 13-14.1 1.8-71.1 2.5-78.1 1.1-11-2.3-19.3-10.4-22.3-21.7-2.3-8.8-2.3-261 0-269.8 2.8-10.8 10-18.1 20.8-21.1 7.6-2.2 71.2-1.5 84.2.9zM903.8 241H891v199h9.3c5 0 14.3-.5 20.6-1 9.5-.9 12.8-1.7 20.6-4.9 41.1-16.9 59.5-47.6 59.5-99.2 0-23.9-3.7-38.9-13.3-53.7-14.5-22.4-36.2-36-62.7-39.2-4.6-.5-14.2-1-21.2-1z" d="M939.5 185.4c33.5 6.1 61.7 21.9 83.2 46.5 17.6 20.1 27.6 40.3 33.4 67 4.2 19.7 3.7 56.3-1.3 79.1-10.6 49.7-36.5 83.1-81.3 105-15.9 7.8-27.3 11.6-38.6 13-14.1 1.8-71.1 2.5-78.1 1.1-11-2.3-19.3-10.4-22.3-21.7-2.3-8.8-2.3-261 0-269.8 2.8-10.8 10-18.1 20.8-21.1 7.6-2.2 71.2-1.5 84.2.9M903.8 241H891v199h9.3c5 0 14.3-.5 20.6-1 9.5-.9 12.8-1.7 20.6-4.9 41.1-16.9 59.5-47.6 59.5-99.2 0-23.9-3.7-38.9-13.3-53.7-14.5-22.4-36.2-36-62.7-39.2-4.6-.5-14.2-1-21.2-1"
/> />
<path <path
d="M561.8 245.6c-28.7 5.2-60.3 22.1-83.5 44.7l-6.2 6-9.8-9.4c-20.6-19.7-46.1-33.2-74.3-39.4-11.6-2.5-36.4-3.1-49-1.1-41.8 6.6-73 31-84.8 66.1-6 17.6-7.4 42.4-3.8 63 4.9 27.5 17.3 56.3 32.4 75.3C302 475 359.3 531.2 433 598c37.5 34 35.1 32.4 42 29.3 7.5-3.4 96.9-88.3 144.9-137.7 38.7-39.7 49.3-52.6 59-71.7 11.8-23.2 16.7-44.3 15.8-68.7-.7-22.4-6.3-40.4-17.7-57.1-16.1-23.8-44.3-41.1-75.7-46.6-10.5-1.8-29.2-1.8-39.5.1z" d="M561.8 245.6c-28.7 5.2-60.3 22.1-83.5 44.7l-6.2 6-9.8-9.4c-20.6-19.7-46.1-33.2-74.3-39.4-11.6-2.5-36.4-3.1-49-1.1-41.8 6.6-73 31-84.8 66.1-6 17.6-7.4 42.4-3.8 63 4.9 27.5 17.3 56.3 32.4 75.3C302 475 359.3 531.2 433 598c37.5 34 35.1 32.4 42 29.3 7.5-3.4 96.9-88.3 144.9-137.7 38.7-39.7 49.3-52.6 59-71.7 11.8-23.2 16.7-44.3 15.8-68.7-.7-22.4-6.3-40.4-17.7-57.1-16.1-23.8-44.3-41.1-75.7-46.6-10.5-1.8-29.2-1.8-39.5.1"
style={{ style={{
fill: '#ff504f', fill: '#ff504f',
}} }}

View 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;

View File

@@ -13,3 +13,4 @@ export { default as PartnerTax } from './PartnerTax';
export { default as Poison } from './Poison'; export { default as Poison } from './Poison';
export { default as ResetGame } from './ResetGame'; export { default as ResetGame } from './ResetGame';
export { default as Skull } from './Skull'; export { default as Skull } from './Skull';
export { default as Trinket } from './Trinket';

View 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

View File

@@ -3,9 +3,18 @@ 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 { InitialGameSettings, Orientation, Settings } from '../Types/Settings'; import {
InitialGameSettings,
Settings,
defaultInitialGameSettings,
defaultSettings,
initialGameSettingsSchema,
settingsSchema,
} from '../Types/Settings';
import { gte as semverGreaterThanOrEqual } from 'semver';
export const GlobalSettingsProvider = ({ export const GlobalSettingsProvider = ({
children, children,
@@ -14,46 +23,130 @@ 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 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>(
savedPlaying ? savedPlaying === 'true' : false
);
const setPlayingAndLocalStorage = (playing: boolean) => {
setPlaying(playing);
localStorage.setItem('playing', String(playing));
};
const savedPreStartComplete = localStorage.getItem('preStartComplete');
const [preStartCompleted, setPreStartCompleted] = useState<boolean>(
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) => {
const [initialGameSettings, setInitialSettings] = setShowPlay(showPlay);
useState<InitialGameSettings | null>( localStorage.setItem('showPlay', String(showPlay));
savedGameSettings ? JSON.parse(savedGameSettings) : null
);
const setInitialGameSettings = (initialGameSettings: InitialGameSettings) => {
const defaultSettings: InitialGameSettings = {
numberOfPlayers: 4,
startingLifeTotal: 40,
useCommanderDamage: true,
orientation: Orientation.Landscape,
gameFormat: 'commander',
};
setInitialSettings({ ...defaultSettings, ...initialGameSettings });
}; };
const [settings, setSettings] = useState<Settings>( const savedSettings = localStorage.getItem('settings');
const [randomizingPlayer, setRandomizingPlayer] = useState<boolean>(
savedSettings savedSettings
? JSON.parse(savedSettings) ? Boolean(JSON.parse(savedSettings).preStartMode === 'random-king')
: { goFullscreenOnStart: true, keepAwake: true, showStartingPlayer: true } : true
);
const [settings, setSettings] = useState<Settings>(
savedSettings ? JSON.parse(savedSettings) : defaultSettings
); );
useEffect(() => { const setSettingsAndLocalStorage = (settings: Settings) => {
setSettings(settings);
localStorage.setItem('settings', JSON.stringify(settings));
};
const savedGameSettings = localStorage.getItem('initialGameSettings');
const [initialGameSettings, setInitialGameSettings] =
useState<InitialGameSettings>(
savedGameSettings
? JSON.parse(savedGameSettings)
: defaultInitialGameSettings
);
const setInitialGameSettingsAndLocalStorage = (
initialGameSettings: InitialGameSettings
) => {
setInitialGameSettings(initialGameSettings);
localStorage.setItem( localStorage.setItem(
'initialGameSettings', 'initialGameSettings',
JSON.stringify(initialGameSettings) JSON.stringify(initialGameSettings)
); );
}, [initialGameSettings]); };
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(() => { useEffect(() => {
localStorage.setItem('settings', JSON.stringify(settings)); // If there are no saved settings, set default settings
}, [settings]); if (!savedSettings) {
setSettingsAndLocalStorage(defaultSettings);
return;
}
const parsedSettings = settingsSchema.safeParse(JSON.parse(savedSettings));
// If saved settings are not valid, remove them
if (!parsedSettings.success) {
console.error('invalid settings, resetting to default settings');
setSettingsAndLocalStorage(defaultSettings);
return;
}
localStorage.setItem('settings', JSON.stringify(parsedSettings.data));
}, [settings, savedSettings]);
// Set initial game settings if they are not valid
useEffect(() => {
if (!savedGameSettings) {
setInitialGameSettingsAndLocalStorage(defaultInitialGameSettings);
return;
}
//parse existing game settings with zod schema
const parsedInitialGameSettings =
initialGameSettingsSchema.safeParse(initialGameSettings);
if (!parsedInitialGameSettings.success) {
console.error('invalid game settings, resetting to default settings');
setInitialGameSettingsAndLocalStorage(defaultInitialGameSettings);
return;
}
localStorage.setItem(
'initialGameSettings',
JSON.stringify(parsedInitialGameSettings.data)
);
}, [initialGameSettings, savedGameSettings]);
const [isFullscreen, setIsFullscreen] = useState(false); const [isFullscreen, setIsFullscreen] = useState(false);
@@ -70,6 +163,11 @@ export const GlobalSettingsProvider = ({
}; };
}, []); }, []);
const [isLatestVersion, setIsLatestVersion] = useState(false);
const [remoteVersion, setRemoteVersion] = useState<string | undefined>(
undefined
);
const { isSupported, release, released, request, type } = useWakeLock(); const { isSupported, release, released, request, type } = useWakeLock();
const active = settings.keepAwake; const active = settings.keepAwake;
@@ -78,14 +176,6 @@ export const GlobalSettingsProvider = ({
request(); request();
} }
const removeLocalStorage = async () => {
localStorage.removeItem('initialGameSettings');
localStorage.removeItem('players');
localStorage.removeItem('playing');
localStorage.removeItem('showPlay');
setShowPlay(localStorage.getItem('showPlay') === 'true' ?? false);
};
const ctxValue = useMemo((): GlobalSettingsContextType => { const ctxValue = useMemo((): GlobalSettingsContextType => {
const goToStart = async () => { const goToStart = async () => {
const currentPlayers = localStorage.getItem('players'); const currentPlayers = localStorage.getItem('players');
@@ -100,7 +190,6 @@ export const GlobalSettingsProvider = ({
}; };
const toggleWakeLock = async () => { const toggleWakeLock = async () => {
console.log('on press', active);
if (active) { if (active) {
setSettings({ ...settings, keepAwake: false }); setSettings({ ...settings, keepAwake: false });
release(); release();
@@ -127,6 +216,56 @@ export const GlobalSettingsProvider = ({
} }
}; };
const setPreStartCompletedAndLocalStorage = (preStartComplete: boolean) => {
setPreStartCompleted(preStartComplete);
localStorage.setItem('playing', String(playing));
};
async function checkForNewVersion(source: 'settings' | 'start_menu') {
try {
const result = await fetch(
'https://api.github.com/repos/Vikeo/LifeTrinket/releases/latest',
{
headers: {
Authorization: `Bearer ${
import.meta.env.VITE_REPO_READ_ACCESS_TOKEN
}`,
Accept: 'application/vnd.github+json',
'X-GitHub-Api-Version': '2022-11-28',
},
}
);
const data = await result.json();
if (!data.name) {
setRemoteVersion(undefined);
setIsLatestVersion(false);
return;
}
setRemoteVersion(data.name);
const isLatest = semverGreaterThanOrEqual(
import.meta.env.VITE_APP_VERSION,
data.name
);
if (isLatest) {
setIsLatestVersion(true);
return;
}
analytics.trackEvent(`${source}_has_new_version`, {
remoteVersion: data.name,
installedVersion: import.meta.env.VITE_APP_VERSION,
});
setIsLatestVersion(false);
} catch (error) {
console.error('error getting latest version string', error);
}
}
return { return {
fullscreen: { isFullscreen, enableFullscreen, disableFullscreen }, fullscreen: { isFullscreen, enableFullscreen, disableFullscreen },
wakeLock: { wakeLock: {
@@ -139,24 +278,45 @@ export const GlobalSettingsProvider = ({
}, },
goToStart, goToStart,
showPlay, showPlay,
setShowPlay, setShowPlay: setShowPlayAndLocalStorage,
playing,
setPlaying: setPlayingAndLocalStorage,
initialGameSettings, initialGameSettings,
setInitialGameSettings, setInitialGameSettings,
settings, settings,
setSettings, setSettings: setSettingsAndLocalStorage,
randomizingPlayer,
setRandomizingPlayer,
isPWA: window?.matchMedia('(display-mode: standalone)').matches, isPWA: window?.matchMedia('(display-mode: standalone)').matches,
preStartCompleted,
setPreStartCompleted: setPreStartCompletedAndLocalStorage,
savedGame,
saveCurrentGame: setCurrentGameAndLocalStorage,
version: {
installedVersion: import.meta.env.VITE_APP_VERSION,
remoteVersion,
isLatest: isLatestVersion,
checkForNewVersion,
},
}; };
}, [ }, [
active,
analytics,
initialGameSettings,
isFullscreen, isFullscreen,
isSupported, isSupported,
release, release,
active,
request, request,
settings,
showPlay,
type, type,
showPlay,
playing,
initialGameSettings,
settings,
randomizingPlayer,
preStartCompleted,
savedGame,
remoteVersion,
isLatestVersion,
analytics,
]); ]);
return ( return (

View File

@@ -7,6 +7,17 @@ import { InitialGameSettings } from '../Types/Settings';
export const PlayersProvider = ({ children }: { children: ReactNode }) => { export const PlayersProvider = ({ children }: { children: ReactNode }) => {
const savedPlayers = localStorage.getItem('players'); const savedPlayers = localStorage.getItem('players');
const savedStartingPlayerIndex = localStorage.getItem('startingPlayerIndex');
const [startingPlayerIndex, setStartingPlayerIndex] = useState<number>(
savedStartingPlayerIndex ? parseInt(savedStartingPlayerIndex) : -1
);
const setStartingPlayerIndexAndLocalStorage = (index: number) => {
setStartingPlayerIndex(index);
localStorage.setItem('startingPlayerIndex', String(index));
};
const [players, setPlayers] = useState<Player[]>( const [players, setPlayers] = useState<Player[]>(
savedPlayers ? JSON.parse(savedPlayers) : [] savedPlayers ? JSON.parse(savedPlayers) : []
); );
@@ -50,9 +61,7 @@ export const PlayersProvider = ({ children }: { children: ReactNode }) => {
return; return;
} }
const startingPlayerIndex = Math.floor( const newStartingPlayerIndex = Math.floor(Math.random() * players.length);
Math.random() * initialGameSettings.numberOfPlayers
);
players.forEach((player: Player) => { players.forEach((player: Player) => {
player.commanderDamage.map((damage) => { player.commanderDamage.map((damage) => {
@@ -65,16 +74,9 @@ export const PlayersProvider = ({ children }: { children: ReactNode }) => {
}); });
player.lifeTotal = initialGameSettings.startingLifeTotal; player.lifeTotal = initialGameSettings.startingLifeTotal;
player.hasLost = false; player.hasLost = false;
const isStartingPlayer = player.index === startingPlayerIndex; player.isStartingPlayer = newStartingPlayerIndex === player.index;
player.isStartingPlayer = isStartingPlayer;
if (player.isStartingPlayer) {
player.showStartingPlayer = true;
}
updatePlayer(player); updatePlayer(player);
}); });
@@ -87,8 +89,10 @@ export const PlayersProvider = ({ children }: { children: ReactNode }) => {
updatePlayer, updatePlayer,
updateLifeTotal, updateLifeTotal,
resetCurrentGame, resetCurrentGame,
startingPlayerIndex,
setStartingPlayerIndex: setStartingPlayerIndexAndLocalStorage,
}; };
}, [players]); }, [players, startingPlayerIndex]);
return ( return (
<PlayersContext.Provider value={ctxValue}> <PlayersContext.Provider value={ctxValue}>

View File

@@ -6,17 +6,17 @@ export type Player = {
commanderDamage: CommanderDamage[]; commanderDamage: CommanderDamage[];
extraCounters: ExtraCounter[]; extraCounters: ExtraCounter[];
isStartingPlayer: boolean; isStartingPlayer: boolean;
showStartingPlayer: boolean;
hasLost: boolean; hasLost: boolean;
isSide: boolean;
}; };
export type PlayerSettings = { export type PlayerSettings = {
rotation: Rotation; rotation: Rotation;
useCommanderDamage: boolean; useCommanderDamage: boolean;
usePartner?: boolean; usePartner: boolean;
usePoison?: boolean; usePoison: boolean;
useEnergy?: boolean; useEnergy: boolean;
useExperience?: boolean; useExperience: boolean;
}; };
type ExtraCounter = { type ExtraCounter = {

View File

@@ -1,13 +1,30 @@
import { z } from 'zod';
export enum Orientation { export enum Orientation {
OppositeLandscape = 'opposite-landscape', OppositeLandscape = 'opposite-landscape',
Landscape = 'landscape', Landscape = 'landscape',
Portrait = 'portrait', Portrait = 'portrait',
} }
export enum GameFormat {
Commander = 'commander',
Standard = 'standard',
TwoHeadedGiant = 'two-headed-giant',
}
export enum PreStartMode {
None = 'none',
RandomKing = 'random-king',
FingerGame = 'finger-game',
Trivia = 'trivia',
}
export type Settings = { export type Settings = {
keepAwake: boolean; keepAwake: boolean;
showStartingPlayer: boolean; showStartingPlayer: boolean;
showPlayerMenuCog: boolean;
goFullscreenOnStart: boolean; goFullscreenOnStart: boolean;
preStartMode: PreStartMode;
}; };
export type InitialGameSettings = { export type InitialGameSettings = {
@@ -18,4 +35,34 @@ export type InitialGameSettings = {
orientation: Orientation; orientation: Orientation;
}; };
type GameFormat = 'commander' | 'standard' | 'two-headed-giant'; export const initialGameSettingsSchema = z.object({
startingLifeTotal: z.number().min(1).max(200),
useCommanderDamage: z.boolean(),
gameFormat: z.nativeEnum(GameFormat),
numberOfPlayers: z.number().min(1).max(6),
orientation: z.nativeEnum(Orientation),
});
export const defaultInitialGameSettings = {
numberOfPlayers: 4,
startingLifeTotal: 40,
useCommanderDamage: true,
orientation: Orientation.Landscape,
gameFormat: GameFormat.Commander,
};
export const settingsSchema = z.object({
keepAwake: z.boolean(),
showStartingPlayer: z.boolean(),
showPlayerMenuCog: z.boolean(),
goFullscreenOnStart: z.boolean(),
preStartMode: z.nativeEnum(PreStartMode),
});
export const defaultSettings: Settings = {
goFullscreenOnStart: true,
keepAwake: true,
showStartingPlayer: true,
showPlayerMenuCog: true,
preStartMode: PreStartMode.None,
};

View File

@@ -0,0 +1,87 @@
type RGBA = {
red: number;
green: number;
blue: number;
alpha: number;
};
export const hexToRgb = (hex: string): RGBA => {
hex = hex.replace(/^#/, '');
let alpha = 255;
if (hex.length === 8) {
alpha = parseInt(hex.slice(6, 8), 16);
hex = hex.substring(0, 6);
}
if (hex.length === 4) {
alpha = parseInt(hex.slice(3, 4).repeat(2), 16);
hex = hex.substring(0, 3);
}
if (hex.length === 3) {
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
}
const num = parseInt(hex, 16);
const red = num >> 16;
const green = (num >> 8) & 255;
const blue = num & 255;
return { red, green, blue, alpha };
};
export const luminance = (a: number, b: number) => {
const l1 = Math.max(a, b);
const l2 = Math.min(a, b);
return (l1 + 0.05) / (l2 + 0.05);
};
export const rgbContrast = (a: RGBA, b: RGBA) => {
return luminance(relativeLuminance(a), relativeLuminance(b));
};
// calculate the color contrast ratio
export const checkContrast = (hexC1: string, hexC2: string) => {
const color1rgb = hexToRgb(hexC1);
const color2rgb = hexToRgb(hexC2);
const contrast = rgbContrast(color1rgb, color2rgb);
if (contrast >= 7) {
return 'AAA';
}
if (contrast >= 4.5) {
return 'AA';
}
if (contrast >= 3) {
return 'AA Large';
}
return 'Fail';
};
// red, green, and blue coefficients
const rc = 0.2126;
const gc = 0.7152;
const bc = 0.0722;
// low-gamma adjust coefficient
const lowc = 1 / 12.92;
function adjustGamma(input: number) {
return Math.pow((input + 0.055) / 1.055, 2.4);
}
export const relativeLuminance = (rgb: RGBA) => {
const rsrgb = rgb.red / 255;
const gsrgb = rgb.green / 255;
const bsrgb = rgb.blue / 255;
const r = rsrgb <= 0.03928 ? rsrgb * lowc : adjustGamma(rsrgb);
const g = gsrgb <= 0.03928 ? gsrgb * lowc : adjustGamma(gsrgb);
const b = bsrgb <= 0.03928 ? bsrgb * lowc : adjustGamma(bsrgb);
return r * rc + g * gc + b * bc;
};

View File

@@ -2,7 +2,19 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
html {
overflow: hidden;
}
body { body {
overflow: auto;
}
html,
body {
height: 100%;
position: relative;
background-color: theme('colors.background.default');
margin: 0; margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
@@ -10,8 +22,61 @@ body {
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
#root {
touch-action: manipulation;
}
code { code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace; monospace;
} }
// hide scrollbar globally
::-webkit-scrollbar {
display: none;
}
/* width */
::-webkit-scrollbar {
width: 10px;
}
/* Track */
::-webkit-scrollbar-track {
background-color: theme('colors.background.default');
}
/* Handle */
::-webkit-scrollbar-thumb {
background: theme('colors.primary.dark');
border-radius: 32px;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: theme('colors.primary.main');
}
* {
scrollbar-width: none;
-ms-overflow-style: none;
}
@layer utilities {
.pointer-events-all {
pointer-events: all;
}
.webkit-user-select-none {
-webkit-touch-callout: none;
-webkit-tap-highlight-color: transparent;
-moz-user-select: -moz-none;
-webkit-user-select: none;
-ms-user-select: none;
}
.show-scrollbar {
scrollbar-width: auto;
-ms-overflow-style: auto;
}
}

10
src/vite-env.d.ts vendored
View File

@@ -1 +1,11 @@
/// <reference types="vite/client" /> /// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_APP_VERSION: string;
readonly VITE_REPO_READ_ACCESS_TOKEN: string;
readonly VITE_FIREBASE_ANALYTICS_API_KEY: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}

View File

@@ -1,41 +0,0 @@
import tailwindcssGridAreas from '@savvywombat/tailwindcss-grid-areas';
/** @type {import('tailwindcss').Config} */
export default {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
theme: {
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 player2 player2 player2 player2 player3',
'player0 player4 player4 player4 player4 player5 player5 player5 player5 player3',
],
},
},
},
plugins: [tailwindcssGridAreas],
};

113
tailwind.config.ts Normal file
View File

@@ -0,0 +1,113 @@
//@ts-expect-error - tailwindcss-grid-areas does not have typescript support
import tailwindcssGridAreas from '@savvywombat/tailwindcss-grid-areas';
import type { Config } from 'tailwindcss';
export const baseColors = {
primary: {
main: '#3E7D78',
dark: '#2D5F5B',
},
secondary: {
main: '#284F4C',
dark: '#1B3B38',
},
background: {
default: '#08253B',
backdrop: 'rgba(0, 0, 0, 0.3)',
settings: 'rgba(20, 20, 0, 0.9)',
},
icons: {
dark: '#00000080',
light: '#ffffff4f',
},
text: {
primary: '#F5F5F5',
secondary: '#76A6A5',
},
action: {
disabled: '#234A47',
},
common: {
white: '#F9FFE3',
black: '#000000',
},
lifeCounter: {
text: 'rgba(0, 0, 0, 0.4)',
lostWrapper: '#000000',
},
interface: {
loseButton: {
background: '#43434380',
},
recentDifference: {
background: 'rgba(255, 255, 255, 0.6);',
text: '#333333',
},
},
};
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}'],
theme: {
screens: {
modalSm: '548px',
},
extend: {
gridTemplateAreas: twGridTemplateAreas,
colors: baseColors,
keyframes: {
fadeOut: {
'0%': {
opacity: '1',
},
'33%': {
opacity: '0.6',
},
'100%': {
opacity: '0',
},
},
},
animation: {
fadeOut: 'fadeOut 3s 1s ease-out forwards',
},
fontSize: {
xxs: ['0.625rem', '1rem'],
},
},
},
plugins: [tailwindcssGridAreas],
} satisfies Config;
// #98FF98

View File

@@ -1,13 +1,32 @@
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc'; import { VitePWA } from 'vite-plugin-pwa';
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [
VitePWA({
registerType: 'autoUpdate',
workbox: {
clientsClaim: true,
skipWaiting: true,
},
}),
],
build: { build: {
minify: 'esbuild', minify: 'esbuild',
rollupOptions: { rollupOptions: {
external: ['babel-plugin-macros'], external: ['babel-plugin-macros'],
}, },
}, },
define: {
'import.meta.env.VITE_APP_VERSION': JSON.stringify(
process.env.npm_package_version
),
VITE_REPO_READ_ACCESS_TOKEN: JSON.stringify(
process.env.VITE_REPO_READ_ACCESS_TOKEN
),
VITE_FIREBASE_ANALYTICS_API_KEY: JSON.stringify(
process.env.VITE_FIREBASE_ANALYTICS_API_KEY
),
},
}); });

6990
yarn.lock

File diff suppressed because it is too large Load Diff