Browse Source

Change single quote for double quote

Jeremie Pardou-Piquemal 3 years ago
parent
commit
7e540d1310

+ 1 - 1
.eslintrc

@@ -12,5 +12,5 @@
     "semi": "error"
   },
   "plugins": ["react-hooks"],
-  "extends": ["plugin:react-hooks/recommended"]
+  "extends": ["plugin:react-hooks/recommended", "plugin:prettier/recommended"]
 }

+ 1 - 1
.prettierrc

@@ -2,5 +2,5 @@
   "trailingComma": "es5",
   "tabWidth": 2,
   "semi": true,
-  "singleQuote": true
+  "singleQuote": false
 }

+ 39 - 0
package-lock.json

@@ -5428,6 +5428,15 @@
         }
       }
     },
+    "eslint-config-prettier": {
+      "version": "6.11.0",
+      "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz",
+      "integrity": "sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==",
+      "dev": true,
+      "requires": {
+        "get-stdin": "^6.0.0"
+      }
+    },
     "eslint-config-react-app": {
       "version": "5.2.1",
       "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-5.2.1.tgz",
@@ -5634,6 +5643,15 @@
         }
       }
     },
+    "eslint-plugin-prettier": {
+      "version": "3.1.4",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz",
+      "integrity": "sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg==",
+      "dev": true,
+      "requires": {
+        "prettier-linter-helpers": "^1.0.0"
+      }
+    },
     "eslint-plugin-react": {
       "version": "7.20.0",
       "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.20.0.tgz",
@@ -6057,6 +6075,12 @@
       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
       "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="
     },
+    "fast-diff": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+      "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+      "dev": true
+    },
     "fast-glob": {
       "version": "2.2.7",
       "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz",
@@ -6504,6 +6528,12 @@
       "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
       "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g=="
     },
+    "get-stdin": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
+      "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
+      "dev": true
+    },
     "get-stream": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
@@ -10951,6 +10981,15 @@
       "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==",
       "dev": true
     },
+    "prettier-linter-helpers": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+      "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+      "dev": true,
+      "requires": {
+        "fast-diff": "^1.1.2"
+      }
+    },
     "pretty-bytes": {
       "version": "5.3.0",
       "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz",

+ 2 - 0
package.json

@@ -55,6 +55,8 @@
     "@testing-library/react": "^9.5.0",
     "@testing-library/user-event": "^7.2.1",
     "babel-eslint": "^10.1.0",
+    "eslint-config-prettier": "^6.11.0",
+    "eslint-plugin-prettier": "^3.1.4",
     "eslint-plugin-react": "^7.20.0",
     "eslint-plugin-react-hooks": "^4.0.4",
     "nodemon": "^2.0.4",

+ 11 - 11
src/App.js

@@ -1,23 +1,23 @@
-import React from 'react';
-import './App.css';
+import React from "react";
+import "./App.css";
 
 import {
   BrowserRouter as Router,
   Route,
   Switch,
   Redirect,
-} from 'react-router-dom';
+} from "react-router-dom";
 
-import { Provider } from '@scripters/use-socket.io';
-import { RecoilRoot } from 'recoil';
-import { nanoid } from 'nanoid';
-import { useParams } from 'react-router-dom';
+import { Provider } from "@scripters/use-socket.io";
+import { RecoilRoot } from "recoil";
+import { nanoid } from "nanoid";
+import { useParams } from "react-router-dom";
 
-import { C2CProvider } from './hooks/useC2C';
-import BoardView from './views/BoardView';
+import { C2CProvider } from "./hooks/useC2C";
+import BoardView from "./views/BoardView";
 
-const SOCKET_URL = process.env.REACT_APP_SOCKET_URL || 'http://localhost:4000';
-const SOCKET_PATH = process.env.REACT_APP_SOCKET_PATH || '/socket.io';
+const SOCKET_URL = process.env.REACT_APP_SOCKET_URL || "http://localhost:4000";
+const SOCKET_PATH = process.env.REACT_APP_SOCKET_PATH || "/socket.io";
 
 const SOCKET_OPTIONS = {
   forceNew: true,

+ 4 - 4
src/App.test.js

@@ -1,8 +1,8 @@
-import React from 'react';
-import { render } from '@testing-library/react';
-import App from './App';
+import React from "react";
+import { render } from "@testing-library/react";
+import App from "./App";
 
-test('renders learn react link', () => {
+test("renders learn react link", () => {
   const { getByText } = render(<App />);
   const linkElement = getByText(/learn react/i);
   expect(linkElement).toBeInTheDocument();

+ 11 - 11
src/components/ActionPane.js

@@ -1,11 +1,11 @@
-import React from 'react';
-import { useRecoilValue } from 'recoil';
-import { PanZoomRotateState } from '../components/PanZoomRotate';
-import { selectedItemsAtom } from '../components/Selector';
-import { ItemListAtom } from '../components/Items';
-import { useRecoilState, useSetRecoilState } from 'recoil';
-import { insideClass } from '../utils';
-import { useC2C } from '../hooks/useC2C';
+import React from "react";
+import { useRecoilValue } from "recoil";
+import { PanZoomRotateState } from "../components/PanZoomRotate";
+import { selectedItemsAtom } from "../components/Selector";
+import { ItemListAtom } from "../components/Items";
+import { useRecoilState, useSetRecoilState } from "recoil";
+import { insideClass } from "../utils";
+import { useC2C } from "../hooks/useC2C";
 
 const ActionPane = ({ children }) => {
   const [c2c] = useC2C();
@@ -38,7 +38,7 @@ const ActionPane = ({ children }) => {
         x: (e.clientX - left) / panZoomRotate.scale,
         y: (e.clientY - top) / panZoomRotate.scale,
       };
-      const foundElement = insideClass(e.target, 'item');
+      const foundElement = insideClass(e.target, "item");
       if (foundElement) {
         if (!selectedItems.includes(foundElement.id)) {
           setSelectedItems([foundElement.id]);
@@ -51,7 +51,7 @@ const ActionPane = ({ children }) => {
         actionRef.current.prevY = point.y;
         actionRef.current.moving = true;
         actionRef.current.itemId = foundElement.id;
-        wrapperRef.current.style.cursor = 'move';
+        wrapperRef.current.style.cursor = "move";
         e.stopPropagation();
       }
     }
@@ -98,7 +98,7 @@ const ActionPane = ({ children }) => {
   const onMouseUp = (e) => {
     if (actionRef.current.moving === true) {
       actionRef.current = { moving: false };
-      wrapperRef.current.style.cursor = 'auto';
+      wrapperRef.current.style.cursor = "auto";
     }
   };
 

+ 5 - 5
src/components/AvailableItems.js

@@ -1,10 +1,10 @@
-import React from 'react';
-import { nanoid } from 'nanoid';
-import { useRecoilState, atom } from 'recoil';
-import { ItemListAtom } from './Items';
+import React from "react";
+import { nanoid } from "nanoid";
+import { useRecoilState, atom } from "recoil";
+import { ItemListAtom } from "./Items";
 
 export const AvailableItemListAtom = atom({
-  key: 'availableItemList',
+  key: "availableItemList",
   default: [],
 });
 

+ 10 - 10
src/components/Board.js

@@ -1,18 +1,18 @@
-import React from 'react';
-import Items from './Items';
-import Selector from '../components/Selector';
-import ActionPane from './ActionPane';
-import CursorPane from './CursorPane';
+import React from "react";
+import Items from "./Items";
+import Selector from "../components/Selector";
+import ActionPane from "./ActionPane";
+import CursorPane from "./CursorPane";
 
 export const Board = ({ user, users, config }) => {
   if (!config.size) {
     return (
       <p
         style={{
-          position: 'fixed',
-          top: '40vh',
-          width: '100vw',
-          textAlign: 'center',
+          position: "fixed",
+          top: "40vh",
+          width: "100vw",
+          textAlign: "center",
         }}
       >
         Please select a game…
@@ -28,7 +28,7 @@ export const Board = ({ user, users, config }) => {
             className="content"
             style={{
               background:
-                'repeating-linear-gradient(45deg, #606dbc60, #606dbc60 10px, #46529860 10px, #46529860 20px)',
+                "repeating-linear-gradient(45deg, #606dbc60, #606dbc60 10px, #46529860 10px, #46529860 20px)",
               width: `${config.size}px`,
               height: `${config.size}px`,
             }}

+ 12 - 12
src/components/Cursor.js

@@ -1,16 +1,16 @@
-import React from 'react';
+import React from "react";
 
-export const Cursor = ({ color = '#666', size = 40, pos, text }) => (
+export const Cursor = ({ color = "#666", size = 40, pos, text }) => (
   <div
     style={{
-      position: 'fixed',
+      position: "fixed",
       top: pos.y - size / 2,
       left: pos.x - size / 2,
       //transition: 'left 100ms, top 100ms',
-      opacity: '0.7',
-      display: 'flex',
-      flexDirection: 'row',
-      alignItems: 'center',
+      opacity: "0.7",
+      display: "flex",
+      flexDirection: "row",
+      alignItems: "center",
     }}
   >
     <svg viewBox={`0 0 1024 1024`} width={size} height={size}>
@@ -26,11 +26,11 @@ export const Cursor = ({ color = '#666', size = 40, pos, text }) => (
     <div
       style={{
         color,
-        maxWidth: '5em',
-        overflow: 'hidden',
-        textOverflow: 'ellipsis',
-        marginLeft: '0.5em',
-        whiteSpace: 'nowrap',
+        maxWidth: "5em",
+        overflow: "hidden",
+        textOverflow: "ellipsis",
+        marginLeft: "0.5em",
+        whiteSpace: "nowrap",
       }}
     >
       {text}

+ 7 - 7
src/components/CursorPane.js

@@ -1,8 +1,8 @@
-import React from 'react';
-import Cursors from '../components/Cursors';
-import { useC2C } from '../hooks/useC2C';
-import { PanZoomRotateState } from '../components/PanZoomRotate';
-import { useRecoilValue } from 'recoil';
+import React from "react";
+import Cursors from "../components/Cursors";
+import { useC2C } from "../hooks/useC2C";
+import { PanZoomRotateState } from "../components/PanZoomRotate";
+import { useRecoilValue } from "recoil";
 
 export const Board = ({ children, user, users }) => {
   const [c2c] = useC2C();
@@ -10,7 +10,7 @@ export const Board = ({ children, user, users }) => {
 
   const onMouseMove = (e) => {
     const { top, left } = e.currentTarget.getBoundingClientRect();
-    c2c.publish('cursorMove', {
+    c2c.publish("cursorMove", {
       userId: user.id,
       pos: {
         x: (e.clientX - left) / panZoomRotate.scale,
@@ -20,7 +20,7 @@ export const Board = ({ children, user, users }) => {
   };
 
   const onLeave = (e) => {
-    c2c.publish('cursorOff', {
+    c2c.publish("cursorOff", {
       userId: user.id,
     });
   };

+ 5 - 5
src/components/Cursors.js

@@ -1,6 +1,6 @@
-import React from 'react';
-import { useC2C } from '../hooks/useC2C';
-import Cursor from './Cursor';
+import React from "react";
+import { useC2C } from "../hooks/useC2C";
+import Cursor from "./Cursor";
 
 export const Cursors = ({ users }) => {
   const [c2c] = useC2C();
@@ -37,7 +37,7 @@ export const Cursors = ({ users }) => {
   React.useEffect(() => {
     const unsub = [];
     unsub.push(
-      c2c.subscribe('cursorMove', ({ userId, pos }) => {
+      c2c.subscribe("cursorMove", ({ userId, pos }) => {
         //console.log('move', pos);
         setCursors((prevCursors) => {
           return {
@@ -48,7 +48,7 @@ export const Cursors = ({ users }) => {
       })
     );
     unsub.push(
-      c2c.subscribe('cursorOff', ({ userId }) => {
+      c2c.subscribe("cursorOff", ({ userId }) => {
         setCursors((prevCursors) => {
           const newCursors = {
             ...prevCursors,

+ 42 - 42
src/components/GameController.js

@@ -1,27 +1,27 @@
-import React from 'react';
+import React from "react";
 
-import { useC2C } from '../hooks/useC2C';
-import { nanoid } from 'nanoid';
+import { useC2C } from "../hooks/useC2C";
+import { nanoid } from "nanoid";
 
-import { ItemListAtom } from '../components/Items';
-import { useRecoilValue } from 'recoil';
+import { ItemListAtom } from "../components/Items";
+import { useRecoilValue } from "recoil";
 
-import useLocalStorage from '../hooks/useLocalStorage';
+import useLocalStorage from "../hooks/useLocalStorage";
 //import useLocalStorage from 'react-use-localstorage';
 
-import throttle from 'lodash.throttle';
+import throttle from "lodash.throttle";
 
-import tiktok from '../games/tiktok';
-import card from '../games/card';
-import gloomhaven from '../games/gloomhaven';
-import gloomhavenBox from '../games/gloomhaven-box';
-import settlers from '../games/settlers';
-import LoadGame from './LoadGame';
-import AvailableItems from './AvailableItems';
+import tiktok from "../games/tiktok";
+import card from "../games/card";
+import gloomhaven from "../games/gloomhaven";
+import gloomhavenBox from "../games/gloomhaven-box";
+import settlers from "../games/settlers";
+import LoadGame from "./LoadGame";
+import AvailableItems from "./AvailableItems";
 
 const generateDownloadURI = (data) => {
   return (
-    'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(data))
+    "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(data))
   );
 };
 
@@ -36,7 +36,7 @@ export const GameController = ({
   const [c2c, joined, isMaster] = useC2C();
   const [downloadURI, setDownloadURI] = React.useState({});
   const [date, setDate] = React.useState(Date.now());
-  const [gameLocalSave, setGameLocalSave] = useLocalStorage('savedGame', {
+  const [gameLocalSave, setGameLocalSave] = useLocalStorage("savedGame", {
     items: itemList,
     board: boardConfig,
     availableItems: availableItemList,
@@ -58,9 +58,9 @@ export const GameController = ({
   React.useEffect(() => {
     if (joined) {
       if (!isMaster) {
-        c2c.call('getGame').then(
+        c2c.call("getGame").then(
           (game) => {
-            console.log('Get this game from master', game);
+            console.log("Get this game from master", game);
             setAvailableItemList(game.availableItems);
             setItemList(game.items);
             setBoardConfig(game.board);
@@ -83,8 +83,8 @@ export const GameController = ({
     if (joined) {
       if (isMaster) {
         c2c
-          .register('getGame', () => {
-            console.log('Send this game', gameRef.current);
+          .register("getGame", () => {
+            console.log("Send this game", gameRef.current);
             return gameRef.current;
           })
           .then((unregister) => {
@@ -98,8 +98,8 @@ export const GameController = ({
   }, [c2c, isMaster, joined]);
 
   React.useEffect(() => {
-    c2c.subscribe('loadGame', (game) => {
-      console.log('Loadgame', game);
+    c2c.subscribe("loadGame", (game) => {
+      console.log("Loadgame", game);
       //
       setAvailableItemList(game.availableItems);
       setItemList(game.items);
@@ -110,13 +110,13 @@ export const GameController = ({
   const loadTikTok = React.useCallback(() => {
     tiktok.availableItems = [];
     tiktok.items = tiktok.items.map((item) => ({ ...item, id: nanoid() }));
-    c2c.publish('loadGame', tiktok, true);
+    c2c.publish("loadGame", tiktok, true);
   }, [c2c]);
 
   const loadCard = () => {
     card.availableItems = [];
     card.items = card.items.map((item) => ({ ...item, id: nanoid() }));
-    c2c.publish('loadGame', card, true);
+    c2c.publish("loadGame", card, true);
   };
 
   const loadGloomhaven = () => {
@@ -125,7 +125,7 @@ export const GameController = ({
       ...item,
       id: nanoid(),
     }));
-    c2c.publish('loadGame', gloomhaven, true);
+    c2c.publish("loadGame", gloomhaven, true);
   };
 
   const loadSettlers = React.useCallback(() => {
@@ -134,7 +134,7 @@ export const GameController = ({
       ...item,
       id: nanoid(),
     }));
-    c2c.publish('loadGame', settlers, true);
+    c2c.publish("loadGame", settlers, true);
   }, [c2c]);
 
   const onLoadSavedGame = React.useCallback(
@@ -143,7 +143,7 @@ export const GameController = ({
         ...item,
         id: nanoid(),
       }));
-      c2c.publish('loadGame', game, true);
+      c2c.publish("loadGame", game, true);
     },
     [c2c]
   );
@@ -155,7 +155,7 @@ export const GameController = ({
       id: nanoid(),
     }));
 
-    c2c.publish('loadGame', game, true);
+    c2c.publish("loadGame", game, true);
   }, [c2c, gameLocalSave]);
 
   // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -195,17 +195,17 @@ export const GameController = ({
   return (
     <div
       style={{
-        position: 'fixed',
-        left: '0.5em',
-        top: '0.5em',
-        backgroundColor: '#ffffff77',
-        display: 'flex',
-        flexDirection: 'column',
-        width: '15em',
-        height: '100%',
-        padding: '0.5em',
-        textAlign: 'center',
-        'overflow-y': 'scroll',
+        position: "fixed",
+        left: "0.5em",
+        top: "0.5em",
+        backgroundColor: "#ffffff77",
+        display: "flex",
+        flexDirection: "column",
+        width: "15em",
+        height: "100%",
+        padding: "0.5em",
+        textAlign: "center",
+        "overflow-y": "scroll",
       }}
     >
       <h2>Games</h2>
@@ -221,9 +221,9 @@ export const GameController = ({
       </a>
       <div
         style={{
-          'margin-top': '2em',
-          'background-color': 'black',
-          color: 'white',
+          "margin-top": "2em",
+          "background-color": "black",
+          color: "white",
         }}
       >
         <h3>Game Box Content</h3>

+ 83 - 83
src/components/Item.js

@@ -1,8 +1,8 @@
-import React from 'react';
-import { useC2C } from '../hooks/useC2C';
-import { useRecoilValue } from 'recoil';
-import { selectedItemsAtom } from './Selector';
-import { userAtom } from '../hooks/useUser';
+import React from "react";
+import { useC2C } from "../hooks/useC2C";
+import { useRecoilValue } from "recoil";
+import { selectedItemsAtom } from "./Selector";
+import { userAtom } from "../hooks/useUser";
 
 const Rect = ({ width, height, color }) => {
   return (
@@ -19,27 +19,27 @@ const Rect = ({ width, height, color }) => {
 const Round = ({
   radius,
   color,
-  text = '',
-  textColor = '#000',
-  fontSize = '16',
+  text = "",
+  textColor = "#000",
+  fontSize = "16",
 }) => {
   return (
     <div
       style={{
-        borderRadius: '100%',
+        borderRadius: "100%",
         width: radius,
         height: radius,
         backgroundColor: color,
-        textAlign: 'center',
-        display: 'flex',
-        alignItems: 'center',
-        justifyContent: 'center',
+        textAlign: "center",
+        display: "flex",
+        alignItems: "center",
+        justifyContent: "center",
       }}
     >
       <span
         style={{
           textColor,
-          fontSize: fontSize + 'px',
+          fontSize: fontSize + "px",
         }}
       >
         {text}
@@ -50,10 +50,10 @@ const Round = ({
 
 const Counter = ({
   value = 0,
-  color = '#CCC',
-  label = '',
-  textColor = '#000',
-  fontSize = '16',
+  color = "#CCC",
+  label = "",
+  textColor = "#000",
+  fontSize = "16",
   updateState,
 }) => {
   const setValue = (e) => {
@@ -81,31 +81,31 @@ const Counter = ({
     <div
       style={{
         backgroundColor: color,
-        width: '5em',
-        padding: '0.5em',
-        paddingBottom: '2em',
-        textAlign: 'center',
-        fontSize: fontSize + 'px',
-        display: 'flex',
-        justifyContent: 'space-between',
-        flexDirection: 'column',
-        borderRadius: '0.5em',
-        boxShadow: '10px 10px 13px 0px rgb(0, 0, 0, 0.3)',
+        width: "5em",
+        padding: "0.5em",
+        paddingBottom: "2em",
+        textAlign: "center",
+        fontSize: fontSize + "px",
+        display: "flex",
+        justifyContent: "space-between",
+        flexDirection: "column",
+        borderRadius: "0.5em",
+        boxShadow: "10px 10px 13px 0px rgb(0, 0, 0, 0.3)",
       }}
     >
-      <label style={{ userSelect: 'none' }}>
+      <label style={{ userSelect: "none" }}>
         {label}
         <input
           style={{
             textColor,
-            width: '100%',
-            display: 'block',
-            textAlign: 'center',
-            border: 'none',
-            margin: '0.2em 0',
-            padding: '0.2em 0',
-            fontSize: fontSize + 'px',
-            userSelect: 'none',
+            width: "100%",
+            display: "block",
+            textAlign: "center",
+            border: "none",
+            margin: "0.2em 0",
+            padding: "0.2em 0",
+            fontSize: fontSize + "px",
+            userSelect: "none",
           }}
           value={value}
           onChange={setValue}
@@ -113,13 +113,13 @@ const Counter = ({
       </label>
       <span
         style={{
-          paddingTop: '1em',
+          paddingTop: "1em",
         }}
       >
-        <button onClick={increment} style={{ fontSize: fontSize + 'px' }}>
+        <button onClick={increment} style={{ fontSize: fontSize + "px" }}>
           +
         </button>
-        <button onClick={decrement} style={{ fontSize: fontSize + 'px' }}>
+        <button onClick={decrement} style={{ fontSize: fontSize + "px" }}>
           -
         </button>
       </span>
@@ -179,13 +179,13 @@ const Image = ({
           <div
             className="image-text"
             style={{
-              position: 'absolute',
+              position: "absolute",
               right: 0,
-              padding: '0 3px',
-              backgroundColor: 'black',
-              color: 'white',
-              borderRadius: '50%',
-              userSelect: 'none',
+              padding: "0 3px",
+              backgroundColor: "black",
+              color: "white",
+              borderRadius: "50%",
+              userSelect: "none",
             }}
           >
             {backText}
@@ -196,23 +196,23 @@ const Image = ({
           alt=""
           draggable={false}
           {...size}
-          style={{ userSelect: 'none', pointerEvents: 'none' }}
+          style={{ userSelect: "none", pointerEvents: "none" }}
         />
       </>
     );
   } else {
     image = (
-      <div className="image-wrapper" style={{ position: 'relative' }}>
+      <div className="image-wrapper" style={{ position: "relative" }}>
         {unflippedFor && (
           <div
             style={{
-              position: 'absolute',
-              top: '-18px',
-              left: '4px',
-              color: '#555',
-              backgroundColor: '#CCCCCCA0',
-              userSelect: 'none',
-              pointerEvents: 'none',
+              position: "absolute",
+              top: "-18px",
+              left: "4px",
+              color: "#555",
+              backgroundColor: "#CCCCCCA0",
+              userSelect: "none",
+              pointerEvents: "none",
             }}
           >
             Only you
@@ -223,9 +223,9 @@ const Image = ({
             src={overlay.content}
             alt=""
             style={{
-              position: 'absolute',
-              userSelect: 'none',
-              pointerEvents: 'none',
+              position: "absolute",
+              userSelect: "none",
+              pointerEvents: "none",
             }}
           />
         )}
@@ -233,13 +233,13 @@ const Image = ({
           <div
             className="image-text"
             style={{
-              position: 'absolute',
+              position: "absolute",
               right: 0,
-              padding: '0 3px',
-              backgroundColor: 'black',
-              color: 'white',
-              borderRadius: '50%',
-              userSelect: 'none',
+              padding: "0 3px",
+              backgroundColor: "black",
+              color: "white",
+              borderRadius: "50%",
+              userSelect: "none",
             }}
           >
             {text}
@@ -250,7 +250,7 @@ const Image = ({
           alt=""
           draggable={false}
           {...size}
-          style={{ userSelect: 'none', pointerEvents: 'none' }}
+          style={{ userSelect: "none", pointerEvents: "none" }}
         />
       </div>
     );
@@ -261,13 +261,13 @@ const Image = ({
 
 const getComponent = (type) => {
   switch (type) {
-    case 'rect':
+    case "rect":
       return Rect;
-    case 'round':
+    case "round":
       return Round;
-    case 'image':
+    case "image":
       return Image;
-    case 'counter':
+    case "counter":
       return Counter;
     default:
       return Rect;
@@ -287,20 +287,20 @@ const Item = ({ setState, state }) => {
   // Allow to operate on locked item if ctrl is pressed
   React.useEffect(() => {
     const onKeyDown = (e) => {
-      if (e.key === 'Control') {
+      if (e.key === "Control") {
         setUnlock(true);
       }
     };
     const onKeyUp = (e) => {
-      if (e.key === 'Control') {
+      if (e.key === "Control") {
         setUnlock(false);
       }
     };
-    document.addEventListener('keydown', onKeyDown);
-    document.addEventListener('keyup', onKeyUp);
+    document.addEventListener("keydown", onKeyDown);
+    document.addEventListener("keyup", onKeyUp);
     return () => {
-      document.removeEventListener('keydown', onKeyDown);
-      document.removeEventListener('keyup', onKeyUp);
+      document.removeEventListener("keydown", onKeyDown);
+      document.removeEventListener("keyup", onKeyUp);
     };
   }, []);
 
@@ -308,10 +308,10 @@ const Item = ({ setState, state }) => {
 
   const style = {};
   if (selectedItems.includes(state.id)) {
-    style.border = '2px dashed #ff0000A0';
-    style.padding = '2px';
+    style.border = "2px dashed #ff0000A0";
+    style.padding = "2px";
   } else {
-    style.padding = '4px';
+    style.padding = "4px";
   }
 
   const rotation = state.rotation || 0;
@@ -351,11 +351,11 @@ const Item = ({ setState, state }) => {
   const content = (
     <div
       style={{
-        position: 'absolute',
+        position: "absolute",
         left: state.x,
         top: state.y,
-        display: 'inline-block',
-        boxSizing: 'content-box',
+        display: "inline-block",
+        boxSizing: "content-box",
         transform: `rotate(${rotation}deg)`,
         ...style,
       }}
@@ -373,8 +373,8 @@ const Item = ({ setState, state }) => {
   return (
     <div
       style={{
-        pointerEvents: 'none',
-        userSelect: 'none',
+        pointerEvents: "none",
+        userSelect: "none",
       }}
     >
       {content}

+ 6 - 6
src/components/Items.js

@@ -1,10 +1,10 @@
-import React from 'react';
-import { useRecoilState, atom } from 'recoil';
-import Item from '../components/Item';
-import { useC2C } from '../hooks/useC2C';
+import React from "react";
+import { useRecoilState, atom } from "recoil";
+import Item from "../components/Item";
+import { useC2C } from "../hooks/useC2C";
 
 export const ItemListAtom = atom({
-  key: 'itemList',
+  key: "itemList",
   default: [],
 });
 
@@ -15,7 +15,7 @@ const Items = () => {
   const updateItem = React.useCallback(
     (id, callbackOrItem, sync = true) => {
       let callback = callbackOrItem;
-      if (typeof callbackOrItem === 'object') {
+      if (typeof callbackOrItem === "object") {
         callback = (item) => callbackOrItem;
       }
       setItemList((prevList) => {

+ 5 - 5
src/components/LoadGame.js

@@ -1,5 +1,5 @@
-import React from 'react';
-import { useDropzone } from 'react-dropzone';
+import React from "react";
+import { useDropzone } from "react-dropzone";
 
 const LoadGame = ({ onLoad = () => {} }) => {
   const onDrop = React.useCallback(
@@ -7,14 +7,14 @@ const LoadGame = ({ onLoad = () => {} }) => {
       acceptedFiles.forEach((file) => {
         const reader = new FileReader();
 
-        reader.onabort = () => console.log('file reading was aborted');
-        reader.onerror = () => console.log('file reading has failed');
+        reader.onabort = () => console.log("file reading was aborted");
+        reader.onerror = () => console.log("file reading has failed");
         reader.onload = () => {
           try {
             const result = JSON.parse(reader.result);
             onLoad(result);
           } catch (e) {
-            console.log('File parsing failed', e);
+            console.log("File parsing failed", e);
           }
         };
         reader.readAsText(file);

+ 7 - 7
src/components/PanZoomRotate.js

@@ -1,8 +1,8 @@
-import React from 'react';
-import { atom, useRecoilState } from 'recoil';
+import React from "react";
+import { atom, useRecoilState } from "recoil";
 
 export const PanZoomRotateState = atom({
-  key: 'PanZoomRotate',
+  key: "PanZoomRotate",
   default: {
     translateX: 0,
     translateY: 0,
@@ -59,7 +59,7 @@ const PanZoomRotate = ({ children }) => {
       stateRef.current.startY = e.clientY;
       stateRef.current.startTranslateX = dim.translateX;
       stateRef.current.startTranslateY = dim.translateY;
-      wrapperRef.current.style.cursor = 'move';
+      wrapperRef.current.style.cursor = "move";
     }
   };
 
@@ -80,13 +80,13 @@ const PanZoomRotate = ({ children }) => {
   const onMouseUp = (e) => {
     if (e.button === 1 || e.altKey) {
       stateRef.current.moving = false;
-      wrapperRef.current.style.cursor = 'auto';
+      wrapperRef.current.style.cursor = "auto";
     }
   };
 
   const style = {
     transform: `translate(${dim.translateX}px, ${dim.translateY}px) scale(${dim.scale}) rotate(${dim.rotate}deg)`,
-    transformOrigin: 'top left',
+    transformOrigin: "top left",
   };
 
   return (
@@ -98,7 +98,7 @@ const PanZoomRotate = ({ children }) => {
       ref={wrapperRef}
       style={{}}
     >
-      <div style={{ ...style, display: 'inline-block' }} ref={wrappedRef}>
+      <div style={{ ...style, display: "inline-block" }} ref={wrappedRef}>
         {children}
       </div>
     </div>

+ 24 - 24
src/components/SelectedItems.js

@@ -1,12 +1,12 @@
-import React from 'react';
+import React from "react";
 
-import { useRecoilState, useRecoilValue } from 'recoil';
-import { ItemListAtom } from '../components/Items';
-import { selectedItemsAtom } from '../components/Selector';
-import { shuffle as shuffleArray } from '../utils';
-import { useC2C } from '../hooks/useC2C';
-import Slider from 'rc-slider';
-import 'rc-slider/assets/index.css';
+import { useRecoilState, useRecoilValue } from "recoil";
+import { ItemListAtom } from "../components/Items";
+import { selectedItemsAtom } from "../components/Selector";
+import { shuffle as shuffleArray } from "../utils";
+import { useC2C } from "../hooks/useC2C";
+import Slider from "rc-slider";
+import "rc-slider/assets/index.css";
 
 export const SelectedItems = () => {
   const [c2c] = useC2C();
@@ -20,7 +20,7 @@ export const SelectedItems = () => {
   const updateItem = React.useCallback(
     (id, callbackOrItem) => {
       let callback = callbackOrItem;
-      if (typeof callbackOrItem === 'object') {
+      if (typeof callbackOrItem === "object") {
         callback = (item) => callbackOrItem;
       }
       setItemList((prevList) => {
@@ -43,7 +43,7 @@ export const SelectedItems = () => {
   const massUpdateItems = React.useCallback(
     (ids, callbackOrItem) => {
       let callback = callbackOrItem;
-      if (typeof callbackOrItem === 'object') {
+      if (typeof callbackOrItem === "object") {
         callback = (item) => callbackOrItem;
       }
       setItemList((prevList) => {
@@ -140,13 +140,13 @@ export const SelectedItems = () => {
   return (
     <div
       style={{
-        position: 'fixed',
-        right: '1em',
-        bottom: '1em',
-        background: '#ffffff77',
-        padding: '0.2em',
-        maxHeight: '50vh',
-        overflowY: 'scroll',
+        position: "fixed",
+        right: "1em",
+        bottom: "1em",
+        background: "#ffffff77",
+        padding: "0.2em",
+        maxHeight: "50vh",
+        overflowY: "scroll",
       }}
     >
       {selectedItems.length > 1 && (
@@ -161,19 +161,19 @@ export const SelectedItems = () => {
       {selectedItems.length === 1 && (
         <ul
           style={{
-            listStyle: 'none',
+            listStyle: "none",
           }}
         >
           {selectedItemList.map(({ id, ...state }, index) => (
             <li
               key={id}
               style={{
-                display: 'flex',
-                flexDirection: 'column',
-                width: '25em',
+                display: "flex",
+                flexDirection: "column",
+                width: "25em",
               }}
             >
-              <h2 style={{ lineHeight: '30px' }}>{index}</h2>
+              <h2 style={{ lineHeight: "30px" }}>{index}</h2>
               <label>
                 Locked:
                 <input
@@ -197,8 +197,8 @@ export const SelectedItems = () => {
                   step={5}
                   included={false}
                   marks={{
-                    '-45': -45,
-                    '-30': -30,
+                    "-45": -45,
+                    "-30": -30,
                     0: 0,
                     30: 30,
                     45: 45,

+ 12 - 12
src/components/Selector.js

@@ -1,12 +1,12 @@
-import React from 'react';
-import { atom, useRecoilValue } from 'recoil';
-import { PanZoomRotateState } from '../components/PanZoomRotate';
-import { ItemListAtom } from '../components/Items';
-import { useRecoilState } from 'recoil';
-import { insideClass, isPointInsideRect } from '../utils';
+import React from "react";
+import { atom, useRecoilValue } from "recoil";
+import { PanZoomRotateState } from "../components/PanZoomRotate";
+import { ItemListAtom } from "../components/Items";
+import { useRecoilState } from "recoil";
+import { insideClass, isPointInsideRect } from "../utils";
 
 export const selectedItemsAtom = atom({
-  key: 'selectedItems',
+  key: "selectedItems",
   default: [],
 });
 
@@ -34,7 +34,7 @@ const Selector = ({ children }) => {
   });
 
   const onMouseDown = (e) => {
-    if (e.button === 0 && !insideClass(e.target, 'item')) {
+    if (e.button === 0 && !insideClass(e.target, "item")) {
       const { top, left } = e.currentTarget.getBoundingClientRect();
       const displayX = (e.clientX - left) / panZoomRotate.scale;
       const displayY = (e.clientY - top) / panZoomRotate.scale;
@@ -42,7 +42,7 @@ const Selector = ({ children }) => {
       stateRef.current.startX = displayX;
       stateRef.current.startY = displayY;
       setSelector({ ...stateRef.current });
-      wrapperRef.current.style.cursor = 'crosshair';
+      wrapperRef.current.style.cursor = "crosshair";
     } else {
       if (selected.length) {
         /* Should remove selection if clic another item */
@@ -84,7 +84,7 @@ const Selector = ({ children }) => {
       setSelected(selected);
       stateRef.current = { moving: false };
       setSelector({ ...stateRef.current });
-      wrapperRef.current.style.cursor = 'auto';
+      wrapperRef.current.style.cursor = "auto";
     }
   };
 
@@ -100,8 +100,8 @@ const Selector = ({ children }) => {
         <div
           style={{
             zIndex: 100,
-            position: 'absolute',
-            backgroundColor: '#FF000050',
+            position: "absolute",
+            backgroundColor: "#FF000050",
             top: selector.top,
             left: selector.left,
             height: selector.height,

+ 15 - 15
src/components/UserConfig.js

@@ -1,5 +1,5 @@
-import React from 'react';
-import { BlockPicker } from 'react-color';
+import React from "react";
+import { BlockPicker } from "react-color";
 
 const UserConfig = ({ user, setUser, editable }) => {
   const [name, setName] = React.useState(user.name);
@@ -26,19 +26,19 @@ const UserConfig = ({ user, setUser, editable }) => {
       <span
         style={{
           backgroundColor: user.color,
-          width: '20px',
-          height: '20px',
-          margin: '5px',
-          cursor: editable ? 'pointer' : 'auto',
+          width: "20px",
+          height: "20px",
+          margin: "5px",
+          cursor: editable ? "pointer" : "auto",
         }}
         onClick={showColorPicker}
       ></span>
       {showPicker && (
         <div
           style={{
-            position: 'absolute',
-            top: '38px ',
-            left: '-53px',
+            position: "absolute",
+            top: "38px ",
+            left: "-53px",
             zIndex: 1000,
           }}
         >
@@ -51,18 +51,18 @@ const UserConfig = ({ user, setUser, editable }) => {
       {editable && (
         <input
           style={{
-            border: 'none',
-            padding: '2px',
-            paddingLeft: '0.5em',
-            backgroundColor: '#CCC',
-            width: '7em',
+            border: "none",
+            padding: "2px",
+            paddingLeft: "0.5em",
+            backgroundColor: "#CCC",
+            width: "7em",
           }}
           value={name}
           onChange={handleChange}
         />
       )}
       {!editable && (
-        <span style={{ lineHeight: '30px', paddingLeft: '0.5em' }}>
+        <span style={{ lineHeight: "30px", paddingLeft: "0.5em" }}>
           {user.name}
         </span>
       )}

+ 10 - 10
src/components/Users.js

@@ -1,20 +1,20 @@
-import React from 'react';
-import UserConfig from './UserConfig';
+import React from "react";
+import UserConfig from "./UserConfig";
 
 export const Users = ({ user, setUser, users }) => (
   <ul
     style={{
-      position: 'fixed',
-      right: '1em',
-      top: '1em',
-      background: '#ffffff77',
-      padding: '0.2em',
-      listStyle: 'none',
+      position: "fixed",
+      right: "1em",
+      top: "1em",
+      background: "#ffffff77",
+      padding: "0.2em",
+      listStyle: "none",
     }}
   >
     {users.map((u, index) => (
-      <li key={u.id} style={{ display: 'flex', position: 'relative' }}>
-        <span style={{ lineHeight: '30px' }}>{index} - </span>
+      <li key={u.id} style={{ display: "flex", position: "relative" }}>
+        <span style={{ lineHeight: "30px" }}>{index} - </span>
         <UserConfig user={u} setUser={setUser} editable={user.id === u.id} />
       </li>
     ))}

+ 9 - 9
src/games/card.js

@@ -3,9 +3,9 @@ const genGame = () => {
 
   [...Array(3).keys()].forEach(() => {
     items.push({
-      type: 'image',
-      content: '/games/card.jpg',
-      backContent: '/games/back.jpg',
+      type: "image",
+      content: "/games/card.jpg",
+      backContent: "/games/back.jpg",
       width: 100,
       flipped: true,
       x: 100,
@@ -13,9 +13,9 @@ const genGame = () => {
     });
 
     items.push({
-      type: 'image',
-      content: '/games/card.jpg',
-      backContent: '/games/back.jpg',
+      type: "image",
+      content: "/games/card.jpg",
+      backContent: "/games/back.jpg",
       width: 100,
       flipped: true,
       x: 100,
@@ -23,9 +23,9 @@ const genGame = () => {
     });
 
     items.push({
-      type: 'image',
-      content: '/games/8diamond.png',
-      backContent: '/games/back.jpg',
+      type: "image",
+      content: "/games/8diamond.png",
+      backContent: "/games/back.jpg",
       width: 100,
       flipped: true,
       x: 100,

+ 163 - 163
src/games/gloomhaven-box.js

@@ -3,9 +3,9 @@ const genGloomhavenBox = () => {
 
   // map-tiles
 
-  Array.from('abcdefghijkl').forEach((l, index) => {
+  Array.from("abcdefghijkl").forEach((l, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/map-tiles/${l}1a.png`,
       backContent: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/map-tiles/${l}1b.png`,
       text: `${l}1a`,
@@ -14,7 +14,7 @@ const genGloomhavenBox = () => {
       y: 80,
     });
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/map-tiles/${l}2a.png`,
       backContent: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/map-tiles/${l}2b.png`,
       text: `${l}2a`,
@@ -27,40 +27,40 @@ const genGloomhavenBox = () => {
   // character-mats
 
   items.push({
-    type: 'image',
+    type: "image",
     content:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-mats/brute.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-mats/brute.png",
     backContent:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-mats/brute-back.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-mats/brute-back.png",
     width: 300,
     x: 500,
     y: 500,
   });
 
   items.push({
-    type: 'image',
+    type: "image",
     content:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-mats/spellweaver.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-mats/spellweaver.png",
     backContent:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-mats/spellweaver-back.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-mats/spellweaver-back.png",
     width: 300,
     x: 500,
     y: 700,
   });
 
   items.push({
-    type: 'image',
+    type: "image",
     content:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-perks/brute-perks.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-perks/brute-perks.png",
     width: 300,
     x: 1000,
     y: 500,
   });
 
   items.push({
-    type: 'image',
+    type: "image",
     content:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-perks/spellweaver-perks.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-perks/spellweaver-perks.png",
     width: 300,
     x: 1000,
     y: 700,
@@ -69,11 +69,11 @@ const genGloomhavenBox = () => {
   // Attack modifiers
 
   [...Array(19).keys()].forEach((_, index) => {
-    const number = index < 9 ? '0' + (index + 1) : '' + (index + 1);
+    const number = index < 9 ? "0" + (index + 1) : "" + (index + 1);
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/attack-modifiers/base/player/am-p-${number}.png`,
-      backContent: '/games/gloom/attackback.png',
+      backContent: "/games/gloom/attackback.png",
       width: 100,
       flipped: true,
       x: 300,
@@ -82,11 +82,11 @@ const genGloomhavenBox = () => {
   });
 
   [...Array(19).keys()].forEach((_, index) => {
-    const number = index < 9 ? '0' + (index + 1) : '' + (index + 1);
+    const number = index < 9 ? "0" + (index + 1) : "" + (index + 1);
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/attack-modifiers/base/player/am-p-${number}.png`,
-      backContent: '/games/gloom/attackback.png',
+      backContent: "/games/gloom/attackback.png",
       width: 100,
       flipped: true,
       x: 300,
@@ -95,11 +95,11 @@ const genGloomhavenBox = () => {
   });
 
   [...Array(19).keys()].forEach((_, index) => {
-    const number = index < 9 ? '0' + (index + 1) : '' + (index + 1);
+    const number = index < 9 ? "0" + (index + 1) : "" + (index + 1);
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/attack-modifiers/base/monster/am-m-${number}.png`,
-      backContent: '/games/gloom/attackback.png',
+      backContent: "/games/gloom/attackback.png",
       width: 100,
       flipped: true,
       x: 1000,
@@ -110,54 +110,54 @@ const genGloomhavenBox = () => {
   // monster tokens
   [...Array(10).keys()].forEach((_, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content:
-        'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/bandit-guard.png',
+        "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/bandit-guard.png",
       x: 1400 + 60 * index,
       y: 0,
       width: 60,
       text: `${index}`,
       overlay: {
         content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/${
-          index < 7 ? 'normal' : 'elite'
+          index < 7 ? "normal" : "elite"
         }-monster-overlay.svg`,
       },
     });
     items.push({
-      type: 'image',
+      type: "image",
       content:
-        'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/bandit-archer.png',
+        "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/bandit-archer.png",
       x: 1400 + 60 * index,
       y: 50,
       width: 60,
       text: `${index}`,
       overlay: {
         content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/${
-          index < 7 ? 'normal' : 'elite'
+          index < 7 ? "normal" : "elite"
         }-monster-overlay.svg`,
       },
     });
     items.push({
-      type: 'image',
+      type: "image",
       content:
-        'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/living-bones.png',
+        "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/living-bones.png",
       x: 1400 + 60 * index,
       y: 100,
       width: 60,
       text: `${index}`,
       overlay: {
         content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/${
-          index < 7 ? 'normal' : 'elite'
+          index < 7 ? "normal" : "elite"
         }-monster-overlay.svg`,
       },
     });
   });
 
-  const monsters = ['bandit-guard', 'bandit-archer', 'living-bones'];
+  const monsters = ["bandit-guard", "bandit-archer", "living-bones"];
 
   monsters.forEach((monsterName, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com//romgar/gloomhaven/master/images/monster-stat-cards/${monsterName}-0.png`,
       x: 1300 + 200 * index,
       y: 300,
@@ -168,9 +168,9 @@ const genGloomhavenBox = () => {
   // Overlay tokens
   [...Array(20).keys()].forEach((_, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content:
-        'https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/treasures/coin-1.png',
+        "https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/treasures/coin-1.png",
       x: 1100 + 20 * index,
       y: 200,
       width: 50,
@@ -178,8 +178,8 @@ const genGloomhavenBox = () => {
   });
 
   items.push({
-    type: 'counter',
-    label: 'Life #1',
+    type: "counter",
+    label: "Life #1",
     value: 0,
     x: 100,
     y: 100,
@@ -187,8 +187,8 @@ const genGloomhavenBox = () => {
   });
 
   items.push({
-    type: 'counter',
-    label: 'Life #2',
+    type: "counter",
+    label: "Life #2",
     value: 0,
     x: 100,
     y: 300,
@@ -196,8 +196,8 @@ const genGloomhavenBox = () => {
   });
 
   items.push({
-    type: 'counter',
-    label: 'XP #1',
+    type: "counter",
+    label: "XP #1",
     value: 0,
     x: 300,
     y: 100,
@@ -205,8 +205,8 @@ const genGloomhavenBox = () => {
   });
 
   items.push({
-    type: 'counter',
-    label: 'XP #2',
+    type: "counter",
+    label: "XP #2",
     value: 0,
     x: 300,
     y: 300,
@@ -214,8 +214,8 @@ const genGloomhavenBox = () => {
   });
 
   items.push({
-    type: 'counter',
-    label: 'Gold #1',
+    type: "counter",
+    label: "Gold #1",
     value: 0,
     x: 100,
     y: 600,
@@ -223,8 +223,8 @@ const genGloomhavenBox = () => {
   });
 
   items.push({
-    type: 'counter',
-    label: 'Gold #2',
+    type: "counter",
+    label: "Gold #2",
     value: 0,
     x: 100,
     y: 900,
@@ -232,9 +232,9 @@ const genGloomhavenBox = () => {
   });
 
   items.push({
-    type: 'image',
+    type: "image",
     content:
-      'https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/treasures/treasure.png',
+      "https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/treasures/treasure.png",
     x: 1100,
     y: 250,
     width: 50,
@@ -242,9 +242,9 @@ const genGloomhavenBox = () => {
 
   [...Array(2).keys()].forEach((_, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content:
-        'https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/doors/stone-door.png',
+        "https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/doors/stone-door.png",
       x: 1100 + 20 * index,
       y: 300,
       width: 50,
@@ -253,9 +253,9 @@ const genGloomhavenBox = () => {
 
   [...Array(2).keys()].forEach((_, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content:
-        'https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/traps/spike-pit-trap.png',
+        "https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/traps/spike-pit-trap.png",
       x: 1100 + 20 * index,
       y: 350,
       width: 50,
@@ -264,9 +264,9 @@ const genGloomhavenBox = () => {
 
   [...Array(2).keys()].forEach((_, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content:
-        'https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/obstacles/table.png',
+        "https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/obstacles/table.png",
       x: 1100 + 20 * index,
       y: 400,
       width: 50,
@@ -275,41 +275,41 @@ const genGloomhavenBox = () => {
 
   // Character ability cards
   const brute = {
-    code: 'BR',
+    code: "BR",
     abilityCards: {
-      'level-1': [
-        'eye-for-an-eye',
-        'grab-and-go',
-        'leaping-cleave',
-        'overwhelming-assault',
-        'provoking-roar',
-        'shield-bash',
-        'spare-dagger',
-        'sweeping-blow',
-        'trample',
-        'warding-strength',
+      "level-1": [
+        "eye-for-an-eye",
+        "grab-and-go",
+        "leaping-cleave",
+        "overwhelming-assault",
+        "provoking-roar",
+        "shield-bash",
+        "spare-dagger",
+        "sweeping-blow",
+        "trample",
+        "warding-strength",
       ],
-      'level-X': ['balanced-measure', 'skewer', 'wall-of-doom'],
-      'level-2': ['fatal-advance', 'juggernaut'],
-      'level-3': ['brute-force', 'hook-and-chain'],
-      'level-4': ['devastating-hack', 'unstoppable-charge'],
-      'level-5': ['skirmishing-maneuver', 'whirlwind'],
-      'level-6': ['immovable-phalanx', 'quietus'],
-      'level-7': ['crippling-offensive', 'defensive-tactics'],
-      'level-8': ['frenzied-onslaught', 'selfish-retribution'],
-      'level-9': ['face-your-end', 'king-of-the-hill'],
+      "level-X": ["balanced-measure", "skewer", "wall-of-doom"],
+      "level-2": ["fatal-advance", "juggernaut"],
+      "level-3": ["brute-force", "hook-and-chain"],
+      "level-4": ["devastating-hack", "unstoppable-charge"],
+      "level-5": ["skirmishing-maneuver", "whirlwind"],
+      "level-6": ["immovable-phalanx", "quietus"],
+      "level-7": ["crippling-offensive", "defensive-tactics"],
+      "level-8": ["frenzied-onslaught", "selfish-retribution"],
+      "level-9": ["face-your-end", "king-of-the-hill"],
     },
     backCard:
-      'https://raw.githubusercontent.com//romgar/gloomhaven/master/images/character-ability-cards/BR/br-back.png',
+      "https://raw.githubusercontent.com//romgar/gloomhaven/master/images/character-ability-cards/BR/br-back.png",
     icon:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-icons/brute-icon.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-icons/brute-icon.png",
     token:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-icons/brute-character-token.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-icons/brute-character-token.png",
   };
 
-  brute.abilityCards['level-1'].forEach((abilityName, index) => {
+  brute.abilityCards["level-1"].forEach((abilityName, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com//romgar/gloomhaven/master/images/character-ability-cards/${brute.code}/${abilityName}.png`,
       backContent: `${brute.backCard}`,
       x: 0 + 100 * index,
@@ -319,7 +319,7 @@ const genGloomhavenBox = () => {
   });
 
   items.push({
-    type: 'image',
+    type: "image",
     content: brute.icon,
     x: 450,
     y: 500,
@@ -328,7 +328,7 @@ const genGloomhavenBox = () => {
 
   [...Array(5).keys()].forEach((_, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content: brute.token,
       x: 350 + 20 * index,
       y: 500,
@@ -337,39 +337,39 @@ const genGloomhavenBox = () => {
   });
 
   const spellWeaver = {
-    code: 'SW',
+    code: "SW",
     abilityCards: {
-      'level-1': [
-        'fire-orbs',
-        'flame-strike',
-        'freezing-nova',
-        'frost-armor',
-        'impaling-eruption',
-        'mana-bolt',
-        'reviving-ether',
-        'ride-the-wind',
+      "level-1": [
+        "fire-orbs",
+        "flame-strike",
+        "freezing-nova",
+        "frost-armor",
+        "impaling-eruption",
+        "mana-bolt",
+        "reviving-ether",
+        "ride-the-wind",
       ],
-      'level-X': ['crackling-air', 'hardened-spikes', 'aid-from-the-ether'],
-      'level-2': ['flashing-burst', 'icy-blast'],
-      'level-3': ['elemental-aid', 'cold-fire'],
-      'level-4': ['forked-beam', 'spirit-of-doom'],
-      'level-5': ['engulfed-in-flames', 'chromatic-explosion'],
-      'level-6': ['frozen-night', 'living-torch'],
-      'level-7': ['stone-fists', 'twin-restoration'],
-      'level-8': ['zephyr-wings', 'cold-front'],
-      'level-9': ['inferno', 'black-hole'],
+      "level-X": ["crackling-air", "hardened-spikes", "aid-from-the-ether"],
+      "level-2": ["flashing-burst", "icy-blast"],
+      "level-3": ["elemental-aid", "cold-fire"],
+      "level-4": ["forked-beam", "spirit-of-doom"],
+      "level-5": ["engulfed-in-flames", "chromatic-explosion"],
+      "level-6": ["frozen-night", "living-torch"],
+      "level-7": ["stone-fists", "twin-restoration"],
+      "level-8": ["zephyr-wings", "cold-front"],
+      "level-9": ["inferno", "black-hole"],
     },
     backCard:
-      'https://raw.githubusercontent.com//romgar/gloomhaven/master/images/character-ability-cards/SW/sw-back.png',
+      "https://raw.githubusercontent.com//romgar/gloomhaven/master/images/character-ability-cards/SW/sw-back.png",
     icon:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-icons/spellweaver-icon.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-icons/spellweaver-icon.png",
     token:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-icons/spellweaver-character-token.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-icons/spellweaver-character-token.png",
   };
 
-  spellWeaver.abilityCards['level-1'].forEach((abilityName, index) => {
+  spellWeaver.abilityCards["level-1"].forEach((abilityName, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com//romgar/gloomhaven/master/images/character-ability-cards/${spellWeaver.code}/${abilityName}.png`,
       backContent: `${spellWeaver.backCard}`,
       x: 0 + 100 * index,
@@ -379,7 +379,7 @@ const genGloomhavenBox = () => {
   });
 
   items.push({
-    type: 'image',
+    type: "image",
     content: spellWeaver.icon,
     x: 450,
     y: 700,
@@ -388,7 +388,7 @@ const genGloomhavenBox = () => {
 
   [...Array(5).keys()].forEach((_, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content: spellWeaver.token,
       x: 350 + 20 * index,
       y: 700,
@@ -396,10 +396,10 @@ const genGloomhavenBox = () => {
     });
   });
 
-  const elements = ['ice', 'air', 'earth', 'fire', 'dark', 'light'];
+  const elements = ["ice", "air", "earth", "fire", "dark", "light"];
   elements.forEach((elementName, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/elements/${elementName}-element.svg`,
       x: 0 + 30 * index,
       y: 0,
@@ -408,28 +408,28 @@ const genGloomhavenBox = () => {
   });
 
   items.push({
-    type: 'image',
+    type: "image",
     content:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/elements/element-matboard.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/elements/element-matboard.png",
     x: 0,
     y: 100,
     width: 300,
   });
 
   const ailments = [
-    'reinforcement',
-    'disarm',
-    'immobilise',
-    'wound',
-    'stun',
-    'invisible',
-    'confusion',
-    'poison',
+    "reinforcement",
+    "disarm",
+    "immobilise",
+    "wound",
+    "stun",
+    "invisible",
+    "confusion",
+    "poison",
   ];
   [...Array(5).keys()].forEach((_, rowIndex) => {
     ailments.forEach((ailmentName, index) => {
       items.push({
-        type: 'image',
+        type: "image",
         content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/ailments/${ailmentName}.png`,
         x: 0 + 30 * index,
         y: 100 + 40 * rowIndex,
@@ -438,37 +438,37 @@ const genGloomhavenBox = () => {
     });
 
     const battleGoals = [
-      'aggressor',
-      'diehard',
-      'dynamo',
-      'executioner',
-      'explorer',
-      'fasthealer',
-      'hoarder',
-      'hunter',
-      'indigent',
-      'layabout',
-      'masochist',
-      'neutralizer',
-      'opener',
-      'pacifist',
-      'plunderer',
-      'professional',
-      'protector',
-      'purist',
-      'sadist',
-      'scrambler',
-      'straggler',
-      'streamliner',
-      'workhorse',
-      'zealot',
+      "aggressor",
+      "diehard",
+      "dynamo",
+      "executioner",
+      "explorer",
+      "fasthealer",
+      "hoarder",
+      "hunter",
+      "indigent",
+      "layabout",
+      "masochist",
+      "neutralizer",
+      "opener",
+      "pacifist",
+      "plunderer",
+      "professional",
+      "protector",
+      "purist",
+      "sadist",
+      "scrambler",
+      "straggler",
+      "streamliner",
+      "workhorse",
+      "zealot",
     ];
     battleGoals.forEach((battleGoalName, index) => {
       items.push({
-        type: 'image',
+        type: "image",
         content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/battle-goals/${battleGoalName}.png`,
         backContent:
-          'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/battle-goals/battlegoal-back.png',
+          "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/battle-goals/battlegoal-back.png",
         x: 200 + 1 * index,
         y: 0 + 1 * index,
         flipped: true,
@@ -477,24 +477,24 @@ const genGloomhavenBox = () => {
     });
 
     const characterItems = [
-      'boots-of-striding',
-      'cloak-of-invisibility',
-      'eagle-eye-goggles',
-      'heater-shield',
-      'hide-armor',
-      'iron-helmet',
-      'leather-armor',
-      'minor-healing-potion',
-      'minor-power-potion',
-      'minor-stamina-potion',
-      'piercing-bow',
-      'poison-dagger',
-      'war-hammer',
-      'winged-shoes',
+      "boots-of-striding",
+      "cloak-of-invisibility",
+      "eagle-eye-goggles",
+      "heater-shield",
+      "hide-armor",
+      "iron-helmet",
+      "leather-armor",
+      "minor-healing-potion",
+      "minor-power-potion",
+      "minor-stamina-potion",
+      "piercing-bow",
+      "poison-dagger",
+      "war-hammer",
+      "winged-shoes",
     ];
     characterItems.forEach((itemName, index) => {
       items.push({
-        type: 'image',
+        type: "image",
         content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/items/1-14/${itemName}.png`,
         x: 1500 + 40 * index,
         y: 1500,

+ 179 - 179
src/games/gloomhaven.js

@@ -3,7 +3,7 @@ const genGloomhaven = () => {
 
   [...Array(7).keys()].forEach((_, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-ability-cards/archer/ma-ar-${
         index + 1
       }.png`,
@@ -16,7 +16,7 @@ const genGloomhaven = () => {
 
   [...Array(7).keys()].forEach((_, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-ability-cards/guard/ma-gu-${
         index + 1
       }.png`,
@@ -29,7 +29,7 @@ const genGloomhaven = () => {
 
   [...Array(7).keys()].forEach((_, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-ability-cards/living-bones/ma-lb-${
         index + 1
       }.png`,
@@ -43,25 +43,25 @@ const genGloomhaven = () => {
   // map-tiles
 
   [
-    'a4',
-    'b4',
-    'c2',
-    'd2',
-    'e1',
-    'f1',
-    'g2',
-    'h3',
-    'i2',
-    'j2',
-    'k2',
-    'l3',
-    'm1',
-    'n1',
+    "a4",
+    "b4",
+    "c2",
+    "d2",
+    "e1",
+    "f1",
+    "g2",
+    "h3",
+    "i2",
+    "j2",
+    "k2",
+    "l3",
+    "m1",
+    "n1",
   ].forEach((ln, index) => {
     const [l, n] = Array.from(ln);
     [...Array(n).keys()].forEach((y, index) => {
       items.push({
-        type: 'image',
+        type: "image",
         content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/map-tiles/${l}${
           y + 1
         }a.png`,
@@ -79,40 +79,40 @@ const genGloomhaven = () => {
   // character-mats
 
   items.push({
-    type: 'image',
+    type: "image",
     content:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-mats/brute.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-mats/brute.png",
     backContent:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-mats/brute-back.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-mats/brute-back.png",
     width: 300,
     x: 500,
     y: 500,
   });
 
   items.push({
-    type: 'image',
+    type: "image",
     content:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-mats/spellweaver.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-mats/spellweaver.png",
     backContent:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-mats/spellweaver-back.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-mats/spellweaver-back.png",
     width: 300,
     x: 500,
     y: 700,
   });
 
   items.push({
-    type: 'image',
+    type: "image",
     content:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-perks/brute-perks.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-perks/brute-perks.png",
     width: 300,
     x: 1000,
     y: 500,
   });
 
   items.push({
-    type: 'image',
+    type: "image",
     content:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-perks/spellweaver-perks.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-perks/spellweaver-perks.png",
     width: 300,
     x: 1000,
     y: 700,
@@ -121,11 +121,11 @@ const genGloomhaven = () => {
   // Attack modifiers
 
   [...Array(19).keys()].forEach((_, index) => {
-    const number = index < 9 ? '0' + (index + 1) : '' + (index + 1);
+    const number = index < 9 ? "0" + (index + 1) : "" + (index + 1);
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/attack-modifiers/base/player/am-p-${number}.png`,
-      backContent: '/games/gloom/attackback.png',
+      backContent: "/games/gloom/attackback.png",
       width: 100,
       flipped: true,
       x: 300,
@@ -134,11 +134,11 @@ const genGloomhaven = () => {
   });
 
   [...Array(19).keys()].forEach((_, index) => {
-    const number = index < 9 ? '0' + (index + 1) : '' + (index + 1);
+    const number = index < 9 ? "0" + (index + 1) : "" + (index + 1);
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/attack-modifiers/base/player/am-p-${number}.png`,
-      backContent: '/games/gloom/attackback.png',
+      backContent: "/games/gloom/attackback.png",
       width: 100,
       flipped: true,
       x: 300,
@@ -147,11 +147,11 @@ const genGloomhaven = () => {
   });
 
   [...Array(19).keys()].forEach((_, index) => {
-    const number = index < 9 ? '0' + (index + 1) : '' + (index + 1);
+    const number = index < 9 ? "0" + (index + 1) : "" + (index + 1);
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/attack-modifiers/base/monster/am-m-${number}.png`,
-      backContent: '/games/gloom/attackback.png',
+      backContent: "/games/gloom/attackback.png",
       width: 100,
       flipped: true,
       x: 1000,
@@ -162,54 +162,54 @@ const genGloomhaven = () => {
   // monster tokens
   [...Array(10).keys()].forEach((_, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content:
-        'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/bandit-guard.png',
+        "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/bandit-guard.png",
       x: 1400 + 60 * index,
       y: 0,
       width: 60,
       text: `${index}`,
       overlay: {
         content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/${
-          index < 7 ? 'normal' : 'elite'
+          index < 7 ? "normal" : "elite"
         }-monster-overlay.svg`,
       },
     });
     items.push({
-      type: 'image',
+      type: "image",
       content:
-        'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/bandit-archer.png',
+        "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/bandit-archer.png",
       x: 1400 + 60 * index,
       y: 50,
       width: 60,
       text: `${index}`,
       overlay: {
         content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/${
-          index < 7 ? 'normal' : 'elite'
+          index < 7 ? "normal" : "elite"
         }-monster-overlay.svg`,
       },
     });
     items.push({
-      type: 'image',
+      type: "image",
       content:
-        'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/living-bones.png',
+        "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/living-bones.png",
       x: 1400 + 60 * index,
       y: 100,
       width: 60,
       text: `${index}`,
       overlay: {
         content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/monster-tokens/${
-          index < 7 ? 'normal' : 'elite'
+          index < 7 ? "normal" : "elite"
         }-monster-overlay.svg`,
       },
     });
   });
 
-  const monsters = ['bandit-guard', 'bandit-archer', 'living-bones'];
+  const monsters = ["bandit-guard", "bandit-archer", "living-bones"];
 
   monsters.forEach((monsterName, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com//romgar/gloomhaven/master/images/monster-stat-cards/${monsterName}-0.png`,
       x: 1300 + 200 * index,
       y: 300,
@@ -220,9 +220,9 @@ const genGloomhaven = () => {
   // Overlay tokens
   [...Array(20).keys()].forEach((_, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content:
-        'https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/treasures/coin-1.png',
+        "https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/treasures/coin-1.png",
       x: 1100 + 20 * index,
       y: 200,
       width: 50,
@@ -230,8 +230,8 @@ const genGloomhaven = () => {
   });
 
   items.push({
-    type: 'counter',
-    label: 'Life #1',
+    type: "counter",
+    label: "Life #1",
     value: 0,
     x: 100,
     y: 100,
@@ -239,8 +239,8 @@ const genGloomhaven = () => {
   });
 
   items.push({
-    type: 'counter',
-    label: 'Life #2',
+    type: "counter",
+    label: "Life #2",
     value: 0,
     x: 100,
     y: 300,
@@ -248,8 +248,8 @@ const genGloomhaven = () => {
   });
 
   items.push({
-    type: 'counter',
-    label: 'XP #1',
+    type: "counter",
+    label: "XP #1",
     value: 0,
     x: 300,
     y: 100,
@@ -257,8 +257,8 @@ const genGloomhaven = () => {
   });
 
   items.push({
-    type: 'counter',
-    label: 'XP #2',
+    type: "counter",
+    label: "XP #2",
     value: 0,
     x: 300,
     y: 300,
@@ -266,8 +266,8 @@ const genGloomhaven = () => {
   });
 
   items.push({
-    type: 'counter',
-    label: 'Gold #1',
+    type: "counter",
+    label: "Gold #1",
     value: 0,
     x: 100,
     y: 600,
@@ -275,8 +275,8 @@ const genGloomhaven = () => {
   });
 
   items.push({
-    type: 'counter',
-    label: 'Gold #2',
+    type: "counter",
+    label: "Gold #2",
     value: 0,
     x: 100,
     y: 900,
@@ -286,7 +286,7 @@ const genGloomhaven = () => {
   [...Array(3).keys()].forEach(() => {
     [...Array(9).keys()].forEach((_, index) => {
       items.push({
-        type: 'counter',
+        type: "counter",
         label: `Monster #${index + 1}`,
         value: 0,
         x: 200 + 10 * index,
@@ -297,9 +297,9 @@ const genGloomhaven = () => {
   });
 
   items.push({
-    type: 'image',
+    type: "image",
     content:
-      'https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/treasures/treasure.png',
+      "https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/treasures/treasure.png",
     x: 1100,
     y: 250,
     width: 50,
@@ -307,9 +307,9 @@ const genGloomhaven = () => {
 
   [...Array(2).keys()].forEach((_, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content:
-        'https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/doors/stone-door.png',
+        "https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/doors/stone-door.png",
       x: 1100 + 20 * index,
       y: 300,
       width: 50,
@@ -318,9 +318,9 @@ const genGloomhaven = () => {
 
   [...Array(2).keys()].forEach((_, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content:
-        'https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/traps/spike-pit-trap.png',
+        "https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/traps/spike-pit-trap.png",
       x: 1100 + 20 * index,
       y: 350,
       width: 50,
@@ -329,9 +329,9 @@ const genGloomhaven = () => {
 
   [...Array(2).keys()].forEach((_, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content:
-        'https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/obstacles/table.png',
+        "https://raw.githubusercontent.com//romgar/gloomhaven/master/images/overlay-tokens/obstacles/table.png",
       x: 1100 + 20 * index,
       y: 400,
       width: 50,
@@ -340,41 +340,41 @@ const genGloomhaven = () => {
 
   // Character ability cards
   const brute = {
-    code: 'BR',
+    code: "BR",
     abilityCards: {
-      'level-1': [
-        'eye-for-an-eye',
-        'grab-and-go',
-        'leaping-cleave',
-        'overwhelming-assault',
-        'provoking-roar',
-        'shield-bash',
-        'spare-dagger',
-        'sweeping-blow',
-        'trample',
-        'warding-strength',
+      "level-1": [
+        "eye-for-an-eye",
+        "grab-and-go",
+        "leaping-cleave",
+        "overwhelming-assault",
+        "provoking-roar",
+        "shield-bash",
+        "spare-dagger",
+        "sweeping-blow",
+        "trample",
+        "warding-strength",
       ],
-      'level-X': ['balanced-measure', 'skewer', 'wall-of-doom'],
-      'level-2': ['fatal-advance', 'juggernaut'],
-      'level-3': ['brute-force', 'hook-and-chain'],
-      'level-4': ['devastating-hack', 'unstoppable-charge'],
-      'level-5': ['skirmishing-maneuver', 'whirlwind'],
-      'level-6': ['immovable-phalanx', 'quietus'],
-      'level-7': ['crippling-offensive', 'defensive-tactics'],
-      'level-8': ['frenzied-onslaught', 'selfish-retribution'],
-      'level-9': ['face-your-end', 'king-of-the-hill'],
+      "level-X": ["balanced-measure", "skewer", "wall-of-doom"],
+      "level-2": ["fatal-advance", "juggernaut"],
+      "level-3": ["brute-force", "hook-and-chain"],
+      "level-4": ["devastating-hack", "unstoppable-charge"],
+      "level-5": ["skirmishing-maneuver", "whirlwind"],
+      "level-6": ["immovable-phalanx", "quietus"],
+      "level-7": ["crippling-offensive", "defensive-tactics"],
+      "level-8": ["frenzied-onslaught", "selfish-retribution"],
+      "level-9": ["face-your-end", "king-of-the-hill"],
     },
     backCard:
-      'https://raw.githubusercontent.com//romgar/gloomhaven/master/images/character-ability-cards/BR/br-back.png',
+      "https://raw.githubusercontent.com//romgar/gloomhaven/master/images/character-ability-cards/BR/br-back.png",
     icon:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-icons/brute-icon.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-icons/brute-icon.png",
     token:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-icons/brute-character-token.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-icons/brute-character-token.png",
   };
 
-  brute.abilityCards['level-1'].forEach((abilityName, index) => {
+  brute.abilityCards["level-1"].forEach((abilityName, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com//romgar/gloomhaven/master/images/character-ability-cards/${brute.code}/${abilityName}.png`,
       backContent: `${brute.backCard}`,
       x: 0 + 100 * index,
@@ -384,7 +384,7 @@ const genGloomhaven = () => {
   });
 
   items.push({
-    type: 'image',
+    type: "image",
     content: brute.icon,
     x: 450,
     y: 500,
@@ -393,7 +393,7 @@ const genGloomhaven = () => {
 
   [...Array(5).keys()].forEach((_, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content: brute.token,
       x: 350 + 20 * index,
       y: 500,
@@ -402,39 +402,39 @@ const genGloomhaven = () => {
   });
 
   const spellWeaver = {
-    code: 'SW',
+    code: "SW",
     abilityCards: {
-      'level-1': [
-        'fire-orbs',
-        'flame-strike',
-        'freezing-nova',
-        'frost-armor',
-        'impaling-eruption',
-        'mana-bolt',
-        'reviving-ether',
-        'ride-the-wind',
+      "level-1": [
+        "fire-orbs",
+        "flame-strike",
+        "freezing-nova",
+        "frost-armor",
+        "impaling-eruption",
+        "mana-bolt",
+        "reviving-ether",
+        "ride-the-wind",
       ],
-      'level-X': ['crackling-air', 'hardened-spikes', 'aid-from-the-ether'],
-      'level-2': ['flashing-burst', 'icy-blast'],
-      'level-3': ['elemental-aid', 'cold-fire'],
-      'level-4': ['forked-beam', 'spirit-of-doom'],
-      'level-5': ['engulfed-in-flames', 'chromatic-explosion'],
-      'level-6': ['frozen-night', 'living-torch'],
-      'level-7': ['stone-fists', 'twin-restoration'],
-      'level-8': ['zephyr-wings', 'cold-front'],
-      'level-9': ['inferno', 'black-hole'],
+      "level-X": ["crackling-air", "hardened-spikes", "aid-from-the-ether"],
+      "level-2": ["flashing-burst", "icy-blast"],
+      "level-3": ["elemental-aid", "cold-fire"],
+      "level-4": ["forked-beam", "spirit-of-doom"],
+      "level-5": ["engulfed-in-flames", "chromatic-explosion"],
+      "level-6": ["frozen-night", "living-torch"],
+      "level-7": ["stone-fists", "twin-restoration"],
+      "level-8": ["zephyr-wings", "cold-front"],
+      "level-9": ["inferno", "black-hole"],
     },
     backCard:
-      'https://raw.githubusercontent.com//romgar/gloomhaven/master/images/character-ability-cards/SW/sw-back.png',
+      "https://raw.githubusercontent.com//romgar/gloomhaven/master/images/character-ability-cards/SW/sw-back.png",
     icon:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-icons/spellweaver-icon.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-icons/spellweaver-icon.png",
     token:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-icons/spellweaver-character-token.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/character-icons/spellweaver-character-token.png",
   };
 
-  spellWeaver.abilityCards['level-1'].forEach((abilityName, index) => {
+  spellWeaver.abilityCards["level-1"].forEach((abilityName, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com//romgar/gloomhaven/master/images/character-ability-cards/${spellWeaver.code}/${abilityName}.png`,
       backContent: `${spellWeaver.backCard}`,
       x: 0 + 100 * index,
@@ -444,7 +444,7 @@ const genGloomhaven = () => {
   });
 
   items.push({
-    type: 'image',
+    type: "image",
     content: spellWeaver.icon,
     x: 450,
     y: 700,
@@ -453,7 +453,7 @@ const genGloomhaven = () => {
 
   [...Array(5).keys()].forEach((_, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content: spellWeaver.token,
       x: 350 + 20 * index,
       y: 700,
@@ -461,10 +461,10 @@ const genGloomhaven = () => {
     });
   });
 
-  const elements = ['ice', 'air', 'earth', 'fire', 'dark', 'light'];
+  const elements = ["ice", "air", "earth", "fire", "dark", "light"];
   elements.forEach((elementName, index) => {
     items.push({
-      type: 'image',
+      type: "image",
       content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/elements/${elementName}-element.svg`,
       x: 0 + 30 * index,
       y: 0,
@@ -473,28 +473,28 @@ const genGloomhaven = () => {
   });
 
   items.push({
-    type: 'image',
+    type: "image",
     content:
-      'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/elements/element-matboard.png',
+      "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/elements/element-matboard.png",
     x: 0,
     y: 100,
     width: 300,
   });
 
   const ailments = [
-    'reinforcement',
-    'disarm',
-    'immobilise',
-    'wound',
-    'stun',
-    'invisible',
-    'confusion',
-    'poison',
+    "reinforcement",
+    "disarm",
+    "immobilise",
+    "wound",
+    "stun",
+    "invisible",
+    "confusion",
+    "poison",
   ];
   [...Array(5).keys()].forEach((_, rowIndex) => {
     ailments.forEach((ailmentName, index) => {
       items.push({
-        type: 'image',
+        type: "image",
         content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/ailments/${ailmentName}.png`,
         x: 0 + 30 * index,
         y: 100 + 40 * rowIndex,
@@ -503,37 +503,37 @@ const genGloomhaven = () => {
     });
 
     const battleGoals = [
-      'aggressor',
-      'diehard',
-      'dynamo',
-      'executioner',
-      'explorer',
-      'fasthealer',
-      'hoarder',
-      'hunter',
-      'indigent',
-      'layabout',
-      'masochist',
-      'neutralizer',
-      'opener',
-      'pacifist',
-      'plunderer',
-      'professional',
-      'protector',
-      'purist',
-      'sadist',
-      'scrambler',
-      'straggler',
-      'streamliner',
-      'workhorse',
-      'zealot',
+      "aggressor",
+      "diehard",
+      "dynamo",
+      "executioner",
+      "explorer",
+      "fasthealer",
+      "hoarder",
+      "hunter",
+      "indigent",
+      "layabout",
+      "masochist",
+      "neutralizer",
+      "opener",
+      "pacifist",
+      "plunderer",
+      "professional",
+      "protector",
+      "purist",
+      "sadist",
+      "scrambler",
+      "straggler",
+      "streamliner",
+      "workhorse",
+      "zealot",
     ];
     battleGoals.forEach((battleGoalName, index) => {
       items.push({
-        type: 'image',
+        type: "image",
         content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/battle-goals/${battleGoalName}.png`,
         backContent:
-          'https://raw.githubusercontent.com/romgar/gloomhaven/master/images/battle-goals/battlegoal-back.png',
+          "https://raw.githubusercontent.com/romgar/gloomhaven/master/images/battle-goals/battlegoal-back.png",
         x: 200 + 1 * index,
         y: 0 + 1 * index,
         flipped: true,
@@ -542,24 +542,24 @@ const genGloomhaven = () => {
     });
 
     const characterItems = [
-      'boots-of-striding',
-      'cloak-of-invisibility',
-      'eagle-eye-goggles',
-      'heater-shield',
-      'hide-armor',
-      'iron-helmet',
-      'leather-armor',
-      'minor-healing-potion',
-      'minor-power-potion',
-      'minor-stamina-potion',
-      'piercing-bow',
-      'poison-dagger',
-      'war-hammer',
-      'winged-shoes',
+      "boots-of-striding",
+      "cloak-of-invisibility",
+      "eagle-eye-goggles",
+      "heater-shield",
+      "hide-armor",
+      "iron-helmet",
+      "leather-armor",
+      "minor-healing-potion",
+      "minor-power-potion",
+      "minor-stamina-potion",
+      "piercing-bow",
+      "poison-dagger",
+      "war-hammer",
+      "winged-shoes",
     ];
     characterItems.forEach((itemName, index) => {
       items.push({
-        type: 'image',
+        type: "image",
         content: `https://raw.githubusercontent.com/romgar/gloomhaven/master/images/items/1-14/${itemName}.png`,
         x: 1500 + 40 * index,
         y: 1500,

+ 110 - 110
src/games/settlers.js

@@ -6,45 +6,45 @@ const genGame = () => {
   // 20
   [...Array(20)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/clay.png',
-      backContent: '/games/settlers/back.png',
+      type: "image",
+      content: "/games/settlers/clay.png",
+      backContent: "/games/settlers/back.png",
       width: 150,
       x: 100 - v,
       y: 300 - v,
     });
 
     items.push({
-      type: 'image',
-      content: '/games/settlers/stone.png',
-      backContent: '/games/settlers/back.png',
+      type: "image",
+      content: "/games/settlers/stone.png",
+      backContent: "/games/settlers/back.png",
       width: 150,
       x: 300 - v,
       y: 300 - v,
     });
 
     items.push({
-      type: 'image',
-      content: '/games/settlers/wood.png',
-      backContent: '/games/settlers/back.png',
+      type: "image",
+      content: "/games/settlers/wood.png",
+      backContent: "/games/settlers/back.png",
       width: 150,
       x: 500 - v,
       y: 300 - v,
     });
 
     items.push({
-      type: 'image',
-      content: '/games/settlers/wheat.png',
-      backContent: '/games/settlers/back.png',
+      type: "image",
+      content: "/games/settlers/wheat.png",
+      backContent: "/games/settlers/back.png",
       width: 150,
       x: 700 - v,
       y: 300 - v,
     });
 
     items.push({
-      type: 'image',
-      content: '/games/settlers/sheep.png',
-      backContent: '/games/settlers/back.png',
+      type: "image",
+      content: "/games/settlers/sheep.png",
+      backContent: "/games/settlers/back.png",
       width: 150,
       x: 900 - v,
       y: 300 - v,
@@ -54,9 +54,9 @@ const genGame = () => {
   // search
 
   items.push({
-    type: 'image',
-    content: '/games/settlers/cathedral.png',
-    backContent: '/games/settlers/back.png',
+    type: "image",
+    content: "/games/settlers/cathedral.png",
+    backContent: "/games/settlers/back.png",
     width: 150,
     x: 100,
     y: 700,
@@ -64,9 +64,9 @@ const genGame = () => {
 
   [...Array(15)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/knight.png',
-      backContent: '/games/settlers/back.png',
+      type: "image",
+      content: "/games/settlers/knight.png",
+      backContent: "/games/settlers/back.png",
       width: 150,
       x: 300 - v,
       y: 700 - v,
@@ -74,18 +74,18 @@ const genGame = () => {
   });
 
   items.push({
-    type: 'image',
-    content: '/games/settlers/librarie.png',
-    backContent: '/games/settlers/back.png',
+    type: "image",
+    content: "/games/settlers/librarie.png",
+    backContent: "/games/settlers/back.png",
     width: 150,
     x: 500,
     y: 700,
   });
 
   items.push({
-    type: 'image',
-    content: '/games/settlers/market.png',
-    backContent: '/games/settlers/back.png',
+    type: "image",
+    content: "/games/settlers/market.png",
+    backContent: "/games/settlers/back.png",
     width: 150,
     x: 700,
     y: 700,
@@ -93,9 +93,9 @@ const genGame = () => {
 
   [...Array(2)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/progress.png',
-      backContent: '/games/settlers/back.png',
+      type: "image",
+      content: "/games/settlers/progress.png",
+      backContent: "/games/settlers/back.png",
       width: 150,
       x: 900 - v,
       y: 700 - v,
@@ -104,9 +104,9 @@ const genGame = () => {
 
   [...Array(2)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/progress2.png',
-      backContent: '/games/settlers/back.png',
+      type: "image",
+      content: "/games/settlers/progress2.png",
+      backContent: "/games/settlers/back.png",
       width: 150,
       x: 1100 - v,
       y: 700 - v,
@@ -115,9 +115,9 @@ const genGame = () => {
 
   [...Array(2)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/progress3.png',
-      backContent: '/games/settlers/back.png',
+      type: "image",
+      content: "/games/settlers/progress3.png",
+      backContent: "/games/settlers/back.png",
       width: 150,
       x: 1300 - v,
       y: 700 - v,
@@ -125,27 +125,27 @@ const genGame = () => {
   });
 
   items.push({
-    type: 'image',
-    content: '/games/settlers/universty.png',
-    backContent: '/games/settlers/back.png',
+    type: "image",
+    content: "/games/settlers/universty.png",
+    backContent: "/games/settlers/back.png",
     width: 150,
     x: 1500,
     y: 700,
   });
 
   items.push({
-    type: 'image',
-    content: '/games/settlers/cathedral.png',
-    backContent: '/games/settlers/back.png',
+    type: "image",
+    content: "/games/settlers/cathedral.png",
+    backContent: "/games/settlers/back.png",
     width: 150,
     x: 1700,
     y: 700,
   });
 
   items.push({
-    type: 'image',
-    content: '/games/settlers/cathedral.png',
-    backContent: '/games/settlers/back.png',
+    type: "image",
+    content: "/games/settlers/cathedral.png",
+    backContent: "/games/settlers/back.png",
     width: 150,
     x: 1700,
     y: 700,
@@ -155,9 +155,9 @@ const genGame = () => {
 
   [...Array(3)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/tileClay.png',
-      backContent: '/games/settlers/tileBack.png',
+      type: "image",
+      content: "/games/settlers/tileClay.png",
+      backContent: "/games/settlers/tileBack.png",
       width: 300,
       x: 100 - v,
       y: 1000 - v,
@@ -166,9 +166,9 @@ const genGame = () => {
   });
   [...Array(3)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/tileStone.png',
-      backContent: '/games/settlers/tileBack.png',
+      type: "image",
+      content: "/games/settlers/tileStone.png",
+      backContent: "/games/settlers/tileBack.png",
       width: 300,
       x: 400 - v,
       y: 1000 - v,
@@ -177,9 +177,9 @@ const genGame = () => {
   });
   [...Array(4)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/tileSheep.png',
-      backContent: '/games/settlers/tileBack.png',
+      type: "image",
+      content: "/games/settlers/tileSheep.png",
+      backContent: "/games/settlers/tileBack.png",
       width: 300,
       x: 700 - v,
       y: 1000 - v,
@@ -188,9 +188,9 @@ const genGame = () => {
   });
   [...Array(4)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/tileWheat.png',
-      backContent: '/games/settlers/tileBack.png',
+      type: "image",
+      content: "/games/settlers/tileWheat.png",
+      backContent: "/games/settlers/tileBack.png",
       width: 300,
       x: 1000 - v,
       y: 1000 - v,
@@ -199,9 +199,9 @@ const genGame = () => {
   });
   [...Array(4)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/tileWood.png',
-      backContent: '/games/settlers/tileBack.png',
+      type: "image",
+      content: "/games/settlers/tileWood.png",
+      backContent: "/games/settlers/tileBack.png",
       width: 300,
       x: 1300 - v,
       y: 1000 - v,
@@ -210,9 +210,9 @@ const genGame = () => {
   });
 
   items.push({
-    type: 'image',
-    content: '/games/settlers/tileDesert.png',
-    backContent: '/games/settlers/tileBack.png',
+    type: "image",
+    content: "/games/settlers/tileDesert.png",
+    backContent: "/games/settlers/tileBack.png",
     width: 300,
     x: 1600,
     y: 1000,
@@ -221,9 +221,9 @@ const genGame = () => {
 
   [...Array(9)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/tileSea2.png',
-      backContent: '/games/settlers/tileBack.png',
+      type: "image",
+      content: "/games/settlers/tileSea2.png",
+      backContent: "/games/settlers/tileBack.png",
       width: 300,
       x: 1900 - v,
       y: 1000 - v,
@@ -233,9 +233,9 @@ const genGame = () => {
 
   [...Array(4)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/portAll.png',
-      backContent: '/games/settlers/tileBack.png',
+      type: "image",
+      content: "/games/settlers/portAll.png",
+      backContent: "/games/settlers/tileBack.png",
       width: 300,
       x: 300 - v,
       y: 1400 - v,
@@ -244,45 +244,45 @@ const genGame = () => {
   });
 
   items.push({
-    type: 'image',
-    content: '/games/settlers/portClay.png',
-    backContent: '/games/settlers/tileBack.png',
+    type: "image",
+    content: "/games/settlers/portClay.png",
+    backContent: "/games/settlers/tileBack.png",
     width: 300,
     x: 600,
     y: 1400,
     rotation: 30,
   });
   items.push({
-    type: 'image',
-    content: '/games/settlers/portStone.png',
-    backContent: '/games/settlers/tileBack.png',
+    type: "image",
+    content: "/games/settlers/portStone.png",
+    backContent: "/games/settlers/tileBack.png",
     width: 300,
     x: 900,
     y: 1400,
     rotation: 30,
   });
   items.push({
-    type: 'image',
-    content: '/games/settlers/portSheep.png',
-    backContent: '/games/settlers/tileBack.png',
+    type: "image",
+    content: "/games/settlers/portSheep.png",
+    backContent: "/games/settlers/tileBack.png",
     width: 300,
     x: 1200,
     y: 1400,
     rotation: 30,
   });
   items.push({
-    type: 'image',
-    content: '/games/settlers/portWheat.png',
-    backContent: '/games/settlers/tileBack.png',
+    type: "image",
+    content: "/games/settlers/portWheat.png",
+    backContent: "/games/settlers/tileBack.png",
     width: 300,
     x: 1500,
     y: 1400,
     rotation: 30,
   });
   items.push({
-    type: 'image',
-    content: '/games/settlers/portWood.png',
-    backContent: '/games/settlers/tileBack.png',
+    type: "image",
+    content: "/games/settlers/portWood.png",
+    backContent: "/games/settlers/tileBack.png",
     width: 300,
     x: 1800,
     y: 1400,
@@ -293,8 +293,8 @@ const genGame = () => {
 
   [...Array(5)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/houseRed.png',
+      type: "image",
+      content: "/games/settlers/houseRed.png",
       width: 70,
       x: 100 + 100 * v,
       y: 1800,
@@ -303,8 +303,8 @@ const genGame = () => {
 
   [...Array(4)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/house2red.png',
+      type: "image",
+      content: "/games/settlers/house2red.png",
       width: 90,
       x: 100 + 100 * v,
       y: 1900,
@@ -313,8 +313,8 @@ const genGame = () => {
 
   [...Array(15)].forEach((_, v) => {
     items.push({
-      type: 'rect',
-      color: '#FF0000',
+      type: "rect",
+      color: "#FF0000",
       width: 20,
       height: 100,
       x: 700 + 40 * v,
@@ -324,8 +324,8 @@ const genGame = () => {
 
   [...Array(5)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/houseBlue.png',
+      type: "image",
+      content: "/games/settlers/houseBlue.png",
       width: 70,
       x: 100 + 100 * v,
       y: 2050,
@@ -334,8 +334,8 @@ const genGame = () => {
 
   [...Array(4)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/house2Blue.png',
+      type: "image",
+      content: "/games/settlers/house2Blue.png",
       width: 90,
       x: 100 + 100 * v,
       y: 2100,
@@ -344,8 +344,8 @@ const genGame = () => {
 
   [...Array(15)].forEach((_, v) => {
     items.push({
-      type: 'rect',
-      color: '#0000FF',
+      type: "rect",
+      color: "#0000FF",
       width: 20,
       height: 100,
       x: 700 + 40 * v,
@@ -355,8 +355,8 @@ const genGame = () => {
 
   [...Array(5)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/houseOrange.png',
+      type: "image",
+      content: "/games/settlers/houseOrange.png",
       width: 70,
       x: 100 + 100 * v,
       y: 2250,
@@ -365,8 +365,8 @@ const genGame = () => {
 
   [...Array(4)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/house2Orange.png',
+      type: "image",
+      content: "/games/settlers/house2Orange.png",
       width: 90,
       x: 100 + 100 * v,
       y: 2300,
@@ -375,8 +375,8 @@ const genGame = () => {
 
   [...Array(15)].forEach((_, v) => {
     items.push({
-      type: 'rect',
-      color: '#ff6600',
+      type: "rect",
+      color: "#ff6600",
       width: 20,
       height: 100,
       x: 700 + 40 * v,
@@ -386,8 +386,8 @@ const genGame = () => {
 
   [...Array(5)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/houseWhite.png',
+      type: "image",
+      content: "/games/settlers/houseWhite.png",
       width: 70,
       x: 100 + 100 * v,
       y: 2450,
@@ -396,8 +396,8 @@ const genGame = () => {
 
   [...Array(4)].forEach((_, v) => {
     items.push({
-      type: 'image',
-      content: '/games/settlers/house2White.png',
+      type: "image",
+      content: "/games/settlers/house2White.png",
       width: 90,
       x: 100 + 100 * v,
       y: 2500,
@@ -406,8 +406,8 @@ const genGame = () => {
 
   [...Array(15)].forEach((_, v) => {
     items.push({
-      type: 'rect',
-      color: '#ffd5d5',
+      type: "rect",
+      color: "#ffd5d5",
       width: 20,
       height: 100,
       x: 700 + 40 * v,
@@ -416,8 +416,8 @@ const genGame = () => {
   });
 
   items.push({
-    type: 'image',
-    content: '/games/settlers/rogue.png',
+    type: "image",
+    content: "/games/settlers/rogue.png",
     x: 100,
     y: 2700,
   });

+ 26 - 26
src/games/tiktok.js

@@ -2,16 +2,16 @@ const genTikTok = () => {
   const items = [];
 
   items.push({
-    type: 'image',
-    content: '/games/tiktok.svg',
+    type: "image",
+    content: "/games/tiktok.svg",
     locked: true,
     x: 100,
     y: 100,
   });
 
   items.push({
-    type: 'rect',
-    color: '#00D022',
+    type: "rect",
+    color: "#00D022",
     width: 80,
     height: 80,
     x: 10,
@@ -19,8 +19,8 @@ const genTikTok = () => {
   });
 
   items.push({
-    type: 'rect',
-    color: '#00D022',
+    type: "rect",
+    color: "#00D022",
     width: 80,
     height: 80,
     x: 15,
@@ -28,8 +28,8 @@ const genTikTok = () => {
   });
 
   items.push({
-    type: 'rect',
-    color: '#00D022',
+    type: "rect",
+    color: "#00D022",
     width: 80,
     height: 80,
     x: 20,
@@ -37,8 +37,8 @@ const genTikTok = () => {
   });
 
   items.push({
-    type: 'rect',
-    color: '#00D022',
+    type: "rect",
+    color: "#00D022",
     width: 80,
     height: 80,
     x: 25,
@@ -46,8 +46,8 @@ const genTikTok = () => {
   });
 
   items.push({
-    type: 'rect',
-    color: '#00D022',
+    type: "rect",
+    color: "#00D022",
     width: 80,
     height: 80,
     x: 30,
@@ -55,8 +55,8 @@ const genTikTok = () => {
   });
 
   items.push({
-    type: 'rect',
-    color: '#00D022',
+    type: "rect",
+    color: "#00D022",
     width: 80,
     height: 80,
     x: 35,
@@ -66,48 +66,48 @@ const genTikTok = () => {
   // Red player
 
   items.push({
-    type: 'round',
-    color: '#D00022',
+    type: "round",
+    color: "#D00022",
     radius: 80,
     x: 1010,
     y: 1050,
   });
 
   items.push({
-    type: 'round',
-    color: '#D00022',
+    type: "round",
+    color: "#D00022",
     radius: 80,
     x: 1015,
     y: 1052,
   });
 
   items.push({
-    type: 'round',
-    color: '#D00022',
+    type: "round",
+    color: "#D00022",
     radius: 80,
     x: 1020,
     y: 1054,
   });
 
   items.push({
-    type: 'round',
-    color: '#D00022',
+    type: "round",
+    color: "#D00022",
     radius: 80,
     x: 1025,
     y: 1056,
   });
 
   items.push({
-    type: 'round',
-    color: '#D00022',
+    type: "round",
+    color: "#D00022",
     radius: 80,
     x: 1030,
     y: 1058,
   });
 
   items.push({
-    type: 'round',
-    color: '#D00022',
+    type: "round",
+    color: "#D00022",
     radius: 80,
     x: 1035,
     y: 1060,

+ 5 - 5
src/hooks/useC2C.js

@@ -1,6 +1,6 @@
-import React, { useContext } from 'react';
-import { useSocket } from '@scripters/use-socket.io';
-import { join } from 'client2client.io';
+import React, { useContext } from "react";
+import { useSocket } from "@scripters/use-socket.io";
+import { join } from "client2client.io";
 
 export const C2CContext = React.createContext([null, false]);
 
@@ -16,10 +16,10 @@ export const C2CProvider = ({ room, ...props }) => {
       return;
     }
     join(socket, room, (newRoom) => {
-      console.log('isMaster');
+      console.log("isMaster");
       setIsMaster(true);
     }).then((newRoom) => {
-      console.log('Connected…');
+      console.log("Connected…");
       roomRef.current = newRoom;
 
       setC2c(newRoom);

+ 3 - 3
src/hooks/useLocalStorage.js

@@ -1,4 +1,4 @@
-import React from 'react';
+import React from "react";
 
 const useLocalStorage = (key, initialValue) => {
   // State to store our value
@@ -49,9 +49,9 @@ const useLocalStorage = (key, initialValue) => {
         setStoredValue(JSON.parse(e.newValue));
       }
     };
-    window.addEventListener('storage', localStorageChanged);
+    window.addEventListener("storage", localStorageChanged);
     return () => {
-      window.removeEventListener('storage', localStorageChanged);
+      window.removeEventListener("storage", localStorageChanged);
     };
   }, [key]);
 

+ 12 - 12
src/hooks/useUser.js

@@ -1,14 +1,14 @@
-import React from 'react';
-import { useC2C } from './useC2C';
-import randomColor from 'randomcolor';
-import debounce from 'lodash.debounce';
-import { atom, useRecoilState } from 'recoil';
+import React from "react";
+import { useC2C } from "./useC2C";
+import randomColor from "randomcolor";
+import debounce from "lodash.debounce";
+import { atom, useRecoilState } from "recoil";
 
 const getUser = () => {
   if (localStorage.user) {
     const localUser = {
-      name: 'Player',
-      color: randomColor({ luminosity: 'dark' }),
+      name: "Player",
+      color: randomColor({ luminosity: "dark" }),
       ...JSON.parse(localStorage.user),
     };
     // Id is given by server
@@ -17,19 +17,19 @@ const getUser = () => {
     return localUser;
   }
   const newUser = {
-    name: 'Player',
-    color: randomColor({ luminosity: 'dark' }),
+    name: "Player",
+    color: randomColor({ luminosity: "dark" }),
   };
   persistUser(newUser);
   return newUser;
 };
 
 const persistUser = (user) => {
-  localStorage.setItem('user', JSON.stringify(user));
+  localStorage.setItem("user", JSON.stringify(user));
 };
 
 export const userAtom = atom({
-  key: 'user',
+  key: "user",
   default: getUser(),
 });
 
@@ -57,7 +57,7 @@ function useUser() {
   // eslint-disable-next-line react-hooks/exhaustive-deps
   const debouncedEmitUpdateUser = React.useCallback(
     debounce((newUser) => {
-      c2c.publish('userUpdate', newUser, true);
+      c2c.publish("userUpdate", newUser, true);
     }, 500),
     [c2c]
   );

+ 14 - 14
src/hooks/useUsers.js

@@ -1,5 +1,5 @@
-import React from 'react';
-import { useC2C } from './useC2C';
+import React from "react";
+import { useC2C } from "./useC2C";
 
 function useUsers() {
   const usersRef = React.useRef([]);
@@ -8,9 +8,9 @@ function useUsers() {
 
   React.useEffect(() => {
     if (joined) {
-      console.log('joined');
+      console.log("joined");
       if (!isMaster) {
-        c2c.call('getUserList').then((userList) => {
+        c2c.call("getUserList").then((userList) => {
           usersRef.current = userList;
           setUsers(userList);
         });
@@ -23,7 +23,7 @@ function useUsers() {
     if (joined) {
       if (isMaster) {
         c2c
-          .register('getUserList', () => {
+          .register("getUserList", () => {
             return usersRef.current;
           })
           .then((unregister) => {
@@ -31,18 +31,18 @@ function useUsers() {
           });
 
         unsub.push(
-          c2c.subscribe('userLeave', (userId) => {
-            console.log('userLeave');
+          c2c.subscribe("userLeave", (userId) => {
+            console.log("userLeave");
             usersRef.current = usersRef.current.filter(
               ({ id }) => id !== userId
             );
             setUsers(usersRef.current);
-            console.log('publish new user list', usersRef.current);
-            c2c.publish('updateUserList', usersRef.current);
+            console.log("publish new user list", usersRef.current);
+            c2c.publish("updateUserList", usersRef.current);
           })
         );
         unsub.push(
-          c2c.subscribe('userUpdate', (user) => {
+          c2c.subscribe("userUpdate", (user) => {
             if (usersRef.current.find((u) => u.id === user.id)) {
               const newUsers = usersRef.current.map((u) =>
                 u.id === user.id ? user : u
@@ -53,15 +53,15 @@ function useUsers() {
               usersRef.current = newUsers;
             }
             setUsers(usersRef.current);
-            console.log('publish new user list', usersRef.current);
-            c2c.publish('updateUserList', usersRef.current);
+            console.log("publish new user list", usersRef.current);
+            c2c.publish("updateUserList", usersRef.current);
           })
         );
       }
 
       unsub.push(
-        c2c.subscribe('updateUserList', (newList) => {
-          console.log('User list', newList);
+        c2c.subscribe("updateUserList", (newList) => {
+          console.log("User list", newList);
           usersRef.current = newList;
           setUsers(usersRef.current);
         })

+ 3 - 3
src/index.css

@@ -1,7 +1,7 @@
 body {
   margin: 0;
-  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
-    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
+    "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
     sans-serif;
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
@@ -10,6 +10,6 @@ body {
 }
 
 code {
-  font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+  font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
     monospace;
 }

+ 6 - 6
src/index.js

@@ -1,14 +1,14 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-import './index.css';
-import App from './App';
-import * as serviceWorker from './serviceWorker';
+import React from "react";
+import ReactDOM from "react-dom";
+import "./index.css";
+import App from "./App";
+import * as serviceWorker from "./serviceWorker";
 
 ReactDOM.render(
   <React.StrictMode>
     <App />
   </React.StrictMode>,
-  document.getElementById('root')
+  document.getElementById("root")
 );
 
 // If you want your app to work offline and load faster, you can change

+ 11 - 11
src/server.js

@@ -1,30 +1,30 @@
-var express = require('express');
-var cors = require('cors');
-const path = require('path');
+var express = require("express");
+var cors = require("cors");
+const path = require("path");
 
 var app = express();
-var http = require('http').createServer(app);
-var handleC2C = require('client2client.io').handleC2C;
+var http = require("http").createServer(app);
+var handleC2C = require("client2client.io").handleC2C;
 
 app.use(cors());
 
 const port = process.env.PORT || 4000;
 
-const socketPath = process.env.REACT_APP_SOCKET_PATH || '/socket.io';
+const socketPath = process.env.REACT_APP_SOCKET_PATH || "/socket.io";
 
-var io = require('socket.io')(http, { path: socketPath });
+var io = require("socket.io")(http, { path: socketPath });
 
-app.use(express.static(path.join(__dirname, '../build')));
+app.use(express.static(path.join(__dirname, "../build")));
 
 // Handles any requests that don't match the ones above
-app.get('*', (req, res) => {
-  res.sendFile(path.join(__dirname, '../build/index.html'));
+app.get("*", (req, res) => {
+  res.sendFile(path.join(__dirname, "../build/index.html"));
 });
 
 http.listen(port, () => {
   console.log(`listening on *:${port}`);
 });
 
-io.on('connection', (socket) => {
+io.on("connection", (socket) => {
   handleC2C(socket);
 });

+ 16 - 16
src/serviceWorker.js

@@ -11,9 +11,9 @@
 // opt-in, read https://bit.ly/CRA-PWA
 
 const isLocalhost = Boolean(
-  window.location.hostname === 'localhost' ||
+  window.location.hostname === "localhost" ||
     // [::1] is the IPv6 localhost address.
-    window.location.hostname === '[::1]' ||
+    window.location.hostname === "[::1]" ||
     // 127.0.0.0/8 are considered localhost for IPv4.
     window.location.hostname.match(
       /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
@@ -21,7 +21,7 @@ const isLocalhost = Boolean(
 );
 
 export function register(config) {
-  if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
+  if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
     // The URL constructor is available in all browsers that support SW.
     const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
     if (publicUrl.origin !== window.location.origin) {
@@ -31,7 +31,7 @@ export function register(config) {
       return;
     }
 
-    window.addEventListener('load', () => {
+    window.addEventListener("load", () => {
       const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
 
       if (isLocalhost) {
@@ -42,8 +42,8 @@ export function register(config) {
         // service worker/PWA documentation.
         navigator.serviceWorker.ready.then(() => {
           console.log(
-            'This web app is being served cache-first by a service ' +
-              'worker. To learn more, visit https://bit.ly/CRA-PWA'
+            "This web app is being served cache-first by a service " +
+              "worker. To learn more, visit https://bit.ly/CRA-PWA"
           );
         });
       } else {
@@ -64,14 +64,14 @@ function registerValidSW(swUrl, config) {
           return;
         }
         installingWorker.onstatechange = () => {
-          if (installingWorker.state === 'installed') {
+          if (installingWorker.state === "installed") {
             if (navigator.serviceWorker.controller) {
               // At this point, the updated precached content has been fetched,
               // but the previous service worker will still serve the older
               // content until all client tabs are closed.
               console.log(
-                'New content is available and will be used when all ' +
-                  'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
+                "New content is available and will be used when all " +
+                  "tabs for this page are closed. See https://bit.ly/CRA-PWA."
               );
 
               // Execute callback
@@ -82,7 +82,7 @@ function registerValidSW(swUrl, config) {
               // At this point, everything has been precached.
               // It's the perfect time to display a
               // "Content is cached for offline use." message.
-              console.log('Content is cached for offline use.');
+              console.log("Content is cached for offline use.");
 
               // Execute callback
               if (config && config.onSuccess) {
@@ -94,21 +94,21 @@ function registerValidSW(swUrl, config) {
       };
     })
     .catch((error) => {
-      console.error('Error during service worker registration:', error);
+      console.error("Error during service worker registration:", error);
     });
 }
 
 function checkValidServiceWorker(swUrl, config) {
   // Check if the service worker can be found. If it can't reload the page.
   fetch(swUrl, {
-    headers: { 'Service-Worker': 'script' },
+    headers: { "Service-Worker": "script" },
   })
     .then((response) => {
       // Ensure service worker exists, and that we really are getting a JS file.
-      const contentType = response.headers.get('content-type');
+      const contentType = response.headers.get("content-type");
       if (
         response.status === 404 ||
-        (contentType != null && contentType.indexOf('javascript') === -1)
+        (contentType != null && contentType.indexOf("javascript") === -1)
       ) {
         // No service worker found. Probably a different app. Reload the page.
         navigator.serviceWorker.ready.then((registration) => {
@@ -123,13 +123,13 @@ function checkValidServiceWorker(swUrl, config) {
     })
     .catch(() => {
       console.log(
-        'No internet connection found. App is running in offline mode.'
+        "No internet connection found. App is running in offline mode."
       );
     });
 }
 
 export function unregister() {
-  if ('serviceWorker' in navigator) {
+  if ("serviceWorker" in navigator) {
     navigator.serviceWorker.ready
       .then((registration) => {
         registration.unregister();

+ 1 - 1
src/setupTests.js

@@ -2,4 +2,4 @@
 // allows you to do things like:
 // expect(element).toHaveTextContent(/react/i)
 // learn more: https://github.com/testing-library/jest-dom
-import '@testing-library/jest-dom/extend-expect';
+import "@testing-library/jest-dom/extend-expect";

+ 11 - 11
src/views/BoardView.js

@@ -1,15 +1,15 @@
-import React from 'react';
+import React from "react";
 
-import Users from '../components/Users';
-import GameController from '../components/GameController';
-import ZoomPanRotate from '../components/PanZoomRotate';
-import Board from '../components/Board';
-import { ItemListAtom } from '../components/Items';
-import { AvailableItemListAtom } from '../components/AvailableItems';
-import useUser from '../hooks/useUser';
-import useUsers from '../hooks/useUsers';
-import { useRecoilState } from 'recoil';
-import SelectedItems from '../components/SelectedItems';
+import Users from "../components/Users";
+import GameController from "../components/GameController";
+import ZoomPanRotate from "../components/PanZoomRotate";
+import Board from "../components/Board";
+import { ItemListAtom } from "../components/Items";
+import { AvailableItemListAtom } from "../components/AvailableItems";
+import useUser from "../hooks/useUser";
+import useUsers from "../hooks/useUsers";
+import { useRecoilState } from "recoil";
+import SelectedItems from "../components/SelectedItems";
 
 export const BoardView = () => {
   const users = useUsers();