diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0913182d14..79b3bd11c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,4 +70,4 @@ jobs: node-version: "20" cache: "npm" - run: npm ci - - run: npx prettier --check . + - run: npx biome format . diff --git a/.husky/pre-commit b/.husky/pre-commit index a282f31f54..b03d3b780f 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,6 +1,3 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - # Add PATH setup to ensure npx is found export PATH="/usr/local/bin:$HOME/.npm-global/bin:$HOME/.nvm/versions/node/$(node -v)/bin:$PATH" diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 6be85b2f6e..0000000000 --- a/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -*.[tj]s diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index b203bcc81a..0000000000 --- a/.prettierrc +++ /dev/null @@ -1,17 +0,0 @@ -{ - "overrides": [ - { - "files": ".husky/**", - "options": { - "plugins": [] - } - }, - { - "files": "Dockerfile", - "options": { - "plugins": [] - } - } - ], - "plugins": ["prettier-plugin-organize-imports", "prettier-plugin-sh"] -} diff --git a/biome.json b/biome.json new file mode 100644 index 0000000000..89d5e82882 --- /dev/null +++ b/biome.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.2.2/schema.json", + "assist": { + "actions": { + "source": { + "organizeImports": "on" + } + }, + "enabled": true + }, + "files": { + "ignoreUnknown": true + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2 + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + }, + "linter": { + "enabled": false + }, + "overrides": [ + { + "assist": { + "actions": { + "source": { + "useSortedKeys": "on" + } + } + }, + "includes": [ + "eslint-plugin-local/**/*.{js,ts,jsx,tsx}", + "src/**/*.{js,ts,jsx,tsx}", + "**/*.test.{js,ts,jsx,tsx}", + "tests/**/*.{js,ts,jsx,tsx}" + ] + } + ], + "vcs": { + "clientKind": "git", + "enabled": true, + "useIgnoreFile": true + } +} diff --git a/eslint.config.js b/eslint.config.js index d2d5df4ec8..0e82e5d2e4 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,13 +1,12 @@ -import eslintConfigPrettier from "eslint-config-prettier/flat"; -import eslintPluginLocal from "./eslint-plugin-local/plugin.js"; +import path from "node:path"; import { fileURLToPath } from "node:url"; -import globals from "globals"; import { includeIgnoreFile } from "@eslint/compat"; -import jest from "eslint-plugin-jest"; -import path from "node:path"; import pluginJs from "@eslint/js"; import stylistic from "@stylistic/eslint-plugin"; +import jest from "eslint-plugin-jest"; +import globals from "globals"; import tseslint from "typescript-eslint"; +import eslintPluginLocal from "./eslint-plugin-local/plugin.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -21,7 +20,6 @@ export default [ { languageOptions: { globals: { ...globals.browser, ...globals.node } } }, pluginJs.configs.recommended, ...tseslint.configs.recommended, - eslintConfigPrettier, { languageOptions: { parserOptions: { @@ -55,7 +53,6 @@ export default [ rules: { // Enable rules "@stylistic/quotes": ["error", "double", { avoidEscape: true }], - "@stylistic/indent": ["error", 2], "@stylistic/semi": "error", "@stylistic/space-infix-ops": "error", "@stylistic/type-annotation-spacing": [ @@ -71,10 +68,7 @@ export default [ }, ], "@stylistic/eol-last": "error", - "@typescript-eslint/consistent-type-definitions": [ - "error", - "type", - ], + "@typescript-eslint/consistent-type-definitions": ["error", "type"], "@typescript-eslint/no-duplicate-enum-values": "error", "@typescript-eslint/no-explicit-any": "error", "@typescript-eslint/no-inferrable-types": "error", @@ -88,9 +82,7 @@ export default [ "@typescript-eslint/prefer-literal-enum-member": "error", "@typescript-eslint/prefer-nullish-coalescing": "error", "@typescript-eslint/prefer-readonly": "error", - "eqeqeq": "error", - "indent": "off", // @stylistic/indent - "sort-keys": "error", + eqeqeq: "error", "@typescript-eslint/no-unsafe-argument": "error", "@typescript-eslint/no-unsafe-assignment": "error", "@typescript-eslint/no-unsafe-member-access": "error", @@ -107,7 +99,10 @@ export default [ "function-call-argument-newline": ["error", "consistent"], "max-depth": ["error", { max: 5 }], "max-len": ["error", { code: 120 }], - "max-lines": ["error", { max: 677, skipBlankLines: true, skipComments: true }], + "max-lines": [ + "error", + { max: 677, skipBlankLines: true, skipComments: true }, + ], "max-lines-per-function": ["error", { max: 561 }], "no-loss-of-precision": "error", "no-multi-spaces": "error", @@ -115,22 +110,30 @@ export default [ "no-trailing-spaces": "error", "object-curly-newline": ["error", { multiline: true, consistent: true }], "object-curly-spacing": ["error", "always"], - "object-property-newline": ["error", { allowAllPropertiesOnSameLine: true }], + "object-property-newline": [ + "error", + { allowAllPropertiesOnSameLine: true }, + ], "object-shorthand": ["error", "always"], "no-undef": "error", "no-unused-vars": "off", // @typescript-eslint/no-unused-vars - "prefer-destructuring": ["error", { - array: false, - object: true, - }], - "quote-props": ["error", "consistent-as-needed"], - "sort-imports": "error", + "prefer-destructuring": [ + "error", + { + array: false, + object: true, + }, + ], + "quote-props": ["error", "as-needed"], "space-before-blocks": ["error", "always"], - "space-before-function-paren": ["error", { - anonymous: "always", - named: "never", - asyncArrow: "always", - }], + "space-before-function-paren": [ + "error", + { + anonymous: "always", + named: "never", + asyncArrow: "always", + }, + ], "space-infix-ops": "off", }, }, @@ -149,7 +152,6 @@ export default [ "@typescript-eslint/no-unsafe-assignment": "off", "@typescript-eslint/no-unsafe-member-access": "off", "max-len": "off", - "sort-keys": "off", }, }, { @@ -158,22 +160,10 @@ export default [ ...globals.jest, }, }, - files: [ - "**/*.test.{js,ts,jsx,tsx}", - "tests/**/*.{js,ts,jsx,tsx}", - ], + files: ["**/*.test.{js,ts,jsx,tsx}", "tests/**/*.{js,ts,jsx,tsx}"], plugins: ["jest"], ...jest.configs["flat/style"], }, - { - files: [ - "src/client/**/*.{js,ts,jsx,tsx}", - ], - rules: { - // Disabled rules for frontend - "sort-keys": "off", - }, - }, { plugins: { local: eslintPluginLocal, diff --git a/package-lock.json b/package-lock.json index 6b0ba88a55..c0caf3114a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,7 @@ "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.3", "@babel/preset-typescript": "^7.24.7", + "@biomejs/biome": "^2.2.2", "@datastructures-js/priority-queue": "^6.3.3", "@eslint/compat": "^1.2.7", "@eslint/js": "^9.21.0", @@ -75,7 +76,6 @@ "css-loader": "^7.1.2", "d3": "^7.9.0", "eslint": "^9.21.0", - "eslint-config-prettier": "^10.1.1", "eslint-formatter-gha": "^1.5.2", "eslint-plugin-jest": "^29.0.1", "eslint-webpack-plugin": "^5.0.0", @@ -95,9 +95,6 @@ "pixi.js": "^8.11.0", "postcss": "^8.5.1", "postcss-loader": "^8.1.1", - "prettier": "^3.5.3", - "prettier-plugin-organize-imports": "^4.1.0", - "prettier-plugin-sh": "^0.17.4", "protobufjs": "^7.5.3", "raw-loader": "^4.0.2", "sinon": "^21.0.0", @@ -2802,6 +2799,169 @@ "dev": true, "license": "MIT" }, + "node_modules/@biomejs/biome": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.2.tgz", + "integrity": "sha512-j1omAiQWCkhuLgwpMKisNKnsM6W8Xtt1l0WZmqY/dFj8QPNkIoTvk4tSsi40FaAAkBE1PU0AFG2RWFBWenAn+w==", + "dev": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.2.2", + "@biomejs/cli-darwin-x64": "2.2.2", + "@biomejs/cli-linux-arm64": "2.2.2", + "@biomejs/cli-linux-arm64-musl": "2.2.2", + "@biomejs/cli-linux-x64": "2.2.2", + "@biomejs/cli-linux-x64-musl": "2.2.2", + "@biomejs/cli-win32-arm64": "2.2.2", + "@biomejs/cli-win32-x64": "2.2.2" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.2.2.tgz", + "integrity": "sha512-6ePfbCeCPryWu0CXlzsWNZgVz/kBEvHiPyNpmViSt6A2eoDf4kXs3YnwQPzGjy8oBgQulrHcLnJL0nkCh80mlQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.2.2.tgz", + "integrity": "sha512-Tn4JmVO+rXsbRslml7FvKaNrlgUeJot++FkvYIhl1OkslVCofAtS35MPlBMhXgKWF9RNr9cwHanrPTUUXcYGag==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.2.2.tgz", + "integrity": "sha512-JfrK3gdmWWTh2J5tq/rcWCOsImVyzUnOS2fkjhiYKCQ+v8PqM+du5cfB7G1kXas+7KQeKSWALv18iQqdtIMvzw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.2.tgz", + "integrity": "sha512-/MhYg+Bd6renn6i1ylGFL5snYUn/Ct7zoGVKhxnro3bwekiZYE8Kl39BSb0MeuqM+72sThkQv4TnNubU9njQRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.2.2.tgz", + "integrity": "sha512-Ogb+77edO5LEP/xbNicACOWVLt8mgC+E1wmpUakr+O4nKwLt9vXe74YNuT3T1dUBxC/SnrVmlzZFC7kQJEfquQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.2.tgz", + "integrity": "sha512-ZCLXcZvjZKSiRY/cFANKg+z6Fhsf9MHOzj+NrDQcM+LbqYRT97LyCLWy2AS+W2vP+i89RyRM+kbGpUzbRTYWig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.2.2.tgz", + "integrity": "sha512-wBe2wItayw1zvtXysmHJQoQqXlTzHSpQRyPpJKiNIR21HzH/CrZRDFic1C1jDdp+zAPtqhNExa0owKMbNwW9cQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.2.2.tgz", + "integrity": "sha512-DAuHhHekGfiGb6lCcsT4UyxQmVwQiBCBUMwVra/dcOSs9q8OhfaZgey51MlekT3p8UwRqtXQfFuEJBhJNdLZwg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, "node_modules/@colors/colors": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", @@ -5573,16 +5733,6 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", "license": "BSD-3-Clause" }, - "node_modules/@reteps/dockerfmt": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@reteps/dockerfmt/-/dockerfmt-0.3.6.tgz", - "integrity": "sha512-Tb5wIMvBf/nLejTQ61krK644/CEMB/cpiaIFXqGApfGqO3GwcR3qnI0DbmkFVCl2OyEp8LnLX3EkucoL0+tbFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^v12.20.0 || ^14.13.0 || >=16.0.0" - } - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -11115,22 +11265,6 @@ } } }, - "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "funding": { - "url": "https://opencollective.com/eslint-config-prettier" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, "node_modules/eslint-formatter-gha": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/eslint-formatter-gha/-/eslint-formatter-gha-1.5.2.tgz", @@ -17139,59 +17273,6 @@ "node": ">= 0.8.0" } }, - "node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-plugin-organize-imports": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz", - "integrity": "sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "prettier": ">=2.0", - "typescript": ">=2.9", - "vue-tsc": "^2.1.0" - }, - "peerDependenciesMeta": { - "vue-tsc": { - "optional": true - } - } - }, - "node_modules/prettier-plugin-sh": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/prettier-plugin-sh/-/prettier-plugin-sh-0.17.4.tgz", - "integrity": "sha512-aAVKXZ7GTEMZdZsIPSwMwddwPvt2ibMbRGd4OJAP0G7QoeYZV+mPNg2Oln3R53sZ4PVjeAA7Xzi/PuI0QlHHfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@reteps/dockerfmt": "^0.3.5", - "sh-syntax": "^0.5.6" - }, - "engines": { - "node": ">=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - }, - "peerDependencies": { - "prettier": "^3.0.3" - } - }, "node_modules/pretty-error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", @@ -18196,22 +18277,6 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, - "node_modules/sh-syntax": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/sh-syntax/-/sh-syntax-0.5.8.tgz", - "integrity": "sha512-JfVoxf4FxQI5qpsPbkHhZo+n6N9YMJobyl4oGEUBb/31oQYlgTjkXQD8PBiafS2UbWoxrTO0Z5PJUBXEPAG1Zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/sh-syntax" - } - }, "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", diff --git a/package.json b/package.json index 9f131e0ec7..cc3c91f408 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "test": "jest", "perf": "npx tsx tests/perf/*.ts", "test:coverage": "jest --coverage", - "format": "prettier --ignore-unknown --write .", + "format": "biome format --write .", "lint": "eslint", "lint:fix": "eslint --fix", "prepare": "husky" @@ -19,13 +19,14 @@ "lint-staged": { "**/*": [ "eslint --fix", - "prettier --ignore-unknown --write" + "biome check --fix" ] }, "devDependencies": { "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.3", "@babel/preset-typescript": "^7.24.7", + "@biomejs/biome": "^2.2.2", "@datastructures-js/priority-queue": "^6.3.3", "@eslint/compat": "^1.2.7", "@eslint/js": "^9.21.0", @@ -61,7 +62,6 @@ "css-loader": "^7.1.2", "d3": "^7.9.0", "eslint": "^9.21.0", - "eslint-config-prettier": "^10.1.1", "eslint-formatter-gha": "^1.5.2", "eslint-plugin-jest": "^29.0.1", "eslint-webpack-plugin": "^5.0.0", @@ -81,9 +81,6 @@ "pixi.js": "^8.11.0", "postcss": "^8.5.1", "postcss-loader": "^8.1.1", - "prettier": "^3.5.3", - "prettier-plugin-organize-imports": "^4.1.0", - "prettier-plugin-sh": "^0.17.4", "protobufjs": "^7.5.3", "raw-loader": "^4.0.2", "sinon": "^21.0.0", diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index ab29a1b78e..6387df18d7 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -1,11 +1,20 @@ +import { translateText } from "../client/Utils"; +import { ServerConfig } from "../core/configuration/Config"; +import { getConfig } from "../core/configuration/ConfigLoader"; +import { EventBus } from "../core/EventBus"; +import { PlayerActions, UnitType } from "../core/game/Game"; +import { TileRef } from "../core/game/GameMap"; +import { GameMapLoader } from "../core/game/GameMapLoader"; import { - AutoUpgradeEvent, - DoBoatAttackEvent, - DoGroundAttackEvent, - InputHandler, - MouseMoveEvent, - MouseUpEvent, -} from "./InputHandler"; + ErrorUpdate, + GameUpdateType, + GameUpdateViewData, + HashUpdate, + WinUpdate, +} from "../core/game/GameUpdates"; +import { GameView, PlayerView } from "../core/game/GameView"; +import { loadTerrainMap, TerrainMapData } from "../core/game/TerrainMapLoader"; +import { UserSettings } from "../core/game/UserSettings"; import { ClientID, GameID, @@ -14,16 +23,20 @@ import { PlayerRecord, ServerMessage, } from "../core/Schemas"; +import { createGameRecord } from "../core/Util"; +import { WorkerClient } from "../core/worker/WorkerClient"; +import { createRenderer, GameRenderer } from "./graphics/GameRenderer"; import { - ErrorUpdate, - GameUpdateType, - GameUpdateViewData, - HashUpdate, - WinUpdate, -} from "../core/game/GameUpdates"; -import { GameRenderer, createRenderer } from "./graphics/GameRenderer"; -import { GameView, PlayerView } from "../core/game/GameView"; -import { PlayerActions, UnitType } from "../core/game/Game"; + AutoUpgradeEvent, + DoBoatAttackEvent, + DoGroundAttackEvent, + InputHandler, + MouseMoveEvent, + MouseUpEvent, +} from "./InputHandler"; +import { endGame, startGame, startTime } from "./LocalPersistantStats"; +import { getPersistentID } from "./Main"; +import { terrainMapFileLoader } from "./TerrainMapFileLoader"; import { SendAttackIntentEvent, SendBoatAttackIntentEvent, @@ -32,20 +45,7 @@ import { SendUpgradeStructureIntentEvent, Transport, } from "./Transport"; -import { TerrainMapData, loadTerrainMap } from "../core/game/TerrainMapLoader"; -import { endGame, startGame, startTime } from "./LocalPersistantStats"; -import { EventBus } from "../core/EventBus"; -import { GameMapLoader } from "../core/game/GameMapLoader"; -import { ServerConfig } from "../core/configuration/Config"; -import { TileRef } from "../core/game/GameMap"; -import { UserSettings } from "../core/game/UserSettings"; -import { WorkerClient } from "../core/worker/WorkerClient"; import { createCanvas } from "./Utils"; -import { createGameRecord } from "../core/Util"; -import { getConfig } from "../core/configuration/ConfigLoader"; -import { getPersistentID } from "./Main"; -import { terrainMapFileLoader } from "./TerrainMapFileLoader"; -import { translateText } from "../client/Utils"; export type LobbyConfig = { serverConfig: ServerConfig; @@ -213,10 +213,10 @@ export class ClientGameRunner { } const players: PlayerRecord[] = [ { - persistentID: getPersistentID(), - username: this.lobby.playerName, clientID: this.lobby.clientID, + persistentID: getPersistentID(), stats: update.allPlayersStats[this.lobby.clientID], + username: this.lobby.playerName, }, ]; @@ -312,8 +312,8 @@ export class ClientGameRunner { } while (turn.turnNumber - 1 > this.turnsSeen) { this.worker.sendTurn({ - turnNumber: this.turnsSeen, intents: [], + turnNumber: this.turnsSeen, }); this.turnsSeen++; } @@ -471,9 +471,9 @@ export class ClientGameRunner { ); upgradeUnits.push({ + distance, unitId: bu.canUpgrade, unitType: bu.type, - distance, }); } } diff --git a/src/client/Cosmetics.ts b/src/client/Cosmetics.ts index be185cee4b..2d001ff274 100644 --- a/src/client/Cosmetics.ts +++ b/src/client/Cosmetics.ts @@ -1,10 +1,10 @@ -import { Cosmetics, CosmeticsSchema, Pattern } from "../core/CosmeticSchemas"; +import { z } from "zod"; import { StripeCreateCheckoutSessionResponseSchema, UserMeResponse, } from "../core/ApiSchemas"; +import { Cosmetics, CosmeticsSchema, Pattern } from "../core/CosmeticSchemas"; import { getApiBase, getAuthHeader } from "./jwt"; -import { z } from "zod"; export async function patterns( userMe: UserMeResponse | null, @@ -38,16 +38,16 @@ export async function handlePurchase(priceId: string) { const response = await fetch( `${getApiBase()}/stripe/create-checkout-session`, { - method: "POST", - headers: { - "Content-Type": "application/json", - "authorization": getAuthHeader(), - }, body: JSON.stringify({ + cancelUrl: `${window.location.origin}#purchase-completed=false`, priceId, successUrl: `${window.location.origin}#purchase-completed=true`, - cancelUrl: `${window.location.origin}#purchase-completed=false`, }), + headers: { + authorization: getAuthHeader(), + "Content-Type": "application/json", + }, + method: "POST", }, ); diff --git a/src/client/DarkModeButton.ts b/src/client/DarkModeButton.ts index e3544f8ff8..e4a9200131 100644 --- a/src/client/DarkModeButton.ts +++ b/src/client/DarkModeButton.ts @@ -1,4 +1,4 @@ -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, state } from "lit/decorators.js"; import { UserSettings } from "../core/game/UserSettings"; diff --git a/src/client/FlagInput.ts b/src/client/FlagInput.ts index e5aeabc21b..009d4ae989 100644 --- a/src/client/FlagInput.ts +++ b/src/client/FlagInput.ts @@ -1,7 +1,7 @@ -import { LitElement, css, html } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, state } from "lit/decorators.js"; -import { FlagSchema } from "../core/Schemas"; import { renderPlayerFlag } from "../core/CustomFlag"; +import { FlagSchema } from "../core/Schemas"; const flagKey = "flag"; @@ -36,9 +36,9 @@ export class FlagInput extends LitElement { private dispatchFlagEvent() { this.dispatchEvent( new CustomEvent("flag-change", { - detail: { flag: this.flag }, bubbles: true, composed: true, + detail: { flag: this.flag }, }), ); } diff --git a/src/client/FlagInputModal.ts b/src/client/FlagInputModal.ts index c207383edf..3af32557d0 100644 --- a/src/client/FlagInputModal.ts +++ b/src/client/FlagInputModal.ts @@ -1,4 +1,4 @@ -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, query, state } from "lit/decorators.js"; import Countries from "./data/countries.json"; @@ -33,10 +33,13 @@ export class FlagInputModal extends LitElement {
- ${this.isModalOpen ? Countries.filter( - (country) => !country.restricted && this.includedInSearch(country), - ).map( - (country) => html` + ${ + this.isModalOpen + ? Countries.filter( + (country) => + !country.restricted && this.includedInSearch(country), + ).map( + (country) => html` `, - ) : html``} + ) + : html`` + }
`; @@ -82,9 +87,9 @@ export class FlagInputModal extends LitElement { localStorage.setItem("flag", flag); this.dispatchEvent( new CustomEvent("flag-change", { - detail: { flag }, bubbles: true, composed: true, + detail: { flag }, }), ); } diff --git a/src/client/GameStartingModal.ts b/src/client/GameStartingModal.ts index 9ae0ad2323..0ec37e28d7 100644 --- a/src/client/GameStartingModal.ts +++ b/src/client/GameStartingModal.ts @@ -1,4 +1,4 @@ -import { LitElement, css, html } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, state } from "lit/decorators.js"; import { translateText } from "./Utils"; diff --git a/src/client/GoogleAdElement.ts b/src/client/GoogleAdElement.ts index 2d22d33bcc..4424fa0429 100644 --- a/src/client/GoogleAdElement.ts +++ b/src/client/GoogleAdElement.ts @@ -1,4 +1,4 @@ -import { LitElement, css, html } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators.js"; declare global { diff --git a/src/client/HelpModal.ts b/src/client/HelpModal.ts index e7ec2d181b..7b9919d8c2 100644 --- a/src/client/HelpModal.ts +++ b/src/client/HelpModal.ts @@ -1,6 +1,6 @@ import "./components/Difficulties"; import "./components/Maps"; -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, query } from "lit/decorators.js"; import { getAltKey, getModifierKey, translateText } from "../client/Utils"; diff --git a/src/client/HostLobbyModal.ts b/src/client/HostLobbyModal.ts index b4d2b41795..3437ad3dfd 100644 --- a/src/client/HostLobbyModal.ts +++ b/src/client/HostLobbyModal.ts @@ -2,33 +2,33 @@ import "./components/Difficulties"; import "./components/Maps"; import "./components/baseComponents/Modal"; -import { - ClientInfo, - GameConfig, - GameInfo, - GameInfoSchema, - TeamCountConfig, -} from "../core/Schemas"; +import { html, LitElement } from "lit"; +import { customElement, query, state } from "lit/decorators.js"; +import randomMap from "../../resources/images/RandomMap.webp"; +import { translateText } from "../client/Utils"; +import { getServerConfigFromClient } from "../core/configuration/ConfigLoader"; import { Difficulty, Duos, GameMapType, GameMode, + mapCategories, Quads, Trios, UnitType, - mapCategories, } from "../core/game/Game"; -import { LitElement, html } from "lit"; -import { customElement, query, state } from "lit/decorators.js"; -import { DifficultyDescription } from "./components/Difficulties"; -import { JoinLobbyEvent } from "./Main"; import { UserSettings } from "../core/game/UserSettings"; +import { + ClientInfo, + GameConfig, + GameInfo, + GameInfoSchema, + TeamCountConfig, +} from "../core/Schemas"; import { generateID } from "../core/Util"; -import { getServerConfigFromClient } from "../core/configuration/ConfigLoader"; -import randomMap from "../../resources/images/RandomMap.webp"; +import { DifficultyDescription } from "./components/Difficulties"; +import { JoinLobbyEvent } from "./Main"; import { renderUnitTypeOptions } from "./utilities/RenderUnitTypeOptions"; -import { translateText } from "../client/Utils"; @customElement("host-lobby-modal") export class HostLobbyModal extends LitElement { @@ -206,8 +206,10 @@ export class HostLobbyModal extends LitElement { > html`
this.handleDifficultySelection(value)} > html`
this.handleTeamCountSelection(o)} >
- ${typeof o === "string" - ? translateText(`public_lobby.teams_${o}`) - : translateText("public_lobby.teams", { - num: o, - })} + ${ + typeof o === "string" + ? translateText(`public_lobby.teams_${o}`) + : translateText("public_lobby.teams", { + num: o, + }) + }
`, @@ -457,9 +461,9 @@ export class HostLobbyModal extends LitElement { style="display: flex; flex-wrap: wrap; justify-content: center; gap: 12px;" > ${renderUnitTypeOptions({ - disabledUnits: this.disabledUnits, - toggleUnit: this.toggleUnit.bind(this), - })} + disabledUnits: this.disabledUnits, + toggleUnit: this.toggleUnit.bind(this), + })}
@@ -482,11 +486,12 @@ export class HostLobbyModal extends LitElement { (client) => html` ${client.username} - ${client.clientID === this.lobbyCreatorClientID - ? html`(${translateText("host_modal.host_badge")})` - : html` + : html` - `} + ` + } `, )} @@ -538,12 +544,12 @@ export class HostLobbyModal extends LitElement { .then(() => { this.dispatchEvent( new CustomEvent("join-lobby", { + bubbles: true, + composed: true, detail: { - gameID: this.lobbyId, clientID: this.lobbyCreatorClientID, + gameID: this.lobbyId, } as JoinLobbyEvent, - bubbles: true, - composed: true, }), ); }); @@ -649,24 +655,24 @@ export class HostLobbyModal extends LitElement { const response = await fetch( `${window.location.origin}/${config.workerPath(this.lobbyId)}/api/game/${this.lobbyId}`, { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, body: JSON.stringify({ - gameMap: this.selectedMap, + bots: this.bots, difficulty: this.selectedDifficulty, + disabledUnits: this.disabledUnits, disableNPCs: this.disableNPCs, - bots: this.bots, - infiniteGold: this.infiniteGold, donateGold: this.donateGold, - infiniteTroops: this.infiniteTroops, donateTroops: this.donateTroops, - instantBuild: this.instantBuild, + gameMap: this.selectedMap, gameMode: this.gameMode, - disabledUnits: this.disabledUnits, + infiniteGold: this.infiniteGold, + infiniteTroops: this.infiniteTroops, + instantBuild: this.instantBuild, playerTeams: this.teamCount, } satisfies Partial), + headers: { + "Content-Type": "application/json", + }, + method: "PUT", }, ); return response; @@ -695,18 +701,18 @@ export class HostLobbyModal extends LitElement { await this.putGameConfig(); console.log( `Starting private game with map: ${ - GameMapType[this.selectedMap as keyof typeof GameMapType]} ${ - this.useRandomMap ? " (Randomly selected)" : ""}`, + GameMapType[this.selectedMap as keyof typeof GameMapType] + } ${this.useRandomMap ? " (Randomly selected)" : ""}`, ); this.close(); const config = await getServerConfigFromClient(); const response = await fetch( `${window.location.origin}/${config.workerPath(this.lobbyId)}/api/start_game/${this.lobbyId}`, { - method: "POST", headers: { "Content-Type": "application/json", }, + method: "POST", }, ); return response; @@ -730,10 +736,10 @@ export class HostLobbyModal extends LitElement { private async pollPlayers() { const config = await getServerConfigFromClient(); fetch(`/${config.workerPath(this.lobbyId)}/api/game/${this.lobbyId}`, { - method: "GET", headers: { "Content-Type": "application/json", }, + method: "GET", }) .then((response) => response.json()) .then(GameInfoSchema.parse) @@ -748,9 +754,9 @@ export class HostLobbyModal extends LitElement { // Dispatch event to be handled by WebSocket instead of HTTP this.dispatchEvent( new CustomEvent("kick-player", { - detail: { target: clientID }, bubbles: true, composed: true, + detail: { target: clientID }, }), ); } @@ -763,10 +769,10 @@ async function createLobby(creatorClientID: string): Promise { const response = await fetch( `/${config.workerPath(id)}/api/create_game/${id}?creatorClientID=${encodeURIComponent(creatorClientID)}`, { - method: "POST", headers: { "Content-Type": "application/json", }, + method: "POST", // body: JSON.stringify(data), // Include this if you need to send data }, ); diff --git a/src/client/InputHandler.ts b/src/client/InputHandler.ts index 8ff2db2f08..67b865669f 100644 --- a/src/client/InputHandler.ts +++ b/src/client/InputHandler.ts @@ -1,8 +1,8 @@ import { EventBus, GameEvent } from "../core/EventBus"; -import { ReplaySpeedMultiplier } from "./utilities/ReplaySpeedMultiplier"; import { UnitType } from "../core/game/Game"; import { UnitView } from "../core/game/GameView"; import { UserSettings } from "../core/game/UserSettings"; +import { ReplaySpeedMultiplier } from "./utilities/ReplaySpeedMultiplier"; export class MouseUpEvent implements GameEvent { constructor( @@ -145,20 +145,20 @@ export class InputHandler { initialize() { this.keybinds = { - toggleView: "Space", - centerCamera: "KeyC", - moveUp: "KeyW", - moveDown: "KeyS", - moveLeft: "KeyA", - moveRight: "KeyD", - zoomOut: "KeyQ", - zoomIn: "KeyE", + altKey: "AltLeft", attackRatioDown: "Digit1", attackRatioUp: "Digit2", boatAttack: "KeyB", + centerCamera: "KeyC", groundAttack: "KeyG", modifierKey: "ControlLeft", - altKey: "AltLeft", + moveDown: "KeyS", + moveLeft: "KeyA", + moveRight: "KeyD", + moveUp: "KeyW", + toggleView: "Space", + zoomIn: "KeyE", + zoomOut: "KeyQ", ...(JSON.parse(localStorage.getItem("settings.keybinds") ?? "{}") ?? {}), }; diff --git a/src/client/JoinPrivateLobbyModal.ts b/src/client/JoinPrivateLobbyModal.ts index ccedb0bd16..fdabfd47da 100644 --- a/src/client/JoinPrivateLobbyModal.ts +++ b/src/client/JoinPrivateLobbyModal.ts @@ -1,16 +1,16 @@ import "./components/baseComponents/Button"; import "./components/baseComponents/Modal"; +import { html, LitElement } from "lit"; +import { customElement, query, state } from "lit/decorators.js"; +import { translateText } from "../client/Utils"; +import { getServerConfigFromClient } from "../core/configuration/ConfigLoader"; import { GameInfo, GameInfoSchema } from "../core/Schemas"; -import { LitElement, html } from "lit"; +import { getClientID } from "../core/Util"; import { WorkerApiArchivedGameLobbySchema, WorkerApiGameIdExistsSchema, } from "../core/WorkerSchemas"; -import { customElement, query, state } from "lit/decorators.js"; import { JoinLobbyEvent } from "./Main"; -import { getClientID } from "../core/Util"; -import { getServerConfigFromClient } from "../core/configuration/ConfigLoader"; -import { translateText } from "../client/Utils"; @customElement("join-private-lobby-modal") export class JoinPrivateLobbyModal extends LitElement { @@ -82,13 +82,16 @@ export class JoinPrivateLobbyModal extends LitElement { ${this.message}
- ${this.hasJoined && this.players.length > 0 - ? html`
+ ${ + this.hasJoined && this.players.length > 0 + ? html`
${this.players.length} - ${this.players.length === 1 - ? translateText("private_lobby.player") - : translateText("private_lobby.players")} + ${ + this.players.length === 1 + ? translateText("private_lobby.player") + : translateText("private_lobby.players") + }
@@ -97,16 +100,19 @@ export class JoinPrivateLobbyModal extends LitElement { )}
` - : ""} + : "" + }
- ${!this.hasJoined - ? html` ` - : ""} + : "" + }
`; @@ -139,9 +145,9 @@ export class JoinPrivateLobbyModal extends LitElement { this.message = ""; this.dispatchEvent( new CustomEvent("leave-lobby", { - detail: { lobby: this.lobbyIdInput.value }, bubbles: true, composed: true, + detail: { lobby: this.lobbyIdInput.value }, }), ); } @@ -205,8 +211,8 @@ export class JoinPrivateLobbyModal extends LitElement { const url = `/${config.workerPath(lobbyId)}/api/game/${lobbyId}/exists`; const response = await fetch(url, { - method: "GET", headers: { "Content-Type": "application/json" }, + method: "GET", }); const json = await response.json(); @@ -218,12 +224,12 @@ export class JoinPrivateLobbyModal extends LitElement { this.dispatchEvent( new CustomEvent("join-lobby", { + bubbles: true, + composed: true, detail: { - gameID: lobbyId, clientID: getClientID(lobbyId), + gameID: lobbyId, } as JoinLobbyEvent, - bubbles: true, - composed: true, }), ); @@ -239,8 +245,8 @@ export class JoinPrivateLobbyModal extends LitElement { const archiveUrl = `/${config.workerPath(lobbyId)}/api/archived_game/${lobbyId}`; const archiveResponse = await fetch(archiveUrl, { - method: "GET", headers: { "Content-Type": "application/json" }, + method: "GET", }); const json = await archiveResponse.json(); @@ -262,13 +268,13 @@ export class JoinPrivateLobbyModal extends LitElement { if (archiveData.exists) { this.dispatchEvent( new CustomEvent("join-lobby", { + bubbles: true, + composed: true, detail: { + clientID: getClientID(lobbyId), gameID: lobbyId, gameRecord: archiveData.gameRecord, - clientID: getClientID(lobbyId), } as JoinLobbyEvent, - bubbles: true, - composed: true, }), ); @@ -285,10 +291,10 @@ export class JoinPrivateLobbyModal extends LitElement { fetch( `/${config.workerPath(this.lobbyIdInput.value)}/api/game/${this.lobbyIdInput.value}`, { - method: "GET", headers: { "Content-Type": "application/json", }, + method: "GET", }, ) .then((response) => response.json()) diff --git a/src/client/LangSelector.ts b/src/client/LangSelector.ts index 4454797173..49efb33b8a 100644 --- a/src/client/LangSelector.ts +++ b/src/client/LangSelector.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import "./LanguageModal"; -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, state } from "lit/decorators.js"; import ar from "../../resources/lang/ar.json"; @@ -51,32 +51,32 @@ export class LangSelector extends LitElement { ar, bg, bn, + cs, + da, de, en, - es, eo, + es, + fi, fr, - it, + gl, + he, hi, + it, ja, + ko, nl, pl, "pt-BR": pt_BR, ru, sh, - tr, + sk, + sl, + "sv-SE": sv_SE, tp, + tr, uk, - cs, - he, - da, - fi, - "sv-SE": sv_SE, "zh-CN": zh_CN, - ko, - gl, - sl, - sk, }; createRenderRoot() { @@ -148,8 +148,8 @@ export class LangSelector extends LitElement { list.push({ code: langData.lang_code ?? langCode, - native: langData.native ?? langCode, en: langData.en ?? langCode, + native: langData.native ?? langCode, svg: langData.svg ?? langCode, }); } @@ -159,8 +159,8 @@ export class LangSelector extends LitElement { if (this.debugKeyPressed) { debugLang = { code: "debug", - native: "Debug", en: "Debug", + native: "Debug", svg: "xx", }; this.debugMode = true; @@ -289,16 +289,16 @@ export class LangSelector extends LitElement { this.languageList.find((l) => l.code === this.currentLang) ?? (this.currentLang === "debug" ? { - code: "debug", - native: "Debug", - en: "Debug", - svg: "xx", - } + code: "debug", + en: "Debug", + native: "Debug", + svg: "xx", + } : { - native: "English", - en: "English", - svg: "uk_us_flag", - }); + en: "English", + native: "English", + svg: "uk_us_flag", + }); return html`
@@ -327,7 +327,7 @@ export class LangSelector extends LitElement { .languageList=${this.languageList} .currentLang=${this.currentLang} @language-selected=${(e: CustomEvent) => - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument this.changeLanguage(e.detail.lang)} @close-modal=${() => (this.showModal = false)} > @@ -348,7 +348,7 @@ function flattenTranslations( if (typeof value === "string") { result[fullKey] = value; } else if (value && typeof value === "object" && !Array.isArray(value)) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument flattenTranslations(value, fullKey, result); } else { console.warn("Unknown type", typeof value, value); diff --git a/src/client/LanguageModal.ts b/src/client/LanguageModal.ts index 4b5c5e34d7..98333fc013 100644 --- a/src/client/LanguageModal.ts +++ b/src/client/LanguageModal.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, property } from "lit/decorators.js"; import { translateText } from "../client/Utils"; @@ -54,9 +54,9 @@ export class LanguageModal extends LitElement { private readonly selectLanguage = (lang: string) => { this.dispatchEvent( new CustomEvent("language-selected", { - detail: { lang }, bubbles: true, composed: true, + detail: { lang }, }), ); }; @@ -111,7 +111,7 @@ export class LanguageModal extends LitElement {
` + }
@@ -149,17 +150,19 @@ export class PublicLobby extends LitElement {
- ${lobby.gameConfig.gameMode === GameMode.Team - ? typeof teamCount === "string" - ? translateText(`public_lobby.teams_${teamCount}`) - : translateText("public_lobby.teams", { - num: teamCount ?? 0, - }) - : translateText("game_mode.ffa")} ${translateText( @@ -203,20 +206,20 @@ export class PublicLobby extends LitElement { this.currLobby = lobby; this.dispatchEvent( new CustomEvent("join-lobby", { + bubbles: true, + composed: true, detail: { - gameID: lobby.gameID, clientID: getClientID(lobby.gameID), + gameID: lobby.gameID, } as JoinLobbyEvent, - bubbles: true, - composed: true, }), ); } else { this.dispatchEvent( new CustomEvent("leave-lobby", { - detail: { lobby: this.currLobby }, bubbles: true, composed: true, + detail: { lobby: this.currLobby }, }), ); this.leaveLobby(); diff --git a/src/client/SinglePlayerModal.ts b/src/client/SinglePlayerModal.ts index 577a9257f5..405a95afd1 100644 --- a/src/client/SinglePlayerModal.ts +++ b/src/client/SinglePlayerModal.ts @@ -2,29 +2,29 @@ import "./components/Difficulties"; import "./components/Maps"; import "./components/baseComponents/Button"; import "./components/baseComponents/Modal"; +import { html, LitElement } from "lit"; +import { customElement, query, state } from "lit/decorators.js"; +import randomMap from "../../resources/images/RandomMap.webp"; +import { translateText } from "../client/Utils"; import { Difficulty, Duos, GameMapType, GameMode, GameType, + mapCategories, Quads, Trios, UnitType, - mapCategories, } from "../core/game/Game"; -import { LitElement, html } from "lit"; -import { customElement, query, state } from "lit/decorators.js"; +import { UserSettings } from "../core/game/UserSettings"; +import { TeamCountConfig } from "../core/Schemas"; import { generateID, getClientID } from "../core/Util"; import { DifficultyDescription } from "./components/Difficulties"; import { FlagInput } from "./FlagInput"; import { JoinLobbyEvent } from "./Main"; -import { TeamCountConfig } from "../core/Schemas"; -import { UserSettings } from "../core/game/UserSettings"; import { UsernameInput } from "./UsernameInput"; -import randomMap from "../../resources/images/RandomMap.webp"; import { renderUnitTypeOptions } from "./utilities/RenderUnitTypeOptions"; -import { translateText } from "../client/Utils"; @customElement("single-player-modal") export class SinglePlayerModal extends LitElement { @@ -96,8 +96,10 @@ export class SinglePlayerModal extends LitElement { >
@@ -140,9 +142,9 @@ export class SinglePlayerModal extends LitElement { .map( ([key, value]) => html`
this.handleDifficultySelection(value)} > ${translateText("host_modal.mode")}
this.handleGameModeSelection(GameMode.FFA)} >
@@ -174,9 +176,9 @@ export class SinglePlayerModal extends LitElement {
this.handleGameModeSelection(GameMode.Team)} >
@@ -186,9 +188,10 @@ export class SinglePlayerModal extends LitElement {
- ${this.gameMode === GameMode.FFA - ? "" - : html` + ${ + this.gameMode === GameMode.FFA + ? "" + : html`
@@ -198,22 +201,27 @@ export class SinglePlayerModal extends LitElement { ${[2, 3, 4, 5, 6, 7, Quads, Trios, Duos].map( (o) => html`
this.handleTeamCountSelection(o)} >
- ${typeof o === "string" - ? translateText(`public_lobby.teams_${o}`) - : translateText("public_lobby.teams", { num: o })} + ${ + typeof o === "string" + ? translateText(`public_lobby.teams_${o}`) + : translateText("public_lobby.teams", { + num: o, + }) + }
`, )}
- `} + ` + }
@@ -233,10 +241,11 @@ export class SinglePlayerModal extends LitElement { .value="${String(this.bots)}" />
- ${translateText("single_modal.bots")}${this - .bots === 0 - ? translateText("single_modal.bots_disabled") - : this.bots} + ${translateText("single_modal.bots")}${ + this.bots === 0 + ? translateText("single_modal.bots_disabled") + : this.bots + }
@@ -410,7 +419,8 @@ export class SinglePlayerModal extends LitElement { } console.log( - `Starting single player game with map: ${GameMapType[this.selectedMap as keyof typeof GameMapType] + `Starting single player game with map: ${ + GameMapType[this.selectedMap as keyof typeof GameMapType] }${this.useRandomMap ? " (Randomly selected)" : ""}`, ); const gameID = generateID(); @@ -429,43 +439,43 @@ export class SinglePlayerModal extends LitElement { } this.dispatchEvent( new CustomEvent("join-lobby", { + bubbles: true, + composed: true, detail: { clientID, gameID, gameStartInfo: { + config: { + bots: this.bots, + difficulty: this.selectedDifficulty, + disabledUnits: this.disabledUnits + .map((u) => Object.values(UnitType).find((ut) => ut === u)) + .filter((ut): ut is UnitType => ut !== undefined), + disableNPCs: this.disableNPCs, + donateGold: this.donateGold, + donateTroops: this.donateTroops, + gameMap: this.selectedMap, + gameMode: this.gameMode, + gameType: GameType.Singleplayer, + infiniteGold: this.infiniteGold, + infiniteTroops: this.infiniteTroops, + instantBuild: this.instantBuild, + playerTeams: this.teamCount, + }, gameID, players: [ { clientID, - username: usernameInput.getCurrentUsername(), flag: flagInput.getCurrentFlag() === "xx" ? "" : flagInput.getCurrentFlag(), pattern: this.userSettings.getSelectedPattern(), + username: usernameInput.getCurrentUsername(), }, ], - config: { - gameMap: this.selectedMap, - gameType: GameType.Singleplayer, - gameMode: this.gameMode, - playerTeams: this.teamCount, - difficulty: this.selectedDifficulty, - disableNPCs: this.disableNPCs, - bots: this.bots, - infiniteGold: this.infiniteGold, - donateGold: this.donateGold, - infiniteTroops: this.infiniteTroops, - donateTroops: this.donateTroops, - instantBuild: this.instantBuild, - disabledUnits: this.disabledUnits - .map((u) => Object.values(UnitType).find((ut) => ut === u)) - .filter((ut): ut is UnitType => ut !== undefined), - }, }, } satisfies JoinLobbyEvent, - bubbles: true, - composed: true, }), ); this.close(); diff --git a/src/client/TerrainMapFileLoader.ts b/src/client/TerrainMapFileLoader.ts index 40be9b4dbf..b3c335dd61 100644 --- a/src/client/TerrainMapFileLoader.ts +++ b/src/client/TerrainMapFileLoader.ts @@ -1,4 +1,4 @@ -import { FetchGameMapLoader } from "../core/game/FetchGameMapLoader"; import version from "../../resources/version.txt"; +import { FetchGameMapLoader } from "../core/game/FetchGameMapLoader"; export const terrainMapFileLoader = new FetchGameMapLoader("/maps", version); diff --git a/src/client/TerritoryPatternsModal.ts b/src/client/TerritoryPatternsModal.ts index 7a1f086ce7..483b89b28f 100644 --- a/src/client/TerritoryPatternsModal.ts +++ b/src/client/TerritoryPatternsModal.ts @@ -1,14 +1,14 @@ import "./components/Difficulties"; import "./components/Maps"; -import { LitElement, html, render } from "lit"; -import { customElement, query, state } from "lit/decorators.js"; -import { handlePurchase, patterns } from "./Cosmetics"; -import { Pattern } from "../core/CosmeticSchemas"; -import { PatternDecoder } from "../core/PatternDecoder"; +import { base64url } from "jose"; import type { TemplateResult } from "lit"; +import { html, LitElement, render } from "lit"; +import { customElement, query, state } from "lit/decorators.js"; import { UserMeResponse } from "../core/ApiSchemas"; +import { Pattern } from "../core/CosmeticSchemas"; import { UserSettings } from "../core/game/UserSettings"; -import { base64url } from "jose"; +import { PatternDecoder } from "../core/PatternDecoder"; +import { handlePurchase, patterns } from "./Cosmetics"; import { translateText } from "./Utils"; @customElement("territory-patterns-modal") @@ -116,8 +116,9 @@ export class TerritoryPatternsModal extends LitElement { return html`
${translateText("territory_patterns.blocked.purchase")}
@@ -133,9 +134,11 @@ export class TerritoryPatternsModal extends LitElement {
- ${pattern.product !== null - ? html` + ${ + pattern.product !== null + ? html`
`; } @@ -202,9 +207,11 @@ export class TerritoryPatternsModal extends LitElement { >
diff --git a/src/client/Transport.ts b/src/client/Transport.ts index e98a7de285..3c0b60118d 100644 --- a/src/client/Transport.ts +++ b/src/client/Transport.ts @@ -1,3 +1,5 @@ +import { z } from "zod"; +import { EventBus, GameEvent } from "../core/EventBus"; import { AllPlayers, GameType, @@ -7,6 +9,8 @@ import { Tick, UnitType, } from "../core/game/Game"; +import { TileRef } from "../core/game/GameMap"; +import { PlayerView } from "../core/game/GameView"; import { AllPlayersStats, ClientHashMessage, @@ -20,13 +24,9 @@ import { ServerMessageSchema, Winner, } from "../core/Schemas"; -import { EventBus, GameEvent } from "../core/EventBus"; +import { replacer } from "../core/Util"; import { LobbyConfig } from "./ClientGameRunner"; import { LocalServer } from "./LocalServer"; -import { PlayerView } from "../core/game/GameView"; -import { TileRef } from "../core/game/GameMap"; -import { replacer } from "../core/Util"; -import { z } from "zod"; export class PauseGameEvent implements GameEvent { constructor(public readonly paused: boolean) {} @@ -375,14 +375,14 @@ export class Transport { joinGame(numTurns: number) { this.sendMsg({ - type: "join", - gameID: this.lobbyConfig.gameID, clientID: this.lobbyConfig.clientID, + flag: this.lobbyConfig.flag, + gameID: this.lobbyConfig.gameID, lastTurn: numTurns, + pattern: this.lobbyConfig.pattern, token: this.lobbyConfig.token, + type: "join", username: this.lobbyConfig.playerName, - flag: this.lobbyConfig.flag, - pattern: this.lobbyConfig.pattern, } satisfies ClientJoinMessage); } @@ -408,26 +408,26 @@ export class Transport { private onSendAllianceRequest(event: SendAllianceRequestIntentEvent) { this.sendIntent({ - type: "allianceRequest", clientID: this.lobbyConfig.clientID, recipient: event.recipient.id(), + type: "allianceRequest", }); } private onAllianceRequestReplyUIEvent(event: SendAllianceReplyIntentEvent) { this.sendIntent({ - type: "allianceRequestReply", + accept: event.accepted, clientID: this.lobbyConfig.clientID, requestor: event.requestor.id(), - accept: event.accepted, + type: "allianceRequestReply", }); } private onBreakAllianceRequestUIEvent(event: SendBreakAllianceIntentEvent) { this.sendIntent({ - type: "breakAlliance", clientID: this.lobbyConfig.clientID, recipient: event.recipient.id(), + type: "breakAlliance", }); } @@ -435,114 +435,114 @@ export class Transport { event: SendAllianceExtensionIntentEvent, ) { this.sendIntent({ - type: "allianceExtension", clientID: this.lobbyConfig.clientID, recipient: event.recipient.id(), + type: "allianceExtension", }); } private onSendSpawnIntentEvent(event: SendSpawnIntentEvent) { this.sendIntent({ - type: "spawn", clientID: this.lobbyConfig.clientID, flag: this.lobbyConfig.flag, - pattern: this.lobbyConfig.pattern, name: this.lobbyConfig.playerName, + pattern: this.lobbyConfig.pattern, playerType: PlayerType.Human, tile: event.tile, + type: "spawn", }); } private onSendAttackIntent(event: SendAttackIntentEvent) { this.sendIntent({ - type: "attack", clientID: this.lobbyConfig.clientID, targetID: event.targetID, troops: event.troops, + type: "attack", }); } private onSendBoatAttackIntent(event: SendBoatAttackIntentEvent) { this.sendIntent({ - type: "boat", clientID: this.lobbyConfig.clientID, - targetID: event.targetID, - troops: event.troops, dst: event.dst, src: event.src, + targetID: event.targetID, + troops: event.troops, + type: "boat", }); } private onSendUpgradeStructureIntent(event: SendUpgradeStructureIntentEvent) { this.sendIntent({ + clientID: this.lobbyConfig.clientID, type: "upgrade_structure", unit: event.unitType, - clientID: this.lobbyConfig.clientID, unitId: event.unitId, }); } private onSendTargetPlayerIntent(event: SendTargetPlayerIntentEvent) { this.sendIntent({ - type: "targetPlayer", clientID: this.lobbyConfig.clientID, target: event.targetID, + type: "targetPlayer", }); } private onSendEmojiIntent(event: SendEmojiIntentEvent) { this.sendIntent({ - type: "emoji", clientID: this.lobbyConfig.clientID, + emoji: event.emoji, recipient: event.recipient === AllPlayers ? AllPlayers : event.recipient.id(), - emoji: event.emoji, + type: "emoji", }); } private onSendDonateGoldIntent(event: SendDonateGoldIntentEvent) { this.sendIntent({ - type: "donate_gold", clientID: this.lobbyConfig.clientID, - recipient: event.recipient.id(), gold: event.gold, + recipient: event.recipient.id(), + type: "donate_gold", }); } private onSendDonateTroopIntent(event: SendDonateTroopsIntentEvent) { this.sendIntent({ - type: "donate_troops", clientID: this.lobbyConfig.clientID, recipient: event.recipient.id(), troops: event.troops, + type: "donate_troops", }); } private onSendQuickChatIntent(event: SendQuickChatEvent) { this.sendIntent({ - type: "quick_chat", clientID: this.lobbyConfig.clientID, - recipient: event.recipient.id(), quickChatKey: event.quickChatKey, + recipient: event.recipient.id(), target: event.target, + type: "quick_chat", }); } private onSendEmbargoIntent(event: SendEmbargoIntentEvent) { this.sendIntent({ - type: "embargo", + action: event.action, clientID: this.lobbyConfig.clientID, targetID: event.target.id(), - action: event.action, + type: "embargo", }); } private onBuildUnitIntent(event: BuildUnitIntentEvent) { this.sendIntent({ - type: "build_unit", clientID: this.lobbyConfig.clientID, - unit: event.unit, tile: event.tile, + type: "build_unit", + unit: event.unit, }); } @@ -561,9 +561,9 @@ export class Transport { private onSendWinnerEvent(event: SendWinnerEvent) { if (this.isLocal || this.socket?.readyState === WebSocket.OPEN) { this.sendMsg({ + allPlayersStats: event.allPlayersStats, type: "winner", winner: event.winner, - allPlayersStats: event.allPlayersStats, } satisfies ClientSendWinnerMessage); } else { console.log( @@ -577,9 +577,9 @@ export class Transport { private onSendHashEvent(event: SendHashEvent) { if (this.isLocal || this.socket?.readyState === WebSocket.OPEN) { this.sendMsg({ - type: "hash", - turnNumber: event.tick, hash: event.hash, + turnNumber: event.tick, + type: "hash", } satisfies ClientHashMessage); } else { console.log( @@ -592,50 +592,50 @@ export class Transport { private onCancelAttackIntentEvent(event: CancelAttackIntentEvent) { this.sendIntent({ - type: "cancel_attack", - clientID: this.lobbyConfig.clientID, attackID: event.attackID, + clientID: this.lobbyConfig.clientID, + type: "cancel_attack", }); } private onCancelBoatIntentEvent(event: CancelBoatIntentEvent) { this.sendIntent({ - type: "cancel_boat", clientID: this.lobbyConfig.clientID, + type: "cancel_boat", unitID: event.unitID, }); } private onMoveWarshipEvent(event: MoveWarshipIntentEvent) { this.sendIntent({ - type: "move_warship", clientID: this.lobbyConfig.clientID, - unitId: event.unitId, tile: event.tile, + type: "move_warship", + unitId: event.unitId, }); } private onSendDeleteUnitIntent(event: SendDeleteUnitIntentEvent) { this.sendIntent({ - type: "delete_unit", clientID: this.lobbyConfig.clientID, + type: "delete_unit", unitId: event.unitId, }); } private onSendKickPlayerIntent(event: SendKickPlayerIntentEvent) { this.sendIntent({ - type: "kick_player", clientID: this.lobbyConfig.clientID, target: event.target, + type: "kick_player", }); } private sendIntent(intent: Intent) { if (this.isLocal || this.socket?.readyState === WebSocket.OPEN) { const msg = { - type: "intent", intent, + type: "intent", } satisfies ClientIntentMessage; this.sendMsg(msg); } else { diff --git a/src/client/UserSettingModal.ts b/src/client/UserSettingModal.ts index 2e46a7300d..2f84499058 100644 --- a/src/client/UserSettingModal.ts +++ b/src/client/UserSettingModal.ts @@ -2,12 +2,12 @@ import "./components/baseComponents/setting/SettingKeybind"; import "./components/baseComponents/setting/SettingNumber"; import "./components/baseComponents/setting/SettingSlider"; import "./components/baseComponents/setting/SettingToggle"; -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, query, state } from "lit/decorators.js"; -import { SettingKeybind } from "./components/baseComponents/setting/SettingKeybind"; -import { UserSettings } from "../core/game/UserSettings"; -import { translateText } from "../client/Utils"; import { z } from "zod"; +import { translateText } from "../client/Utils"; +import { UserSettings } from "../core/game/UserSettings"; +import { SettingKeybind } from "./components/baseComponents/setting/SettingKeybind"; const KeybindSchema = z.record(z.string(), z.string()); @@ -100,9 +100,9 @@ export class UserSettingModal extends LitElement { this.dispatchEvent( new CustomEvent("dark-mode-changed", { - detail: { darkMode: enabled }, bubbles: true, composed: true, + detail: { darkMode: enabled }, }), ); @@ -233,18 +233,22 @@ export class UserSettingModal extends LitElement {
- ${this.settingsMode === "basic" - ? this.renderBasicSettings() - : this.renderKeybindSettings()} + ${ + this.settingsMode === "basic" + ? this.renderBasicSettings() + : this.renderKeybindSettings() + }
@@ -361,13 +367,15 @@ export class UserSettingModal extends LitElement { description="${translateText("user_setting.attack_ratio_desc")}" min="1" max="100" - .value=${Number(localStorage.getItem("settings.attackRatio") ?? "0.2") * - 100} + .value=${ + Number(localStorage.getItem("settings.attackRatio") ?? "0.2") * 100 + } @change=${this.sliderAttackRatio} > - ${this.showEasterEggSettings - ? html` + ${ + this.showEasterEggSettings + ? html` ` - : null} + : null + } `; } diff --git a/src/client/UsernameInput.ts b/src/client/UsernameInput.ts index 6b72ac4487..7f3127247c 100644 --- a/src/client/UsernameInput.ts +++ b/src/client/UsernameInput.ts @@ -1,12 +1,12 @@ -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; +import { customElement, property, state } from "lit/decorators.js"; +import { v4 as uuidv4 } from "uuid"; +import { translateText } from "../client/Utils"; +import { UserSettings } from "../core/game/UserSettings"; import { MAX_USERNAME_LENGTH, validateUsername, } from "../core/validations/username"; -import { customElement, property, state } from "lit/decorators.js"; -import { UserSettings } from "../core/game/UserSettings"; -import { translateText } from "../client/Utils"; -import { v4 as uuidv4 } from "uuid"; const usernameKey = "username"; @@ -48,8 +48,9 @@ export class UsernameInput extends LitElement { focus:ring-blue-500 focus:border-blue-500 dark:border-gray-300/60 dark:bg-gray-700 dark:text-white" /> - ${this.validationError - ? html`
= { + blue: "text-blue-400", fail: "text-red-400", - warn: "text-yellow-400", - success: "text-green-400", info: "text-gray-200", - blue: "text-blue-400", + success: "text-green-400", + warn: "text-yellow-400", white: "text-white", }; diff --git a/src/client/components/Difficulties.ts b/src/client/components/Difficulties.ts index d4c1640b5a..58aa9e21f7 100644 --- a/src/client/components/Difficulties.ts +++ b/src/client/components/Difficulties.ts @@ -1,4 +1,4 @@ -import { LitElement, css, html } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators.js"; export enum DifficultyDescription { diff --git a/src/client/components/Maps.ts b/src/client/components/Maps.ts index 77a0bab883..76f4d3501c 100644 --- a/src/client/components/Maps.ts +++ b/src/client/components/Maps.ts @@ -1,4 +1,4 @@ -import { LitElement, css, html } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators.js"; import { GameMapType } from "../../core/game/Game"; import { terrainMapFileLoader } from "../TerrainMapFileLoader"; @@ -6,35 +6,35 @@ import { translateText } from "../Utils"; // Add map descriptions export const MapDescription: Record = { - World: "World", - GiantWorldMap: "Giant World Map", - Europe: "Europe", - EuropeClassic: "Europe Classic", - Mena: "MENA", - NorthAmerica: "North America", - Oceania: "Oceania", - BlackSea: "Black Sea", Africa: "Africa", - Pangaea: "Pangaea", Asia: "Asia", - Mars: "Mars", - MarsRevised: "Mars Revised", - SouthAmerica: "South America", - Britannia: "Britannia", - GatewayToTheAtlantic: "Gateway to the Atlantic", Australia: "Australia", - Iceland: "Iceland", - EastAsia: "East Asia", + Baikal: "Baikal", BetweenTwoSeas: "Between Two Seas", - FaroeIslands: "Faroe Islands", + BlackSea: "Black Sea", + Britannia: "Britannia", DeglaciatedAntarctica: "Deglaciated Antarctica", + EastAsia: "East Asia", + Europe: "Europe", + EuropeClassic: "Europe Classic", FalklandIslands: "Falkland Islands", - Baikal: "Baikal", + FaroeIslands: "Faroe Islands", + GatewayToTheAtlantic: "Gateway to the Atlantic", + GiantWorldMap: "Giant World Map", Halkidiki: "Halkidiki", - StraitOfGibraltar: "Strait of Gibraltar", + Iceland: "Iceland", Italia: "Italia", - Yenisei: "Yenisei", + Mars: "Mars", + MarsRevised: "Mars Revised", + Mena: "MENA", + NorthAmerica: "North America", + Oceania: "Oceania", + Pangaea: "Pangaea", Pluto: "Pluto", + SouthAmerica: "South America", + StraitOfGibraltar: "Strait of Gibraltar", + World: "World", + Yenisei: "Yenisei", }; @customElement("map-display") @@ -119,17 +119,19 @@ export class MapDisplay extends LitElement { render() { return html`
- ${this.isLoading - ? html`
+ ${ + this.isLoading + ? html`
${translateText("map_component.loading")}
` - : this.mapWebpPath - ? html`${this.mapKey}` - : html`
Error
`} + : html`
Error
` + }
${this.translation || this.mapName}
`; diff --git a/src/client/components/ModalOverlay.ts b/src/client/components/ModalOverlay.ts index 772a7399b1..5c12cfba13 100644 --- a/src/client/components/ModalOverlay.ts +++ b/src/client/components/ModalOverlay.ts @@ -1,4 +1,4 @@ -import { LitElement, css, html } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; @customElement("modal-overlay") diff --git a/src/client/components/NewsButton.ts b/src/client/components/NewsButton.ts index 860340226d..6185f67082 100644 --- a/src/client/components/NewsButton.ts +++ b/src/client/components/NewsButton.ts @@ -1,9 +1,9 @@ -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators.js"; -import { NewsModal } from "../NewsModal"; import megaphone from "../../../resources/images/Megaphone.svg"; -import { translateText } from "../Utils"; import version from "../../../resources/version.txt"; +import { NewsModal } from "../NewsModal"; +import { translateText } from "../Utils"; @customElement("news-button") export class NewsButton extends LitElement { @@ -38,10 +38,9 @@ export class NewsButton extends LitElement { render() { return html`
`; } diff --git a/src/client/components/baseComponents/Modal.ts b/src/client/components/baseComponents/Modal.ts index ae58d9078c..8680f001da 100644 --- a/src/client/components/baseComponents/Modal.ts +++ b/src/client/components/baseComponents/Modal.ts @@ -1,4 +1,4 @@ -import { LitElement, css, html } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators.js"; import { translateText } from "../../Utils"; @@ -83,18 +83,21 @@ export class OModal extends LitElement { render() { return html` - ${this.isModalOpen - ? html` + ${ + this.isModalOpen + ? html` ` - : html``} + : html`` + } `; } } diff --git a/src/client/components/baseComponents/setting/SettingKeybind.ts b/src/client/components/baseComponents/setting/SettingKeybind.ts index 2be0d500bd..d7cc9809fd 100644 --- a/src/client/components/baseComponents/setting/SettingKeybind.ts +++ b/src/client/components/baseComponents/setting/SettingKeybind.ts @@ -1,4 +1,4 @@ -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, property } from "lit/decorators.js"; import { translateText } from "../../../../client/Utils"; @@ -6,7 +6,7 @@ import { translateText } from "../../../../client/Utils"; export class SettingKeybind extends LitElement { @property() label = "Setting"; @property() description = ""; - @property({ type: String, reflect: true }) action = ""; + @property({ reflect: true, type: String }) action = ""; @property({ type: String }) defaultKey = ""; @property({ type: String }) value = ""; @property({ type: Boolean }) easter = false; @@ -80,9 +80,9 @@ export class SettingKeybind extends LitElement { this.dispatchEvent( new CustomEvent("change", { - detail: { action: this.action, value: code }, bubbles: true, composed: true, + detail: { action: this.action, value: code }, }), ); @@ -94,9 +94,9 @@ export class SettingKeybind extends LitElement { this.value = this.defaultKey; this.dispatchEvent( new CustomEvent("change", { - detail: { action: this.action, value: this.defaultKey }, bubbles: true, composed: true, + detail: { action: this.action, value: this.defaultKey }, }), ); } @@ -105,9 +105,9 @@ export class SettingKeybind extends LitElement { this.value = ""; this.dispatchEvent( new CustomEvent("change", { - detail: { action: this.action, value: "Null" }, bubbles: true, composed: true, + detail: { action: this.action, value: "Null" }, }), ); this.requestUpdate(); diff --git a/src/client/components/baseComponents/setting/SettingNumber.ts b/src/client/components/baseComponents/setting/SettingNumber.ts index 8b7f807703..c9ecdae2b3 100644 --- a/src/client/components/baseComponents/setting/SettingNumber.ts +++ b/src/client/components/baseComponents/setting/SettingNumber.ts @@ -1,4 +1,4 @@ -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, property } from "lit/decorators.js"; @customElement("setting-number") @@ -21,9 +21,9 @@ export class SettingNumber extends LitElement { this.dispatchEvent( new CustomEvent("change", { - detail: { value: newValue }, bubbles: true, composed: true, + detail: { value: newValue }, }), ); } diff --git a/src/client/components/baseComponents/setting/SettingSlider.ts b/src/client/components/baseComponents/setting/SettingSlider.ts index 67bd562dda..4108506e68 100644 --- a/src/client/components/baseComponents/setting/SettingSlider.ts +++ b/src/client/components/baseComponents/setting/SettingSlider.ts @@ -1,4 +1,4 @@ -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, property } from "lit/decorators.js"; @customElement("setting-slider") @@ -21,9 +21,9 @@ export class SettingSlider extends LitElement { this.dispatchEvent( new CustomEvent("change", { - detail: { value: this.value }, bubbles: true, composed: true, + detail: { value: this.value }, }), ); } diff --git a/src/client/components/baseComponents/setting/SettingToggle.ts b/src/client/components/baseComponents/setting/SettingToggle.ts index 18f1dfe71c..b1b7c5bdc3 100644 --- a/src/client/components/baseComponents/setting/SettingToggle.ts +++ b/src/client/components/baseComponents/setting/SettingToggle.ts @@ -1,4 +1,4 @@ -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, property } from "lit/decorators.js"; @customElement("setting-toggle") @@ -6,7 +6,7 @@ export class SettingToggle extends LitElement { @property() label = "Setting"; @property() description = ""; @property() id = ""; - @property({ type: Boolean, reflect: true }) checked = false; + @property({ reflect: true, type: Boolean }) checked = false; @property({ type: Boolean }) easter = false; createRenderRoot() { @@ -18,9 +18,9 @@ export class SettingToggle extends LitElement { this.checked = input.checked; this.dispatchEvent( new CustomEvent("change", { - detail: { checked: this.checked }, bubbles: true, composed: true, + detail: { checked: this.checked }, }), ); } diff --git a/src/client/graphics/AnimatedSpriteLoader.ts b/src/client/graphics/AnimatedSpriteLoader.ts index 2c82ee27eb..51e691d5e1 100644 --- a/src/client/graphics/AnimatedSpriteLoader.ts +++ b/src/client/graphics/AnimatedSpriteLoader.ts @@ -1,19 +1,19 @@ -import { AnimatedSprite } from "./AnimatedSprite"; -import { FxType } from "./fx/Fx"; -import { PlayerView } from "../../core/game/GameView"; -import SAMExplosion from "../../../resources/sprites/samExplosion.png"; -import { Theme } from "../../core/configuration/Config"; -import { colorizeCanvas } from "./SpriteLoader"; +import miniBigSmoke from "../../../resources/sprites/bigsmoke.png"; import conquestSword from "../../../resources/sprites/conquestSword.png"; import dust from "../../../resources/sprites/dust.png"; -import miniBigSmoke from "../../../resources/sprites/bigsmoke.png"; import miniExplosion from "../../../resources/sprites/miniExplosion.png"; import miniFire from "../../../resources/sprites/minifire.png"; -import miniSmoke from "../../../resources/sprites/smoke.png"; -import miniSmokeAndFire from "../../../resources/sprites/smokeAndFire.png"; import nuke from "../../../resources/sprites/nukeExplosion.png"; +import SAMExplosion from "../../../resources/sprites/samExplosion.png"; import sinkingShip from "../../../resources/sprites/sinkingShip.png"; +import miniSmoke from "../../../resources/sprites/smoke.png"; +import miniSmokeAndFire from "../../../resources/sprites/smokeAndFire.png"; import unitExplosion from "../../../resources/sprites/unitExplosion.png"; +import { Theme } from "../../core/configuration/Config"; +import { PlayerView } from "../../core/game/GameView"; +import { AnimatedSprite } from "./AnimatedSprite"; +import { FxType } from "./fx/Fx"; +import { colorizeCanvas } from "./SpriteLoader"; type AnimatedSpriteConfig = { url: string; @@ -27,108 +27,109 @@ type AnimatedSpriteConfig = { const ANIMATED_SPRITE_CONFIG: Partial> = { [FxType.MiniFire]: { - url: miniFire, - frameWidth: 7, frameCount: 6, frameDuration: 100, + frameWidth: 7, looping: true, originX: 3, originY: 11, + url: miniFire, }, [FxType.MiniSmoke]: { - url: miniSmoke, - frameWidth: 11, frameCount: 4, frameDuration: 120, + frameWidth: 11, looping: true, originX: 2, originY: 10, + url: miniSmoke, }, [FxType.MiniBigSmoke]: { - url: miniBigSmoke, - frameWidth: 24, frameCount: 5, frameDuration: 120, + frameWidth: 24, looping: true, originX: 9, originY: 14, + url: miniBigSmoke, }, [FxType.MiniSmokeAndFire]: { - url: miniSmokeAndFire, - frameWidth: 24, frameCount: 5, frameDuration: 120, + frameWidth: 24, looping: true, originX: 9, originY: 14, + url: miniSmokeAndFire, }, [FxType.MiniExplosion]: { - url: miniExplosion, - frameWidth: 13, frameCount: 4, frameDuration: 70, + frameWidth: 13, looping: false, originX: 6, originY: 6, + url: miniExplosion, }, [FxType.Dust]: { - url: dust, - frameWidth: 9, frameCount: 3, frameDuration: 100, + frameWidth: 9, looping: false, originX: 4, originY: 5, + url: dust, }, [FxType.UnitExplosion]: { - url: unitExplosion, - frameWidth: 19, frameCount: 4, frameDuration: 70, + frameWidth: 19, looping: false, originX: 9, originY: 9, + url: unitExplosion, }, [FxType.SinkingShip]: { - url: sinkingShip, - frameWidth: 16, frameCount: 14, frameDuration: 90, + frameWidth: 16, looping: false, originX: 7, originY: 7, + url: sinkingShip, }, [FxType.Nuke]: { - url: nuke, - frameWidth: 60, frameCount: 9, frameDuration: 70, + frameWidth: 60, looping: false, originX: 30, originY: 30, + url: nuke, }, [FxType.SAMExplosion]: { - url: SAMExplosion, - frameWidth: 48, frameCount: 9, frameDuration: 70, + frameWidth: 48, looping: false, originX: 23, originY: 19, + url: SAMExplosion, }, [FxType.Conquest]: { - url: conquestSword, - frameWidth: 21, frameCount: 10, frameDuration: 90, + frameWidth: 21, looping: false, originX: 10, originY: 16, + url: conquestSword, }, }; export class AnimatedSpriteLoader { - private readonly animatedSpriteImageMap: Map = new Map(); + private readonly animatedSpriteImageMap: Map = + new Map(); // Do not color the same sprite twice private readonly coloredAnimatedSpriteCache: Map = new Map(); diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index 327495154f..145bd74358 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -1,17 +1,19 @@ +import { EventBus } from "../../core/EventBus"; +import { GameView } from "../../core/game/GameView"; +import { UserSettings } from "../../core/game/UserSettings"; +import { GameStartingModal } from "../GameStartingModal"; +import { RedrawGraphicsEvent } from "../InputHandler"; import { AlertFrame } from "./layers/AlertFrame"; import { BuildMenu } from "./layers/BuildMenu"; import { ChatDisplay } from "./layers/ChatDisplay"; import { ChatModal } from "./layers/ChatModal"; import { ControlPanel } from "./layers/ControlPanel"; import { EmojiTable } from "./layers/EmojiTable"; -import { EventBus } from "../../core/EventBus"; import { EventsDisplay } from "./layers/EventsDisplay"; import { FPSDisplay } from "./layers/FPSDisplay"; import { FxLayer } from "./layers/FxLayer"; import { GameLeftSidebar } from "./layers/GameLeftSidebar"; import { GameRightSidebar } from "./layers/GameRightSidebar"; -import { GameStartingModal } from "../GameStartingModal"; -import { GameView } from "../../core/game/GameView"; import { GutterAdModal } from "./layers/GutterAdModal"; import { HeadsUpMessage } from "./layers/HeadsUpMessage"; import { Layer } from "./layers/Layer"; @@ -22,7 +24,6 @@ import { NameLayer } from "./layers/NameLayer"; import { PlayerInfoOverlay } from "./layers/PlayerInfoOverlay"; import { PlayerPanel } from "./layers/PlayerPanel"; import { RailroadLayer } from "./layers/RailroadLayer"; -import { RedrawGraphicsEvent } from "../InputHandler"; import { ReplayPanel } from "./layers/ReplayPanel"; import { SettingsModal } from "./layers/SettingsModal"; import { SpawnAd } from "./layers/SpawnAd"; @@ -32,13 +33,12 @@ import { StructureLayer } from "./layers/StructureLayer"; import { TeamStats } from "./layers/TeamStats"; import { TerrainLayer } from "./layers/TerrainLayer"; import { TerritoryLayer } from "./layers/TerritoryLayer"; -import { TransformHandler } from "./TransformHandler"; import { UILayer } from "./layers/UILayer"; -import { UIState } from "./UIState"; import { UnitDisplay } from "./layers/UnitDisplay"; import { UnitLayer } from "./layers/UnitLayer"; -import { UserSettings } from "../../core/game/UserSettings"; import { WinModal } from "./layers/WinModal"; +import { TransformHandler } from "./TransformHandler"; +import { UIState } from "./UIState"; export function createRenderer( canvas: HTMLCanvasElement, diff --git a/src/client/graphics/NameBoxCalculator.ts b/src/client/graphics/NameBoxCalculator.ts index 2abc985733..be5cdc8e72 100644 --- a/src/client/graphics/NameBoxCalculator.ts +++ b/src/client/graphics/NameBoxCalculator.ts @@ -56,9 +56,9 @@ export function placeName(game: Game, player: Player): NameViewData { center = new Cell(center.x, center.y - fontSize / 3); return { + size: fontSize, x: Math.ceil(center.x), y: Math.ceil(center.y), - size: fontSize, }; } @@ -69,14 +69,14 @@ export function createGrid( scalingFactor: number, ): boolean[][] { const scaledBoundingBox: { min: Point; max: Point } = { - min: { - x: Math.floor(boundingBox.min.x / scalingFactor), - y: Math.floor(boundingBox.min.y / scalingFactor), - }, max: { x: Math.floor(boundingBox.max.x / scalingFactor), y: Math.floor(boundingBox.max.y / scalingFactor), }, + min: { + x: Math.floor(boundingBox.min.x / scalingFactor), + y: Math.floor(boundingBox.min.y / scalingFactor), + }, }; const width = scaledBoundingBox.max.x - scaledBoundingBox.min.x + 1; @@ -105,7 +105,7 @@ export function findLargestInscribedRectangle(grid: boolean[][]): Rectangle { const rows = grid[0].length; const cols = grid.length; const heights: number[] = new Array(cols).fill(0); - let largestRect: Rectangle = { x: 0, y: 0, width: 0, height: 0 }; + let largestRect: Rectangle = { height: 0, width: 0, x: 0, y: 0 }; for (let row = 0; row < rows; row++) { for (let col = 0; col < cols; col++) { @@ -123,10 +123,10 @@ export function findLargestInscribedRectangle(grid: boolean[][]): Rectangle { largestRect.width * largestRect.height ) { largestRect = { + height: rectForRow.height, + width: rectForRow.width, x: rectForRow.x, y: row - rectForRow.height + 1, - width: rectForRow.width, - height: rectForRow.height, }; } } @@ -137,7 +137,7 @@ export function findLargestInscribedRectangle(grid: boolean[][]): Rectangle { export function largestRectangleInHistogram(widths: number[]): Rectangle { const stack: number[] = []; let maxArea = 0; - let largestRect: Rectangle = { x: 0, y: 0, width: 0, height: 0 }; + let largestRect: Rectangle = { height: 0, width: 0, x: 0, y: 0 }; for (let i = 0; i <= widths.length; i++) { const h = i === widths.length ? 0 : widths[i]; @@ -151,10 +151,10 @@ export function largestRectangleInHistogram(widths: number[]): Rectangle { if (height * width > maxArea) { maxArea = height * width; largestRect = { + height, + width, x: stack.length === 0 ? 0 : stack[stack.length - 1] + 1, y: 0, - width, - height, }; } } diff --git a/src/client/graphics/SpriteLoader.ts b/src/client/graphics/SpriteLoader.ts index 4e140ec4e2..12279bf4c4 100644 --- a/src/client/graphics/SpriteLoader.ts +++ b/src/client/graphics/SpriteLoader.ts @@ -1,22 +1,22 @@ -import { TrainType, UnitType } from "../../core/game/Game"; import { Colord } from "colord"; -import { Theme } from "../../core/configuration/Config"; -import { UnitView } from "../../core/game/GameView"; import atomBombSprite from "../../../resources/sprites/atombomb.png"; import hydrogenBombSprite from "../../../resources/sprites/hydrogenbomb.png"; import mirvSprite from "../../../resources/sprites/mirv2.png"; import samMissileSprite from "../../../resources/sprites/samMissile.png"; import tradeShipSprite from "../../../resources/sprites/tradeship.png"; import trainCarriageSprite from "../../../resources/sprites/trainCarriage.png"; -import trainEngineSprite from "../../../resources/sprites/trainEngine.png"; import trainLoadedCarriageSprite from "../../../resources/sprites/trainCarriageLoaded.png"; +import trainEngineSprite from "../../../resources/sprites/trainEngine.png"; import transportShipSprite from "../../../resources/sprites/transportship.png"; import warshipSprite from "../../../resources/sprites/warship.png"; +import { Theme } from "../../core/configuration/Config"; +import { TrainType, UnitType } from "../../core/game/Game"; +import { UnitView } from "../../core/game/GameView"; // Can't reuse TrainType because "loaded" is not a type, just an attribute const TrainTypeSprite = { - Engine: "Engine", Carriage: "Carriage", + Engine: "Engine", LoadedCarriage: "LoadedCarriage", } as const; diff --git a/src/client/graphics/TransformHandler.ts b/src/client/graphics/TransformHandler.ts index b1569e4633..c0a735e9c0 100644 --- a/src/client/graphics/TransformHandler.ts +++ b/src/client/graphics/TransformHandler.ts @@ -1,12 +1,12 @@ +import { EventBus } from "../../core/EventBus"; +import { Cell } from "../../core/game/Game"; +import { GameView } from "../../core/game/GameView"; import { CenterCameraEvent, DragEvent, ZoomEvent } from "../InputHandler"; import { GoToPlayerEvent, GoToPositionEvent, GoToUnitEvent, } from "./layers/Leaderboard"; -import { Cell } from "../../core/game/Game"; -import { EventBus } from "../../core/EventBus"; -import { GameView } from "../../core/game/GameView"; export const GOTO_INTERVAL_MS = 16; export const CAMERA_MAX_SPEED = 15; diff --git a/src/client/graphics/fx/ConquestFx.ts b/src/client/graphics/fx/ConquestFx.ts index 272ccbd3f2..a7d5ab7a50 100644 --- a/src/client/graphics/fx/ConquestFx.ts +++ b/src/client/graphics/fx/ConquestFx.ts @@ -1,10 +1,10 @@ -import { FadeFx, SpriteFx } from "./SpriteFx"; -import { Fx, FxType } from "./Fx"; -import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader"; import { ConquestUpdate } from "../../../core/game/GameUpdates"; import { GameView } from "../../../core/game/GameView"; -import { TextFx } from "./TextFx"; import { renderNumber } from "../../Utils"; +import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader"; +import { Fx, FxType } from "./Fx"; +import { FadeFx, SpriteFx } from "./SpriteFx"; +import { TextFx } from "./TextFx"; /** * Conquest FX: diff --git a/src/client/graphics/fx/NukeFx.ts b/src/client/graphics/fx/NukeFx.ts index d610d403c9..81b1ed6935 100644 --- a/src/client/graphics/fx/NukeFx.ts +++ b/src/client/graphics/fx/NukeFx.ts @@ -1,7 +1,7 @@ -import { FadeFx, SpriteFx } from "./SpriteFx"; -import { Fx, FxType } from "./Fx"; -import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader"; import { GameView } from "../../../core/game/GameView"; +import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader"; +import { Fx, FxType } from "./Fx"; +import { FadeFx, SpriteFx } from "./SpriteFx"; /** * Shockwave effect: draw a growing 1px white circle @@ -88,10 +88,10 @@ export function nukeFxFactory( radiusFactor: number; density: number; }> = [ - { type: FxType.MiniFire, radiusFactor: 1.0, density: 1 / 25 }, - { type: FxType.MiniSmoke, radiusFactor: 1.0, density: 1 / 28 }, - { type: FxType.MiniBigSmoke, radiusFactor: 0.9, density: 1 / 70 }, - { type: FxType.MiniSmokeAndFire, radiusFactor: 0.9, density: 1 / 70 }, + { density: 1 / 25, radiusFactor: 1.0, type: FxType.MiniFire }, + { density: 1 / 28, radiusFactor: 1.0, type: FxType.MiniSmoke }, + { density: 1 / 70, radiusFactor: 0.9, type: FxType.MiniBigSmoke }, + { density: 1 / 70, radiusFactor: 0.9, type: FxType.MiniSmokeAndFire }, ]; for (const { type, radiusFactor, density } of debrisPlan) { diff --git a/src/client/graphics/fx/SpriteFx.ts b/src/client/graphics/fx/SpriteFx.ts index be5e7ddb79..653d4986a5 100644 --- a/src/client/graphics/fx/SpriteFx.ts +++ b/src/client/graphics/fx/SpriteFx.ts @@ -1,8 +1,8 @@ -import { Fx, FxType } from "./Fx"; +import { Theme } from "../../../core/configuration/Config"; +import { PlayerView } from "../../../core/game/GameView"; import { AnimatedSprite } from "../AnimatedSprite"; import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader"; -import { PlayerView } from "../../../core/game/GameView"; -import { Theme } from "../../../core/configuration/Config"; +import { Fx, FxType } from "./Fx"; function fadeInOut(t: number, fadeIn = 0.3, fadeOut = 0.7): number { if (t < fadeIn) { diff --git a/src/client/graphics/fx/TextFx.ts b/src/client/graphics/fx/TextFx.ts index cfc7288f81..1c034a9b62 100644 --- a/src/client/graphics/fx/TextFx.ts +++ b/src/client/graphics/fx/TextFx.ts @@ -11,9 +11,9 @@ export class TextFx implements Fx { private readonly riseDistance = 30, private readonly font = "11px sans-serif", private readonly color: { r: number; g: number; b: number } = { - r: 255, - g: 255, b: 255, + g: 255, + r: 255, }, ) {} diff --git a/src/client/graphics/fx/Timeline.ts b/src/client/graphics/fx/Timeline.ts index d500a1f33e..26918a8658 100644 --- a/src/client/graphics/fx/Timeline.ts +++ b/src/client/graphics/fx/Timeline.ts @@ -12,7 +12,7 @@ export class Timeline { private timeElapsed = 0; add(delay: number, action: () => void): Timeline { - this.tasks.push({ delay, action, triggered: false }); + this.tasks.push({ action, delay, triggered: false }); return this; } diff --git a/src/client/graphics/fx/UnitExplosionFx.ts b/src/client/graphics/fx/UnitExplosionFx.ts index 8971e057de..f21a6fbba8 100644 --- a/src/client/graphics/fx/UnitExplosionFx.ts +++ b/src/client/graphics/fx/UnitExplosionFx.ts @@ -1,6 +1,6 @@ -import { Fx, FxType } from "./Fx"; -import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader"; import { GameView } from "../../../core/game/GameView"; +import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader"; +import { Fx, FxType } from "./Fx"; import { SpriteFx } from "./SpriteFx"; import { Timeline } from "./Timeline"; @@ -18,9 +18,9 @@ export class UnitExplosionFx implements Fx { game: GameView, ) { const config = [ - { dx: 0, dy: 0, delay: 0, type: FxType.UnitExplosion }, - { dx: 4, dy: -6, delay: 80, type: FxType.UnitExplosion }, - { dx: -6, dy: 4, delay: 160, type: FxType.UnitExplosion }, + { delay: 0, dx: 0, dy: 0, type: FxType.UnitExplosion }, + { delay: 80, dx: 4, dy: -6, type: FxType.UnitExplosion }, + { delay: 160, dx: -6, dy: 4, type: FxType.UnitExplosion }, ]; for (const { dx, dy, delay, type } of config) { this.timeline.add(delay, () => { diff --git a/src/client/graphics/layers/AlertFrame.ts b/src/client/graphics/layers/AlertFrame.ts index a77e0603cd..949b7d28b5 100644 --- a/src/client/graphics/layers/AlertFrame.ts +++ b/src/client/graphics/layers/AlertFrame.ts @@ -1,12 +1,12 @@ +import { css, html, LitElement } from "lit"; +import { customElement, state } from "lit/decorators.js"; import { BrokeAllianceUpdate, GameUpdateType, } from "../../../core/game/GameUpdates"; -import { LitElement, css, html } from "lit"; -import { customElement, state } from "lit/decorators.js"; import { GameView } from "../../../core/game/GameView"; -import { Layer } from "./Layer"; import { UserSettings } from "../../../core/game/UserSettings"; +import { Layer } from "./Layer"; // Parameters for the alert animation const ALERT_SPEED = 1.6; diff --git a/src/client/graphics/layers/BuildMenu.ts b/src/client/graphics/layers/BuildMenu.ts index 53702ed820..b6f49d2ddb 100644 --- a/src/client/graphics/layers/BuildMenu.ts +++ b/src/client/graphics/layers/BuildMenu.ts @@ -1,39 +1,39 @@ -import { - BuildUnitIntentEvent, - SendUpgradeStructureIntentEvent, -} from "../../Transport"; +import { css, html, LitElement } from "lit"; +import { customElement, state } from "lit/decorators.js"; +import warshipIcon from "../../../../resources/images/BattleshipIconWhite.svg"; +import cityIcon from "../../../../resources/images/CityIconWhite.svg"; +import factoryIcon from "../../../../resources/images/FactoryIconWhite.svg"; +import goldCoinIcon from "../../../../resources/images/GoldCoinIcon.svg"; +import mirvIcon from "../../../../resources/images/MIRVIcon.svg"; +import hydrogenBombIcon from "../../../../resources/images/MushroomCloudIconWhite.svg"; +import atomBombIcon from "../../../../resources/images/NukeIconWhite.svg"; +import portIcon from "../../../../resources/images/PortIcon.svg"; +import shieldIcon from "../../../../resources/images/ShieldIconWhite.svg"; +import missileSiloIcon from "../../../../resources/non-commercial/svg/MissileSiloIconWhite.svg"; +import samlauncherIcon from "../../../../resources/non-commercial/svg/SamLauncherIconWhite.svg"; +import { translateText } from "../../../client/Utils"; +import { EventBus } from "../../../core/EventBus"; import { BuildableUnit, Gold, PlayerActions, UnitType, } from "../../../core/game/Game"; +import { TileRef } from "../../../core/game/GameMap"; +import { GameView } from "../../../core/game/GameView"; import { CloseViewEvent, MouseDownEvent, ShowBuildMenuEvent, ShowEmojiMenuEvent, } from "../../InputHandler"; -import { LitElement, css, html } from "lit"; -import { customElement, state } from "lit/decorators.js"; -import { EventBus } from "../../../core/EventBus"; -import { GameView } from "../../../core/game/GameView"; -import { Layer } from "./Layer"; -import { TileRef } from "../../../core/game/GameMap"; -import { TransformHandler } from "../TransformHandler"; -import atomBombIcon from "../../../../resources/images/NukeIconWhite.svg"; -import cityIcon from "../../../../resources/images/CityIconWhite.svg"; -import factoryIcon from "../../../../resources/images/FactoryIconWhite.svg"; -import goldCoinIcon from "../../../../resources/images/GoldCoinIcon.svg"; -import hydrogenBombIcon from "../../../../resources/images/MushroomCloudIconWhite.svg"; -import mirvIcon from "../../../../resources/images/MIRVIcon.svg"; -import missileSiloIcon from "../../../../resources/non-commercial/svg/MissileSiloIconWhite.svg"; -import portIcon from "../../../../resources/images/PortIcon.svg"; +import { + BuildUnitIntentEvent, + SendUpgradeStructureIntentEvent, +} from "../../Transport"; import { renderNumber } from "../../Utils"; -import samlauncherIcon from "../../../../resources/non-commercial/svg/SamLauncherIconWhite.svg"; -import shieldIcon from "../../../../resources/images/ShieldIconWhite.svg"; -import { translateText } from "../../../client/Utils"; -import warshipIcon from "../../../../resources/images/BattleshipIconWhite.svg"; +import { TransformHandler } from "../TransformHandler"; +import { Layer } from "./Layer"; export type BuildItemDisplay = { unitType: UnitType; @@ -46,75 +46,75 @@ export type BuildItemDisplay = { export const buildTable: BuildItemDisplay[][] = [ [ { - unitType: UnitType.AtomBomb, - icon: atomBombIcon, + countable: false, description: "build_menu.desc.atom_bomb", + icon: atomBombIcon, key: "unit_type.atom_bomb", - countable: false, + unitType: UnitType.AtomBomb, }, { - unitType: UnitType.MIRV, - icon: mirvIcon, + countable: false, description: "build_menu.desc.mirv", + icon: mirvIcon, key: "unit_type.mirv", - countable: false, + unitType: UnitType.MIRV, }, { - unitType: UnitType.HydrogenBomb, - icon: hydrogenBombIcon, + countable: false, description: "build_menu.desc.hydrogen_bomb", + icon: hydrogenBombIcon, key: "unit_type.hydrogen_bomb", - countable: false, + unitType: UnitType.HydrogenBomb, }, { - unitType: UnitType.Warship, - icon: warshipIcon, + countable: true, description: "build_menu.desc.warship", + icon: warshipIcon, key: "unit_type.warship", - countable: true, + unitType: UnitType.Warship, }, { - unitType: UnitType.Port, - icon: portIcon, + countable: true, description: "build_menu.desc.port", + icon: portIcon, key: "unit_type.port", - countable: true, + unitType: UnitType.Port, }, { - unitType: UnitType.MissileSilo, - icon: missileSiloIcon, + countable: true, description: "build_menu.desc.missile_silo", + icon: missileSiloIcon, key: "unit_type.missile_silo", - countable: true, + unitType: UnitType.MissileSilo, }, // needs new icon { - unitType: UnitType.SAMLauncher, - icon: samlauncherIcon, + countable: true, description: "build_menu.desc.sam_launcher", + icon: samlauncherIcon, key: "unit_type.sam_launcher", - countable: true, + unitType: UnitType.SAMLauncher, }, { - unitType: UnitType.DefensePost, - icon: shieldIcon, + countable: true, description: "build_menu.desc.defense_post", + icon: shieldIcon, key: "unit_type.defense_post", - countable: true, + unitType: UnitType.DefensePost, }, { - unitType: UnitType.City, - icon: cityIcon, + countable: true, description: "build_menu.desc.city", + icon: cityIcon, key: "unit_type.city", - countable: true, + unitType: UnitType.City, }, { - unitType: UnitType.Factory, - icon: factoryIcon, + countable: true, description: "build_menu.desc.factory", + icon: factoryIcon, key: "unit_type.factory", - countable: true, + unitType: UnitType.Factory, }, ], ]; @@ -389,7 +389,10 @@ export class BuildMenu extends LitElement implements Layer { return player.totalUnitLevels(item.unitType).toString(); } - public sendBuildOrUpgrade(buildableUnit: BuildableUnit, tile?: TileRef): void { + public sendBuildOrUpgrade( + buildableUnit: BuildableUnit, + tile?: TileRef, + ): void { if (tile === undefined) throw new Error("Missing tile"); if (this.eventBus === undefined) throw new Error("Not initialized"); if (buildableUnit.canUpgrade !== false) { @@ -430,9 +433,11 @@ export class BuildMenu extends LitElement implements Layer { @click=${() => this.sendBuildOrUpgrade(buildableUnit, this.clickedTile)} ?disabled=${!enabled} - title=${!enabled - ? translateText("build_menu.not_enough_money") - : ""} + title=${ + !enabled + ? translateText("build_menu.not_enough_money") + : "" + } > ${item.key && translateText(item.key)} ${item.description && - translateText(item.description)}${ + item.description && translateText(item.description) + } ${renderNumber( @@ -459,11 +465,13 @@ export class BuildMenu extends LitElement implements Layer { style="vertical-align: middle;" /> - ${item.countable - ? html`
+ ${ + item.countable + ? html`
${this.count(item)}
` - : ""} + : "" + } `; })} diff --git a/src/client/graphics/layers/ChatDisplay.ts b/src/client/graphics/layers/ChatDisplay.ts index da81e5c21a..56f1e0b8da 100644 --- a/src/client/graphics/layers/ChatDisplay.ts +++ b/src/client/graphics/layers/ChatDisplay.ts @@ -1,16 +1,16 @@ +import { html, LitElement } from "lit"; +import { customElement, state } from "lit/decorators.js"; +import { DirectiveResult } from "lit/directive.js"; +import { UnsafeHTMLDirective, unsafeHTML } from "lit/directives/unsafe-html.js"; +import { EventBus } from "../../../core/EventBus"; +import { MessageType } from "../../../core/game/Game"; import { DisplayMessageUpdate, GameUpdateType, } from "../../../core/game/GameUpdates"; -import { LitElement, html } from "lit"; -import { UnsafeHTMLDirective, unsafeHTML } from "lit/directives/unsafe-html.js"; -import { customElement, state } from "lit/decorators.js"; -import { DirectiveResult } from "lit/directive.js"; -import { EventBus } from "../../../core/EventBus"; import { GameView } from "../../../core/game/GameView"; -import { Layer } from "./Layer"; -import { MessageType } from "../../../core/game/Game"; import { onlyImages } from "../../../core/Util"; +import { Layer } from "./Layer"; type ChatEvent = { description: string; @@ -65,8 +65,8 @@ export class ChatDisplay extends LitElement implements Layer { } this.addEvent({ - description: event.message, createdAt: this.game.ticks(), + description: event.message, highlight: true, unsafeDescription: true, }); @@ -97,9 +97,9 @@ export class ChatDisplay extends LitElement implements Layer { this.chatEvents = [ ...this.chatEvents, { + createdAt: this.game.ticks(), description: msg.message, unsafeDescription: true, - createdAt: this.game.ticks(), }, ]; } @@ -136,10 +136,9 @@ export class ChatDisplay extends LitElement implements Layer {
diff --git a/src/client/graphics/layers/ChatIntegration.ts b/src/client/graphics/layers/ChatIntegration.ts index f8993604be..8d6d991ec2 100644 --- a/src/client/graphics/layers/ChatIntegration.ts +++ b/src/client/graphics/layers/ChatIntegration.ts @@ -1,9 +1,9 @@ -import { COLORS, MenuElement, MenuElementParams } from "./RadialMenuElements"; -import { ChatModal, QuickChatPhrase, quickChatPhrases } from "./ChatModal"; -import { GameView, PlayerView } from "../../../core/game/GameView"; import { EventBus } from "../../../core/EventBus"; +import { GameView, PlayerView } from "../../../core/game/GameView"; import { SendQuickChatEvent } from "../../Transport"; import { translateText } from "../../Utils"; +import { ChatModal, QuickChatPhrase, quickChatPhrases } from "./ChatModal"; +import { COLORS, MenuElement, MenuElementParams } from "./RadialMenuElements"; export class ChatIntegration { private readonly ctModal: ChatModal; @@ -49,18 +49,6 @@ export class ChatIntegration { const phraseText = translateText(`chat.${category.id}.${phrase.key}`); return { - id: `phrase-${category.id}-${phrase.key}`, - name: phraseText, - disabled: () => false, - text: this.shortenText(phraseText), - fontSize: "10px", - color: categoryColor, - tooltipItems: [ - { - text: phraseText, - className: "description", - }, - ], action: (params: MenuElementParams) => { if (phrase.requiresPlayer) { this.ctModal.openWithSelection( @@ -79,18 +67,30 @@ export class ChatIntegration { ); } }, + color: categoryColor, + disabled: () => false, + fontSize: "10px", + id: `phrase-${category.id}-${phrase.key}`, + name: phraseText, + text: this.shortenText(phraseText), + tooltipItems: [ + { + className: "description", + text: phraseText, + }, + ], }; }, ); return { + _action: () => {}, // Empty action placeholder for RadialMenu + color: categoryColor, + disabled: () => false, id: `chat-category-${category.id}`, name: categoryTranslation, - disabled: () => false, - text: categoryTranslation, - color: categoryColor, - _action: () => {}, // Empty action placeholder for RadialMenu subMenu: () => phraseItems, + text: categoryTranslation, }; }); } diff --git a/src/client/graphics/layers/ChatModal.ts b/src/client/graphics/layers/ChatModal.ts index 3aacd9538a..ddca522796 100644 --- a/src/client/graphics/layers/ChatModal.ts +++ b/src/client/graphics/layers/ChatModal.ts @@ -1,11 +1,11 @@ -import { GameView, PlayerView } from "../../../core/game/GameView"; -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, query } from "lit/decorators.js"; -import { CloseViewEvent } from "../../InputHandler"; +import quickChatData from "../../../../resources/QuickChat.json"; import { EventBus } from "../../../core/EventBus"; import { PlayerType } from "../../../core/game/Game"; +import { GameView, PlayerView } from "../../../core/game/GameView"; +import { CloseViewEvent } from "../../InputHandler"; import { SendQuickChatEvent } from "../../Transport"; -import quickChatData from "../../../../resources/QuickChat.json"; import { translateText } from "../../Utils"; export type QuickChatPhrase = { @@ -49,11 +49,11 @@ export class ChatModal extends LitElement { string, Array<{ text: string; requiresPlayer: boolean }> > = { - help: [{ text: "Please give me troops!", requiresPlayer: false }], - attack: [{ text: "Attack [P1]!", requiresPlayer: true }], - defend: [{ text: "Defend [P1]!", requiresPlayer: true }], - greet: [{ text: "Hello!", requiresPlayer: false }], - misc: [{ text: "Let's go!", requiresPlayer: false }], + attack: [{ requiresPlayer: true, text: "Attack [P1]!" }], + defend: [{ requiresPlayer: true, text: "Defend [P1]!" }], + greet: [{ requiresPlayer: false, text: "Hello!" }], + help: [{ requiresPlayer: false, text: "Please give me troops!" }], + misc: [{ requiresPlayer: false, text: "Let's go!" }], }; public categories = [ @@ -78,10 +78,9 @@ export class ChatModal extends LitElement { ${this.categories.map( (category) => html` diff --git a/src/client/graphics/layers/ControlPanel.ts b/src/client/graphics/layers/ControlPanel.ts index 3257f0f6fb..f07c6e6bdb 100644 --- a/src/client/graphics/layers/ControlPanel.ts +++ b/src/client/graphics/layers/ControlPanel.ts @@ -1,14 +1,14 @@ -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, state } from "lit/decorators.js"; -import { renderNumber, renderTroops } from "../../Utils"; -import { AttackRatioEvent } from "../../InputHandler"; -import { ClientID } from "../../../core/Schemas"; +import { translateText } from "../../../client/Utils"; import { EventBus } from "../../../core/EventBus"; -import { GameView } from "../../../core/game/GameView"; import { Gold } from "../../../core/game/Game"; -import { Layer } from "./Layer"; +import { GameView } from "../../../core/game/GameView"; +import { ClientID } from "../../../core/Schemas"; +import { AttackRatioEvent } from "../../InputHandler"; +import { renderNumber, renderTroops } from "../../Utils"; import { UIState } from "../UIState"; -import { translateText } from "../../../client/Utils"; +import { Layer } from "./Layer"; @customElement("control-panel") export class ControlPanel extends LitElement implements Layer { @@ -166,10 +166,12 @@ export class ControlPanel extends LitElement implements Layer { }
e.preventDefault()} >
@@ -180,9 +182,11 @@ export class ControlPanel extends LitElement implements Layer { ${renderTroops(this._troops)} / ${renderTroops(this._maxTroops)} (+${renderTroops(this.troopRate)}) { this.isVisible = true; const cell = transformHandler.screenToWorldCoordinates(e.x, e.y); diff --git a/src/client/graphics/layers/EventsDisplay.ts b/src/client/graphics/layers/EventsDisplay.ts index 66771cfbc4..40621a87c3 100644 --- a/src/client/graphics/layers/EventsDisplay.ts +++ b/src/client/graphics/layers/EventsDisplay.ts @@ -1,12 +1,22 @@ /* eslint-disable max-lines */ + +import { html, LitElement, TemplateResult } from "lit"; +import { customElement, state } from "lit/decorators.js"; +import { DirectiveResult } from "lit/directive.js"; +import { UnsafeHTMLDirective, unsafeHTML } from "lit/directives/unsafe-html.js"; +import allianceIcon from "../../../../resources/images/AllianceIconWhite.svg"; +import chatIcon from "../../../../resources/images/ChatIconWhite.svg"; +import donateGoldIcon from "../../../../resources/images/DonateGoldIconWhite.svg"; +import swordIcon from "../../../../resources/images/SwordIconWhite.svg"; +import { EventBus } from "../../../core/EventBus"; import { AllPlayers, + getMessageCategory, MessageCategory, MessageType, PlayerType, Tick, UnitType, - getMessageCategory, } from "../../../core/game/Game"; import { AllianceExpiredUpdate, @@ -21,31 +31,26 @@ import { TargetPlayerUpdate, UnitIncomingUpdate, } from "../../../core/game/GameUpdates"; +import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; +import { onlyImages } from "../../../core/Util"; import { CancelAttackIntentEvent, CancelBoatIntentEvent, SendAllianceExtensionIntentEvent, SendAllianceReplyIntentEvent, } from "../../Transport"; -import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; +import { + getMessageTypeClasses, + renderNumber, + renderTroops, + translateText, +} from "../../Utils"; +import { Layer } from "./Layer"; import { GoToPlayerEvent, GoToPositionEvent, GoToUnitEvent, } from "./Leaderboard"; -import { LitElement, TemplateResult, html } from "lit"; -import { UnsafeHTMLDirective, unsafeHTML } from "lit/directives/unsafe-html.js"; -import { customElement, state } from "lit/decorators.js"; -import { getMessageTypeClasses, translateText } from "../../Utils"; -import { renderNumber, renderTroops } from "../../Utils"; -import { DirectiveResult } from "lit/directive.js"; -import { EventBus } from "../../../core/EventBus"; -import { Layer } from "./Layer"; -import allianceIcon from "../../../../resources/images/AllianceIconWhite.svg"; -import chatIcon from "../../../../resources/images/ChatIconWhite.svg"; -import donateGoldIcon from "../../../../resources/images/DonateGoldIconWhite.svg"; -import { onlyImages } from "../../../core/Util"; -import swordIcon from "../../../../resources/images/SwordIconWhite.svg"; type GameEvent = { description: string; @@ -88,15 +93,19 @@ export class EventsDisplay extends LitElement implements Layer { @state() private latestGoldAmount: bigint | null = null; @state() private goldAmountAnimating = false; private goldAmountTimeoutId: ReturnType | null = null; - @state() private readonly eventsFilters: Map = new Map([ - [MessageCategory.ATTACK, false], - [MessageCategory.TRADE, false], - [MessageCategory.ALLIANCE, false], - [MessageCategory.CHAT, false], - ]); + @state() private readonly eventsFilters: Map = + new Map([ + [MessageCategory.ATTACK, false], + [MessageCategory.TRADE, false], + [MessageCategory.ALLIANCE, false], + [MessageCategory.CHAT, false], + ]); private renderButton(options: { - content: string | TemplateResult | DirectiveResult; + content: + | string + | TemplateResult + | DirectiveResult; onClick?: () => void; className?: string; disabled?: boolean; @@ -272,35 +281,35 @@ export class EventsDisplay extends LitElement implements Layer { if (!other.isAlive()) continue; this.addEvent({ - description: translateText("events_display.about_to_expire", { - name: other.name(), - }), - type: MessageType.RENEW_ALLIANCE, - duration: this.game.config().allianceExtensionPromptOffset() - 3 * 10, // 3 second buffer buttons: [ { - text: translateText("events_display.focus"), - className: "btn-gray", action: () => this.eventBus?.emit(new GoToPlayerEvent(other)), + className: "btn-gray", preventClose: true, + text: translateText("events_display.focus"), }, { + action: () => + this.eventBus?.emit(new SendAllianceExtensionIntentEvent(other)), + className: "btn", text: translateText("events_display.renew_alliance", { name: other.name(), }), - className: "btn", - action: () => - this.eventBus?.emit(new SendAllianceExtensionIntentEvent(other)), }, { - text: translateText("events_display.ignore"), - className: "btn-info", action: () => {}, + className: "btn-info", + text: translateText("events_display.ignore"), }, ], - highlight: true, createdAt: this.game.ticks(), + description: translateText("events_display.about_to_expire", { + name: other.name(), + }), + duration: this.game.config().allianceExtensionPromptOffset() - 3 * 10, // 3 second buffer focusID: other.smallID(), + highlight: true, + type: MessageType.RENEW_ALLIANCE, }); } } @@ -367,8 +376,8 @@ export class EventsDisplay extends LitElement implements Layer { } this.addEvent({ - description, createdAt: this.game.ticks(), + description, highlight: true, type: event.messageType, unsafeDescription: true, @@ -410,11 +419,11 @@ export class EventsDisplay extends LitElement implements Layer { } this.addEvent({ + createdAt: this.game.ticks(), description: translateText(event.isFrom ? "chat.from" : "chat.to", { - user: otherPlayerDiplayName, msg: translatedMessage, + user: otherPlayerDiplayName, }), - createdAt: this.game.ticks(), highlight: true, type: MessageType.CHAT, unsafeDescription: false, @@ -436,43 +445,42 @@ export class EventsDisplay extends LitElement implements Layer { ) as PlayerView; this.addEvent({ - description: translateText("events_display.request_alliance", { - name: requestor.name(), - }), buttons: [ { - text: translateText("events_display.focus"), - className: "btn-gray", action: () => this.eventBus?.emit(new GoToPlayerEvent(requestor)), + className: "btn-gray", preventClose: true, + text: translateText("events_display.focus"), }, { - text: translateText("events_display.accept_alliance"), - className: "btn", action: () => this.eventBus?.emit( new SendAllianceReplyIntentEvent(requestor, recipient, true), ), + className: "btn", + text: translateText("events_display.accept_alliance"), }, { - text: translateText("events_display.reject_alliance"), - className: "btn-info", action: () => this.eventBus?.emit( new SendAllianceReplyIntentEvent(requestor, recipient, false), ), + className: "btn-info", + text: translateText("events_display.reject_alliance"), }, ], - highlight: true, - type: MessageType.ALLIANCE_REQUEST, createdAt: this.game.ticks(), - priority: 0, + description: translateText("events_display.request_alliance", { + name: requestor.name(), + }), duration: this.game.config().allianceRequestDuration() - 20, // 2 second buffer + focusID: update.requestorID, + priority: 0, shouldDelete: (game) => { // Recipient sent a separate request, so they became allied without the recipient responding. return requestor.isAlliedWith(recipient); }, - focusID: update.requestorID, + type: MessageType.ALLIANCE_REQUEST, }); } @@ -504,18 +512,18 @@ export class EventsDisplay extends LitElement implements Layer { update.request.recipientID, ) as PlayerView; this.addEvent({ + createdAt: this.game.ticks(), description: translateText("events_display.alliance_request_status", { name: recipient.name(), status: update.accepted ? translateText("events_display.alliance_accepted") : translateText("events_display.alliance_rejected"), }), + focusID: update.request.recipientID, + highlight: true, type: update.accepted ? MessageType.ALLIANCE_ACCEPTED : MessageType.ALLIANCE_REJECTED, - highlight: true, - createdAt: this.game.ticks(), - focusID: update.request.recipientID, }); } @@ -541,38 +549,38 @@ export class EventsDisplay extends LitElement implements Layer { traitorDuration === 1 ? translateText("events_display.duration_second") : translateText("events_display.duration_seconds_plural", { - seconds: traitorDuration, - }); + seconds: traitorDuration, + }); this.addEvent({ + createdAt: this.game.ticks(), description: translateText("events_display.betrayal_description", { - name: betrayed.name(), - malusPercent, durationText, + malusPercent, + name: betrayed.name(), }), - type: MessageType.ALLIANCE_BROKEN, - highlight: true, - createdAt: this.game.ticks(), focusID: update.betrayedID, + highlight: true, + type: MessageType.ALLIANCE_BROKEN, }); } else if (betrayed === myPlayer) { const buttons = [ { - text: translateText("events_display.focus"), - className: "btn-gray", action: () => this.eventBus?.emit(new GoToPlayerEvent(traitor)), + className: "btn-gray", preventClose: true, + text: translateText("events_display.focus"), }, ]; this.addEvent({ + buttons, + createdAt: this.game.ticks(), description: translateText("events_display.betrayed_you", { name: traitor.name(), }), - type: MessageType.ALLIANCE_BROKEN, - highlight: true, - createdAt: this.game.ticks(), focusID: update.traitorID, - buttons, + highlight: true, + type: MessageType.ALLIANCE_BROKEN, }); } } @@ -593,13 +601,13 @@ export class EventsDisplay extends LitElement implements Layer { if (!other || !myPlayer.isAlive() || !other.isAlive()) return; this.addEvent({ + createdAt: this.game.ticks(), description: translateText("events_display.alliance_expired", { name: other.name(), }), - type: MessageType.ALLIANCE_EXPIRED, - highlight: true, - createdAt: this.game.ticks(), focusID: otherID, + highlight: true, + type: MessageType.ALLIANCE_EXPIRED, }); } @@ -612,14 +620,14 @@ export class EventsDisplay extends LitElement implements Layer { const target = this.game.playerBySmallID(event.targetID) as PlayerView; this.addEvent({ + createdAt: this.game.ticks(), description: translateText("events_display.attack_request", { name: other.name(), target: target.name(), }), - type: MessageType.ATTACK_REQUEST, - highlight: true, - createdAt: this.game.ticks(), focusID: event.targetID, + highlight: true, + type: MessageType.ATTACK_REQUEST, }); } @@ -667,24 +675,24 @@ export class EventsDisplay extends LitElement implements Layer { if (recipient === myPlayer) { this.addEvent({ - description: `${sender.displayName()}: ${update.emoji.message}`, - unsafeDescription: true, - type: MessageType.CHAT, - highlight: true, createdAt: this.game.ticks(), + description: `${sender.displayName()}: ${update.emoji.message}`, focusID: update.emoji.senderID, + highlight: true, + type: MessageType.CHAT, + unsafeDescription: true, }); } else if (sender === myPlayer && recipient !== AllPlayers) { this.addEvent({ + createdAt: this.game.ticks(), description: translateText("events_display.sent_emoji", { - name: (recipient as PlayerView).displayName(), emoji: update.emoji.message, + name: (recipient as PlayerView).displayName(), }), - unsafeDescription: true, - type: MessageType.CHAT, - highlight: true, - createdAt: this.game.ticks(), focusID: recipient.smallID(), + highlight: true, + type: MessageType.CHAT, + unsafeDescription: true, }); } } @@ -700,12 +708,12 @@ export class EventsDisplay extends LitElement implements Layer { const unitView = this.game.unit(event.unitID); this.addEvent({ + createdAt: this.game.ticks(), description: event.message, - type: event.messageType, - unsafeDescription: false, highlight: true, - createdAt: this.game.ticks(), + type: event.messageType, unitView, + unsafeDescription: false, }); } @@ -740,142 +748,155 @@ export class EventsDisplay extends LitElement implements Layer { private renderIncomingAttacks() { return html` - ${this.incomingAttacks.length > 0 - ? html` - ${this.incomingAttacks.map( - (attack) => { - const attacker = this.game?.playerBySmallID(attack.attackerID); - return html` + ${ + this.incomingAttacks.length > 0 + ? html` + ${this.incomingAttacks.map((attack) => { + const attacker = this.game?.playerBySmallID(attack.attackerID); + return html` ${this.renderButton({ + className: "text-left text-red-400", content: html` ${renderTroops(attack.troops)} ${attacker?.isPlayer() ? attacker.name() : "unknown"} - ${attack.retreating - ? `(${translateText("events_display.retreating")}...)` - : ""} + ${ + attack.retreating + ? `(${translateText("events_display.retreating")}...)` + : "" + } `, onClick: () => this.attackWarningOnClick(attack), - className: "text-left text-red-400", translate: false, })} `; - }, - )} + })} ` - : ""} + : "" + } `; } private renderOutgoingAttacks() { return html` - ${this.outgoingAttacks.length > 0 - ? html` + ${ + this.outgoingAttacks.length > 0 + ? html`
- ${this.outgoingAttacks.map( - (attack) => { - const target = this.game?.playerBySmallID(attack.targetID); - return html` + ${this.outgoingAttacks.map((attack) => { + const target = this.game?.playerBySmallID(attack.targetID); + return html`
${this.renderButton({ + className: "text-left text-blue-400", content: html` ${renderTroops(attack.troops)} ${target?.isPlayer() ? target.name() : "unknown"} `, onClick: async () => this.attackWarningOnClick(attack), - className: "text-left text-blue-400", translate: false, })} - ${!attack.retreating - ? this.renderButton({ - content: "❌", - onClick: () => this.emitCancelAttackIntent(attack.id), - className: "text-left flex-shrink-0", - disabled: attack.retreating, - }) - : html` + this.emitCancelAttackIntent(attack.id), + }) + : html`(${translateText( "events_display.retreating", )}...)`} + >` + }
`; - }, - )} + })}
` - : ""} + : "" + } `; } private renderOutgoingLandAttacks() { return html` - ${this.outgoingLandAttacks.length > 0 - ? html` + ${ + this.outgoingLandAttacks.length > 0 + ? html`
${this.outgoingLandAttacks.map( (landAttack) => html`
${this.renderButton({ + className: "text-left text-gray-400", content: html`${renderTroops(landAttack.troops)} ${translateText("help_modal.ui_wilderness")}`, - className: "text-left text-gray-400", translate: false, })} - ${!landAttack.retreating - ? this.renderButton({ - content: "❌", - onClick: () => - this.emitCancelAttackIntent(landAttack.id), - className: "text-left flex-shrink-0", - disabled: landAttack.retreating, - }) - : html` + this.emitCancelAttackIntent(landAttack.id), + }) + : html`(${translateText( "events_display.retreating", )}...)`} + >` + }
`, )}
` - : ""} + : "" + } `; } private renderBoats() { return html` - ${this.outgoingBoats.length > 0 - ? html` + ${ + this.outgoingBoats.length > 0 + ? html`
${this.outgoingBoats.map( (boat) => html`
${this.renderButton({ + className: "text-left text-blue-400", content: html`${translateText("events_display.boat")}: ${renderTroops(boat.troops())}`, onClick: () => this.emitGoToUnitEvent(boat), - className: "text-left text-blue-400", translate: false, })} - ${!boat.retreating() - ? this.renderButton({ - content: "❌", - onClick: () => this.emitBoatCancelIntent(boat.id()), - className: "text-left flex-shrink-0", - disabled: boat.retreating(), - }) - : html` this.emitBoatCancelIntent(boat.id()), + }) + : html`(${translateText( "events_display.retreating", )}...)`} + >` + }
`, )}
` - : ""} + : "" + } `; } @@ -923,27 +944,28 @@ export class EventsDisplay extends LitElement implements Layer { return html` ${styles} - ${this._hidden - ? html` + ${ + this._hidden + ? html`
${this.renderButton({ + className: + "text-white cursor-pointer pointer-events-auto w-fit p-2 " + + "lg:p-3 rounded-md bg-gray-800/70 backdrop-blur", content: html` Events ${this.newEvents} `, onClick: this.toggleHidden, - className: - "text-white cursor-pointer pointer-events-auto w-fit p-2 " + - "lg:p-3 rounded-md bg-gray-800/70 backdrop-blur", })}
` - : html` + : html`
${this.renderButton({ + className: "cursor-pointer pointer-events-auto", content: html``, onClick: () => this.toggleEventFilter(MessageCategory.ATTACK), - className: "cursor-pointer pointer-events-auto", })} ${this.renderButton({ + className: "cursor-pointer pointer-events-auto", content: html``, onClick: () => this.toggleEventFilter(MessageCategory.TRADE), - className: "cursor-pointer pointer-events-auto", })} ${this.renderButton({ + className: "cursor-pointer pointer-events-auto", content: html``, onClick: () => this.toggleEventFilter(MessageCategory.ALLIANCE), - className: "cursor-pointer pointer-events-auto", })} ${this.renderButton({ + className: "cursor-pointer pointer-events-auto", content: html``, onClick: () => this.toggleEventFilter(MessageCategory.CHAT), - className: "cursor-pointer pointer-events-auto", })}
- ${this.latestGoldAmount !== null - ? html`+${renderNumber(this.latestGoldAmount)}` - : ""} + : "" + } ${this.renderButton({ - content: translateText("leaderboard.hide"), - onClick: this.toggleHidden, className: "text-white cursor-pointer pointer-events-auto", + content: translateText("leaderboard.hide"), + onClick: this.toggleHidden, })}
@@ -1054,30 +1081,36 @@ export class EventsDisplay extends LitElement implements Layer { event.type, )}" > - ${event.focusID - ? this.renderButton({ - content: this.getEventDescription(event), - onClick: () => { - event.focusID && - this.emitGoToPlayerEvent(event.focusID); - }, - className: "text-left", - }) - : event.unitView + ${ + event.focusID ? this.renderButton({ - content: this.getEventDescription(event), - onClick: () => { - event.unitView && - this.emitGoToUnitEvent( - event.unitView, + className: "text-left", + content: this.getEventDescription(event), + onClick: () => { + event.focusID && + this.emitGoToPlayerEvent( + event.focusID, ); - }, - className: "text-left", - }) - : this.getEventDescription(event)} + }, + }) + : event.unitView + ? this.renderButton({ + className: "text-left", + content: + this.getEventDescription(event), + onClick: () => { + event.unitView && + this.emitGoToUnitEvent( + event.unitView, + ); + }, + }) + : this.getEventDescription(event) + } - ${event.buttons - ? html` + ${ + event.buttons + ? html`
${event.buttons.map( (btn) => html` @@ -1086,13 +1119,13 @@ export class EventsDisplay extends LitElement implements Layer { text-white rounded text-md md:text-sm cursor-pointer transition-colors duration-300 - ${btn.className.includes("btn-info") - ? "bg-blue-500 hover:bg-blue-600" - : btn.className.includes( - "btn-gray", - ) - ? "bg-gray-500 hover:bg-gray-600" - : "bg-green-600 hover:bg-green-700"}" + ${ + btn.className.includes("btn-info") + ? "bg-blue-500 hover:bg-blue-600" + : btn.className.includes("btn-gray") + ? "bg-gray-500 hover:bg-gray-600" + : "bg-green-600 hover:bg-green-700" + }" @click=${() => { btn.action(); if (!btn.preventClose) { @@ -1115,62 +1148,72 @@ export class EventsDisplay extends LitElement implements Layer { )}
` - : ""} + : "" + } `, )} - ${this.incomingAttacks.length > 0 - ? html` + ${ + this.incomingAttacks.length > 0 + ? html`
` - : ""} + : "" + } - ${this.outgoingAttacks.length > 0 - ? html` + ${ + this.outgoingAttacks.length > 0 + ? html` ` - : ""} + : "" + } - ${this.outgoingLandAttacks.length > 0 - ? html` + ${ + this.outgoingLandAttacks.length > 0 + ? html` ` - : ""} + : "" + } - ${this.outgoingBoats.length > 0 - ? html` + ${ + this.outgoingBoats.length > 0 + ? html` ` - : ""} + : "" + } - ${filteredEvents.length === 0 && - this.incomingAttacks.length === 0 && - this.outgoingAttacks.length === 0 && - this.outgoingLandAttacks.length === 0 && - this.outgoingBoats.length === 0 - ? html` + ${ + filteredEvents.length === 0 && + this.incomingAttacks.length === 0 && + this.outgoingAttacks.length === 0 && + this.outgoingLandAttacks.length === 0 && + this.outgoingBoats.length === 0 + ? html` ` - : ""} + : "" + }
${this.renderIncomingAttacks()}
${this.renderOutgoingAttacks()}
${this.renderOutgoingLandAttacks()}
${this.renderBoats()}
- `} + ` + } `; } diff --git a/src/client/graphics/layers/FPSDisplay.ts b/src/client/graphics/layers/FPSDisplay.ts index cb999ed51b..7ebd27ef84 100644 --- a/src/client/graphics/layers/FPSDisplay.ts +++ b/src/client/graphics/layers/FPSDisplay.ts @@ -1,9 +1,9 @@ -import { LitElement, css, html } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators.js"; import { EventBus } from "../../../core/EventBus"; -import { Layer } from "./Layer"; -import { TogglePerformanceOverlayEvent } from "../../InputHandler"; import { UserSettings } from "../../../core/game/UserSettings"; +import { TogglePerformanceOverlayEvent } from "../../InputHandler"; +import { Layer } from "./Layer"; @customElement("fps-display") export class FPSDisplay extends LitElement implements Layer { diff --git a/src/client/graphics/layers/FxLayer.ts b/src/client/graphics/layers/FxLayer.ts index 3fa6a20727..128790c8ed 100644 --- a/src/client/graphics/layers/FxLayer.ts +++ b/src/client/graphics/layers/FxLayer.ts @@ -1,21 +1,21 @@ +import { Theme } from "../../../core/configuration/Config"; +import { UnitType } from "../../../core/game/Game"; import { BonusEventUpdate, ConquestUpdate, GameUpdateType, RailroadUpdate, } from "../../../core/game/GameUpdates"; -import { Fx, FxType } from "../fx/Fx"; import { GameView, UnitView } from "../../../core/game/GameView"; -import { ShockwaveFx, nukeFxFactory } from "../fx/NukeFx"; +import { renderNumber } from "../../Utils"; import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader"; -import { Layer } from "./Layer"; +import { conquestFxFactory } from "../fx/ConquestFx"; +import { Fx, FxType } from "../fx/Fx"; +import { nukeFxFactory, ShockwaveFx } from "../fx/NukeFx"; import { SpriteFx } from "../fx/SpriteFx"; import { TextFx } from "../fx/TextFx"; -import { Theme } from "../../../core/configuration/Config"; import { UnitExplosionFx } from "../fx/UnitExplosionFx"; -import { UnitType } from "../../../core/game/Game"; -import { conquestFxFactory } from "../fx/ConquestFx"; -import { renderNumber } from "../../Utils"; +import { Layer } from "./Layer"; export class FxLayer implements Layer { private canvas: HTMLCanvasElement | undefined; diff --git a/src/client/graphics/layers/GameLeftSidebar.ts b/src/client/graphics/layers/GameLeftSidebar.ts index 11cddc2d18..44ee0ac57b 100644 --- a/src/client/graphics/layers/GameLeftSidebar.ts +++ b/src/client/graphics/layers/GameLeftSidebar.ts @@ -1,14 +1,14 @@ -import { LitElement, html } from "lit"; -import { customElement, state } from "lit/decorators.js"; import { Colord } from "colord"; -import { GameMode } from "../../../core/game/Game"; -import { GameView } from "../../../core/game/GameView"; -import { Layer } from "./Layer"; +import { html, LitElement } from "lit"; +import { customElement, state } from "lit/decorators.js"; import leaderboardRegularIcon from "../../../../resources/images/LeaderboardIconRegularWhite.svg"; import leaderboardSolidIcon from "../../../../resources/images/LeaderboardIconSolidWhite.svg"; import teamRegularIcon from "../../../../resources/images/TeamIconRegularWhite.svg"; import teamSolidIcon from "../../../../resources/images/TeamIconSolidWhite.svg"; +import { GameMode } from "../../../core/game/Game"; +import { GameView } from "../../../core/game/GameView"; import { translateText } from "../../Utils"; +import { Layer } from "./Layer"; @customElement("game-left-sidebar") export class GameLeftSidebar extends LitElement implements Layer { @@ -97,8 +97,9 @@ export class GameLeftSidebar extends LitElement implements Layer { transition-transform duration-300 ease-out transform ${this.isVisible ? "translate-x-0" : "-translate-x-full"}`} > - ${this.isPlayerTeamLabelVisible - ? html` + ${ + this.isPlayerTeamLabelVisible + ? html`
e.preventDefault()} @@ -109,7 +110,8 @@ export class GameLeftSidebar extends LitElement implements Layer {
` - : null} + : null + }
treeIcon
- ${this.isTeamGame - ? html` + ${ + this.isTeamGame + ? html`
treeIcon
` - : null} + : null + }
diff --git a/src/client/graphics/layers/GameRightSidebar.ts b/src/client/graphics/layers/GameRightSidebar.ts index 7c8d1c9b66..03d2e4be72 100644 --- a/src/client/graphics/layers/GameRightSidebar.ts +++ b/src/client/graphics/layers/GameRightSidebar.ts @@ -1,20 +1,20 @@ -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, state } from "lit/decorators.js"; -import { EventBus } from "../../../core/EventBus"; -import { GameType } from "../../../core/game/Game"; -import { GameUpdateType } from "../../../core/game/GameUpdates"; -import { GameView } from "../../../core/game/GameView"; -import { Layer } from "./Layer"; -import { PauseGameEvent } from "../../Transport"; -import { ShowReplayPanelEvent } from "./ReplayPanel"; -import { ShowSettingsModalEvent } from "./SettingsModal"; import exitIcon from "../../../../resources/images/ExitIconWhite.svg"; import pauseIcon from "../../../../resources/images/PauseIconWhite.svg"; import playIcon from "../../../../resources/images/PlayIconWhite.svg"; import replayRegularIcon from "../../../../resources/images/ReplayRegularIconWhite.svg"; import replaySolidIcon from "../../../../resources/images/ReplaySolidIconWhite.svg"; import settingsIcon from "../../../../resources/images/SettingIconWhite.svg"; +import { EventBus } from "../../../core/EventBus"; +import { GameType } from "../../../core/game/Game"; +import { GameUpdateType } from "../../../core/game/GameUpdates"; +import { GameView } from "../../../core/game/GameView"; +import { PauseGameEvent } from "../../Transport"; import { translateText } from "../../Utils"; +import { Layer } from "./Layer"; +import { ShowReplayPanelEvent } from "./ReplayPanel"; +import { ShowSettingsModalEvent } from "./SettingsModal"; @customElement("game-right-sidebar") export class GameRightSidebar extends LitElement implements Layer { diff --git a/src/client/graphics/layers/GutterAdModal.ts b/src/client/graphics/layers/GutterAdModal.ts index 2b9ed89914..596833a160 100644 --- a/src/client/graphics/layers/GutterAdModal.ts +++ b/src/client/graphics/layers/GutterAdModal.ts @@ -1,8 +1,8 @@ -import { EventBus, GameEvent } from "../../../core/EventBus"; -import { LitElement, css, html } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, state } from "lit/decorators.js"; -import { Layer } from "./Layer"; +import { EventBus, GameEvent } from "../../../core/EventBus"; import { getGamesPlayed } from "../../Utils"; +import { Layer } from "./Layer"; export class GutterAdModalEvent implements GameEvent { constructor(public readonly isVisible: boolean) {} @@ -96,12 +96,12 @@ export class GutterAdModal extends LitElement implements Layer { window.ramp.que.push(() => { window.ramp.spaAddAds([ { - type: this.leftAdType, selectorId: this.leftContainerId, + type: this.leftAdType, }, { - type: this.rightAdType, selectorId: this.rightContainerId, + type: this.rightAdType, }, ]); this.adLoaded = true; diff --git a/src/client/graphics/layers/HeadsUpMessage.ts b/src/client/graphics/layers/HeadsUpMessage.ts index 2bf546eb41..c5393712fd 100644 --- a/src/client/graphics/layers/HeadsUpMessage.ts +++ b/src/client/graphics/layers/HeadsUpMessage.ts @@ -1,8 +1,8 @@ -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, state } from "lit/decorators.js"; import { GameView } from "../../../core/game/GameView"; -import { Layer } from "./Layer"; import { translateText } from "../../Utils"; +import { Layer } from "./Layer"; @customElement("heads-up-message") export class HeadsUpMessage extends LitElement implements Layer { diff --git a/src/client/graphics/layers/Leaderboard.ts b/src/client/graphics/layers/Leaderboard.ts index b702a63eda..95737ed61a 100644 --- a/src/client/graphics/layers/Leaderboard.ts +++ b/src/client/graphics/layers/Leaderboard.ts @@ -1,11 +1,11 @@ -import { EventBus, GameEvent } from "../../../core/EventBus"; -import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators.js"; -import { Layer } from "./Layer"; -import { renderNumber } from "../../Utils"; import { repeat } from "lit/directives/repeat.js"; import { translateText } from "../../../client/Utils"; +import { EventBus, GameEvent } from "../../../core/EventBus"; +import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; +import { renderNumber } from "../../Utils"; +import { Layer } from "./Layer"; type Entry = { name: string; @@ -107,15 +107,15 @@ export class Leaderboard extends LitElement implements Layer { troops = 0; } return { + gold: renderNumber(player.gold()), + isMyPlayer: player === myPlayer, name: player.displayName(), + player, position: index + 1, score: formatPercentage( player.numTilesOwned() / numTilesWithoutFallout, ), - gold: renderNumber(player.gold()), troops: renderNumber(troops), - isMyPlayer: player === myPlayer, - player, }; }); @@ -137,15 +137,15 @@ export class Leaderboard extends LitElement implements Layer { } this.players.pop(); this.players.push({ + gold: renderNumber(myPlayer.gold()), + isMyPlayer: true, name: myPlayer.displayName(), + player: myPlayer, position: place, score: formatPercentage( myPlayer.numTilesOwned() / this.game.numLandTiles(), ), - gold: renderNumber(myPlayer.gold()), troops: renderNumber(myPlayerTroops), - isMyPlayer: true, - player: myPlayer, }); } @@ -169,10 +169,9 @@ export class Leaderboard extends LitElement implements Layer { } return html`
e.preventDefault()} >
this.setSort("tiles")} > ${translateText("leaderboard.owned")} - ${this._sortKey === "tiles" - ? this._sortOrder === "asc" - ? "⬆️" - : "⬇️" - : ""} + ${ + this._sortKey === "tiles" + ? this._sortOrder === "asc" + ? "⬆️" + : "⬇️" + : "" + }
this.setSort("gold")} > ${translateText("leaderboard.gold")} - ${this._sortKey === "gold" - ? this._sortOrder === "asc" - ? "⬆️" - : "⬇️" - : ""} + ${ + this._sortKey === "gold" + ? this._sortOrder === "asc" + ? "⬆️" + : "⬇️" + : "" + }
this.setSort("troops")} > ${translateText("leaderboard.troops")} - ${this._sortKey === "troops" - ? this._sortOrder === "asc" - ? "⬆️" - : "⬇️" - : ""} + ${ + this._sortKey === "troops" + ? this._sortOrder === "asc" + ? "⬆️" + : "⬇️" + : "" + }
@@ -226,9 +231,9 @@ export class Leaderboard extends LitElement implements Layer { (p) => p.player.id(), (player) => html`
this.handleRowClickPlayer(player.player)} >
diff --git a/src/client/graphics/layers/MainRadialMenu.ts b/src/client/graphics/layers/MainRadialMenu.ts index 97b9d577c1..0c62ac1c71 100644 --- a/src/client/graphics/layers/MainRadialMenu.ts +++ b/src/client/graphics/layers/MainRadialMenu.ts @@ -1,26 +1,26 @@ -import { - COLORS, - MenuElementParams, - centerButtonElement, - rootMenuElement, -} from "./RadialMenuElements"; +import { LitElement } from "lit"; +import { customElement } from "lit/decorators.js"; +import swordIcon from "../../../../resources/images/SwordIconWhite.svg"; +import { EventBus } from "../../../core/EventBus"; +import { PlayerActions } from "../../../core/game/Game"; +import { TileRef } from "../../../core/game/GameMap"; import { GameView, PlayerView } from "../../../core/game/GameView"; -import { RadialMenu, RadialMenuConfig } from "./RadialMenu"; +import { ContextMenuEvent } from "../../InputHandler"; +import { TransformHandler } from "../TransformHandler"; +import { UIState } from "../UIState"; import { BuildMenu } from "./BuildMenu"; import { ChatIntegration } from "./ChatIntegration"; -import { ContextMenuEvent } from "../../InputHandler"; import { EmojiTable } from "./EmojiTable"; -import { EventBus } from "../../../core/EventBus"; import { Layer } from "./Layer"; -import { LitElement } from "lit"; import { PlayerActionHandler } from "./PlayerActionHandler"; -import { PlayerActions } from "../../../core/game/Game"; import { PlayerPanel } from "./PlayerPanel"; -import { TileRef } from "../../../core/game/GameMap"; -import { TransformHandler } from "../TransformHandler"; -import { UIState } from "../UIState"; -import { customElement } from "lit/decorators.js"; -import swordIcon from "../../../../resources/images/SwordIconWhite.svg"; +import { RadialMenu, RadialMenuConfig } from "./RadialMenu"; +import { + COLORS, + centerButtonElement, + MenuElementParams, + rootMenuElement, +} from "./RadialMenuElements"; @customElement("main-radial-menu") export class MainRadialMenu extends LitElement implements Layer { @@ -90,13 +90,7 @@ export class MainRadialMenu extends LitElement implements Layer { const actions = await myPlayer.actions(tile); // Stale check: user might have clicked somewhere else already if (this.clickedTile !== tile) return; - this.updatePlayerActions( - myPlayer, - actions, - tile, - event.x, - event.y, - ); + this.updatePlayerActions(myPlayer, actions, tile, event.x, event.y); } catch (err) { console.error("Failed to fetch player actions:", err); } @@ -120,18 +114,18 @@ export class MainRadialMenu extends LitElement implements Layer { } const params: MenuElementParams = { - myPlayer, - selected: recipient, - tile, - playerActions: actions, - game: this.game, buildMenu: this.buildMenu, - emojiTable: this.emojiTable, - playerActionHandler: this.playerActionHandler, - playerPanel: this.playerPanel, chatIntegration: this.chatIntegration, closeMenu: () => this.closeMenu(), + emojiTable: this.emojiTable, eventBus: this.eventBus, + game: this.game, + myPlayer, + playerActionHandler: this.playerActionHandler, + playerActions: actions, + playerPanel: this.playerPanel, + selected: recipient, + tile, }; this.radialMenu.setParams(params); @@ -152,11 +146,7 @@ export class MainRadialMenu extends LitElement implements Layer { try { const actions = await myPlayer.actions(tile); if (this.clickedTile !== tile) return; // stale - this.updatePlayerActions( - myPlayer, - actions, - tile, - ); + this.updatePlayerActions(myPlayer, actions, tile); } catch (err) { console.error("Failed to refresh player actions:", err); } diff --git a/src/client/graphics/layers/MultiTabModal.ts b/src/client/graphics/layers/MultiTabModal.ts index 412d0f18e6..ce36c5e3e9 100644 --- a/src/client/graphics/layers/MultiTabModal.ts +++ b/src/client/graphics/layers/MultiTabModal.ts @@ -1,11 +1,11 @@ -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators.js"; import { GameEnv } from "../../../core/configuration/Config"; import { GameType } from "../../../core/game/Game"; import { GameView } from "../../../core/game/GameView"; -import { Layer } from "./Layer"; import { MultiTabDetector } from "../../MultiTabDetector"; import { translateText } from "../../Utils"; +import { Layer } from "./Layer"; @customElement("multi-tab-modal") export class MultiTabModal extends LitElement implements Layer { diff --git a/src/client/graphics/layers/NameLayer.ts b/src/client/graphics/layers/NameLayer.ts index c089e04bd7..aa36036228 100644 --- a/src/client/graphics/layers/NameLayer.ts +++ b/src/client/graphics/layers/NameLayer.ts @@ -1,13 +1,3 @@ -import { AllPlayers, Cell, nukeTypes } from "../../../core/game/Game"; -import { GameView, PlayerView } from "../../../core/game/GameView"; -import { createCanvas, renderNumber, renderTroops } from "../../Utils"; -import { AlternateViewEvent } from "../../InputHandler"; -import { EventBus } from "../../../core/EventBus"; -import { Layer } from "./Layer"; -import { PseudoRandom } from "../../../core/PseudoRandom"; -import { Theme } from "../../../core/configuration/Config"; -import { TransformHandler } from "../TransformHandler"; -import { UserSettings } from "../../../core/game/UserSettings"; import allianceIcon from "../../../../resources/images/AllianceIcon.svg"; import allianceRequestBlackIcon from "../../../../resources/images/AllianceRequestBlackIcon.svg"; import allianceRequestWhiteIcon from "../../../../resources/images/AllianceRequestWhiteIcon.svg"; @@ -17,10 +7,20 @@ import embargoBlackIcon from "../../../../resources/images/EmbargoBlackIcon.svg" import embargoWhiteIcon from "../../../../resources/images/EmbargoWhiteIcon.svg"; import nukeRedIcon from "../../../../resources/images/NukeIconRed.svg"; import nukeWhiteIcon from "../../../../resources/images/NukeIconWhite.svg"; -import { renderPlayerFlag } from "../../../core/CustomFlag"; import shieldIcon from "../../../../resources/images/ShieldIconBlack.svg"; import targetIcon from "../../../../resources/images/TargetIcon.svg"; import traitorIcon from "../../../../resources/images/TraitorIcon.svg"; +import { renderPlayerFlag } from "../../../core/CustomFlag"; +import { Theme } from "../../../core/configuration/Config"; +import { EventBus } from "../../../core/EventBus"; +import { AllPlayers, Cell, nukeTypes } from "../../../core/game/Game"; +import { GameView, PlayerView } from "../../../core/game/GameView"; +import { UserSettings } from "../../../core/game/UserSettings"; +import { PseudoRandom } from "../../../core/PseudoRandom"; +import { AlternateViewEvent } from "../../InputHandler"; +import { createCanvas, renderNumber, renderTroops } from "../../Utils"; +import { TransformHandler } from "../TransformHandler"; +import { Layer } from "./Layer"; class RenderInfo { public icons: Map = new Map(); // Track icon elements @@ -611,8 +611,8 @@ export class NameLayer implements Layer { // Position element with scale if (render.location && render.location !== oldLocation) { const scale = Math.min(baseSize * 0.25, 3); - render.element.style.transform = - `translate(${render.location.x}px, ${render.location.y}px) translate(-50%, -50%) scale(${scale})`; + // eslint-disable-next-line max-len + render.element.style.transform = `translate(${render.location.x}px, ${render.location.y}px) translate(-50%, -50%) scale(${scale})`; } } diff --git a/src/client/graphics/layers/OptionsMenu.ts b/src/client/graphics/layers/OptionsMenu.ts index 130977c689..5f098d5d4c 100644 --- a/src/client/graphics/layers/OptionsMenu.ts +++ b/src/client/graphics/layers/OptionsMenu.ts @@ -1,14 +1,14 @@ -import { AlternateViewEvent, RedrawGraphicsEvent } from "../../InputHandler"; -import { LitElement, html } from "lit"; +import { html, LitElement } from "lit"; import { customElement, state } from "lit/decorators.js"; import { EventBus } from "../../../core/EventBus"; import { GameType } from "../../../core/game/Game"; import { GameUpdateType } from "../../../core/game/GameUpdates"; import { GameView } from "../../../core/game/GameView"; -import { Layer } from "./Layer"; -import { PauseGameEvent } from "../../Transport"; import { UserSettings } from "../../../core/game/UserSettings"; +import { AlternateViewEvent, RedrawGraphicsEvent } from "../../InputHandler"; +import { PauseGameEvent } from "../../Transport"; import { translateText } from "../../Utils"; +import { Layer } from "./Layer"; const button = ({ classes = "", @@ -181,10 +181,10 @@ export class OptionsMenu extends LitElement implements Layer { >
${button({ + children: this.isPaused ? "▶️" : "⏸", classes: !this.showPauseButton ? "hidden" : "", onClick: this.onPauseButtonClick, title: this.isPaused ? "Resume game" : "Pause game", - children: this.isPaused ? "▶️" : "⏸", })}
@@ -209,71 +209,68 @@ export class OptionsMenu extends LitElement implements Layer {
${button({ + children: "🌲: " + (this.alternateView ? "On" : "Off"), onClick: this.onTerrainButtonClick, title: "Toggle Terrain", - children: "🌲: " + (this.alternateView ? "On" : "Off"), })} ${button({ + children: "🙂: " + (this.userSettings.emojis() ? "On" : "Off"), onClick: this.onToggleEmojisButtonClick, title: "Toggle Emojis", - children: "🙂: " + (this.userSettings.emojis() ? "On" : "Off"), })} ${button({ + children: "🚨: " + (this.userSettings.alertFrame() ? "On" : "Off"), onClick: this.onToggleAlertFrameButtonClick, title: "Toggle Alert frame", - children: "🚨: " + (this.userSettings.alertFrame() ? "On" : "Off"), })} ${button({ + children: "💥: " + (this.userSettings.fxLayer() ? "On" : "Off"), onClick: this.onToggleSpecialEffectsButtonClick, title: "Toggle Special effects", - children: "💥: " + (this.userSettings.fxLayer() ? "On" : "Off"), })} ${button({ - onClick: this.onToggleTerritoryPatterns, - title: "Territory Patterns", children: "🏳️: " + (this.userSettings.territoryPatterns() ? "On" : "Off"), + onClick: this.onToggleTerritoryPatterns, + title: "Territory Patterns", })} ${button({ + children: "🌙: " + (this.userSettings.darkMode() ? "On" : "Off"), onClick: this.onToggleDarkModeButtonClick, title: "Dark Mode", - children: "🌙: " + (this.userSettings.darkMode() ? "On" : "Off"), })} ${button({ - onClick: this.onToggleRandomNameModeButtonClick, - title: "Random name mode", children: "🥷: " + (this.userSettings.anonymousNames() ? "On" : "Off"), + onClick: this.onToggleRandomNameModeButtonClick, + title: "Random name mode", })} ${button({ - onClick: this.onToggleLeftClickOpensMenu, - title: "Left click", children: "🖱️: " + (this.userSettings.leftClickOpensMenu() ? "Opens menu" : "Attack"), + onClick: this.onToggleLeftClickOpensMenu, + title: "Left click", })} ${button({ - onClick: this.onTogglePerformanceOverlayButtonClick, - title: "Performance Overlay", children: "🚀: " + (this.userSettings.performanceOverlay() ? "On" : "Off"), + onClick: this.onTogglePerformanceOverlayButtonClick, + title: "Performance Overlay", })}
diff --git a/src/client/graphics/layers/PlayerActionHandler.ts b/src/client/graphics/layers/PlayerActionHandler.ts index 819cf12636..87cf7213a8 100644 --- a/src/client/graphics/layers/PlayerActionHandler.ts +++ b/src/client/graphics/layers/PlayerActionHandler.ts @@ -1,4 +1,7 @@ +import { EventBus } from "../../../core/EventBus"; import { PlayerActions, PlayerID } from "../../../core/game/Game"; +import { TileRef } from "../../../core/game/GameMap"; +import { PlayerView } from "../../../core/game/GameView"; import { SendAllianceRequestIntentEvent, SendAttackIntentEvent, @@ -13,9 +16,6 @@ import { SendSpawnIntentEvent, SendTargetPlayerIntentEvent, } from "../../Transport"; -import { EventBus } from "../../../core/EventBus"; -import { PlayerView } from "../../../core/game/GameView"; -import { TileRef } from "../../../core/game/GameMap"; import { UIState } from "../UIState"; export class PlayerActionHandler { diff --git a/src/client/graphics/layers/PlayerInfoOverlay.ts b/src/client/graphics/layers/PlayerInfoOverlay.ts index 8074f10816..97c17fedd8 100644 --- a/src/client/graphics/layers/PlayerInfoOverlay.ts +++ b/src/client/graphics/layers/PlayerInfoOverlay.ts @@ -1,6 +1,9 @@ -import { ContextMenuEvent, MouseMoveEvent } from "../../InputHandler"; -import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; -import { LitElement, TemplateResult, html } from "lit"; +import { html, LitElement, TemplateResult } from "lit"; +import { customElement, property, state } from "lit/decorators.js"; +import { ref } from "lit-html/directives/ref.js"; +import { translateText } from "../../../client/Utils"; +import { renderPlayerFlag } from "../../../core/CustomFlag"; +import { EventBus } from "../../../core/EventBus"; import { PlayerProfile, PlayerType, @@ -8,16 +11,13 @@ import { Unit, UnitType, } from "../../../core/game/Game"; -import { customElement, property, state } from "lit/decorators.js"; -import { renderNumber, renderTroops } from "../../Utils"; -import { CloseRadialMenuEvent } from "./RadialMenu"; -import { EventBus } from "../../../core/EventBus"; -import { Layer } from "./Layer"; import { TileRef } from "../../../core/game/GameMap"; +import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; +import { ContextMenuEvent, MouseMoveEvent } from "../../InputHandler"; +import { renderNumber, renderTroops } from "../../Utils"; import { TransformHandler } from "../TransformHandler"; -import { ref } from "lit-html/directives/ref.js"; -import { renderPlayerFlag } from "../../../core/CustomFlag"; -import { translateText } from "../../../client/Utils"; +import { Layer } from "./Layer"; +import { CloseRadialMenuEvent } from "./RadialMenu"; function euclideanDistWorld( coord: { x: number; y: number }, @@ -222,17 +222,18 @@ export class PlayerInfoOverlay extends LitElement implements Layer { return html`
- ${this.showDetails - ? html` - ${player.team() !== null - ? html`
+ ${ + this.showDetails + ? html` + ${ + player.team() !== null + ? html`
${translateText("player_info_overlay.team")}: ${player.team()}
` - : ""} + : "" + }
${translateText("player_info_overlay.type")}: ${playerType}
- ${player.troops() >= 1 - ? html`
+ ${ + player.troops() >= 1 + ? html`
${translateText("player_info_overlay.d_troops")}: ${renderTroops(player.troops())}
` - : ""} - ${attackingTroops >= 1 - ? html`
+ : "" + } + ${ + attackingTroops >= 1 + ? html`
${translateText("player_info_overlay.a_troops")}: ${renderTroops(attackingTroops)}
` - : ""} + : "" + }
${translateText("player_info_overlay.gold")}: ${renderNumber(player.gold())} @@ -310,7 +319,8 @@ export class PlayerInfoOverlay extends LitElement implements Layer { )} ${relationHtml} ` - : ""} + : "" + }
`; } @@ -328,14 +338,16 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
${unit.type()}
- ${unit.hasHealth() - ? html` + ${ + unit.hasHealth() + ? html`
${translateText("player_info_overlay.health")}: ${unit.health()}
` - : ""} + : "" + }
`; diff --git a/src/client/graphics/layers/PlayerPanel.ts b/src/client/graphics/layers/PlayerPanel.ts index 0c97996a80..7296bc682f 100644 --- a/src/client/graphics/layers/PlayerPanel.ts +++ b/src/client/graphics/layers/PlayerPanel.ts @@ -1,7 +1,20 @@ +import { html, LitElement } from "lit"; +import { customElement, state } from "lit/decorators.js"; +import allianceIcon from "../../../../resources/images/AllianceIconWhite.svg"; +import chatIcon from "../../../../resources/images/ChatIconWhite.svg"; +import donateGoldIcon from "../../../../resources/images/DonateGoldIconWhite.svg"; +import donateTroopIcon from "../../../../resources/images/DonateTroopIconWhite.svg"; +import emojiIcon from "../../../../resources/images/EmojiIconWhite.svg"; +import targetIcon from "../../../../resources/images/TargetIconWhite.svg"; +import traitorIcon from "../../../../resources/images/TraitorIconWhite.svg"; +import { translateText } from "../../../client/Utils"; +import { EventBus } from "../../../core/EventBus"; import { AllPlayers, PlayerActions } from "../../../core/game/Game"; -import { CloseViewEvent, MouseUpEvent } from "../../InputHandler"; +import { TileRef } from "../../../core/game/GameMap"; import { GameView, PlayerView } from "../../../core/game/GameView"; -import { LitElement, html } from "lit"; +import { flattenedEmojiTable } from "../../../core/Util"; +import Countries from "../../data/countries.json"; +import { CloseViewEvent, MouseUpEvent } from "../../InputHandler"; import { SendAllianceRequestIntentEvent, SendBreakAllianceIntentEvent, @@ -11,24 +24,11 @@ import { SendEmojiIntentEvent, SendTargetPlayerIntentEvent, } from "../../Transport"; -import { customElement, state } from "lit/decorators.js"; import { renderNumber, renderTroops } from "../../Utils"; +import { UIState } from "../UIState"; import { ChatModal } from "./ChatModal"; -import Countries from "../../data/countries.json"; import { EmojiTable } from "./EmojiTable"; -import { EventBus } from "../../../core/EventBus"; import { Layer } from "./Layer"; -import { TileRef } from "../../../core/game/GameMap"; -import { UIState } from "../UIState"; -import allianceIcon from "../../../../resources/images/AllianceIconWhite.svg"; -import chatIcon from "../../../../resources/images/ChatIconWhite.svg"; -import donateGoldIcon from "../../../../resources/images/DonateGoldIconWhite.svg"; -import donateTroopIcon from "../../../../resources/images/DonateTroopIconWhite.svg"; -import emojiIcon from "../../../../resources/images/EmojiIconWhite.svg"; -import { flattenedEmojiTable } from "../../../core/Util"; -import targetIcon from "../../../../resources/images/TargetIconWhite.svg"; -import traitorIcon from "../../../../resources/images/TraitorIconWhite.svg"; -import { translateText } from "../../../client/Utils"; @customElement("player-panel") export class PlayerPanel extends LitElement implements Layer { @@ -195,10 +195,7 @@ export class PlayerPanel extends LitElement implements Layer { const remainingTicks = expiresAt - this.g.ticks(); if (remainingTicks > 0) { - const remainingSeconds = Math.max( - 0, - Math.floor(remainingTicks / 10), - ); // 10 ticks per second + const remainingSeconds = Math.max(0, Math.floor(remainingTicks / 10)); // 10 ticks per second this.allianceExpiryText = this.formatDuration(remainingSeconds); } } else { @@ -246,7 +243,10 @@ export class PlayerPanel extends LitElement implements Layer { //flag icon in the playerPanel const flagCode = other.cosmetics.flag; - const country = typeof flagCode === "string" ? Countries.find((c) => c.code === flagCode) : undefined; + const country = + typeof flagCode === "string" + ? Countries.find((c) => c.code === flagCode) + : undefined; const flagName = country?.name; return html` @@ -283,8 +283,9 @@ export class PlayerPanel extends LitElement implements Layer {
- ${country - ? html` + ${ + country + ? html`
${translateText("player_panel.flag")} @@ -298,7 +299,8 @@ export class PlayerPanel extends LitElement implements Layer {
` - : ""} + : "" + }
@@ -333,9 +335,11 @@ export class PlayerPanel extends LitElement implements Layer { ${translateText("player_panel.traitor")}
- ${other.isTraitor() - ? translateText("player_panel.yes") - : translateText("player_panel.no")} + ${ + other.isTraitor() + ? translateText("player_panel.yes") + : translateText("player_panel.no") + }
@@ -355,9 +359,11 @@ export class PlayerPanel extends LitElement implements Layer { ${translateText("player_panel.embargo")}
- ${other.hasEmbargoAgainst(myPlayer) - ? translateText("player_panel.yes") - : translateText("player_panel.no")} + ${ + other.hasEmbargoAgainst(myPlayer) + ? translateText("player_panel.yes") + : translateText("player_panel.no") + }
@@ -371,17 +377,20 @@ export class PlayerPanel extends LitElement implements Layer { class="bg-opacity-50 bg-gray-700 rounded p-2 text-white max-w-72 max-h-20 overflow-y-auto" translate="no" > - ${other.allies().length > 0 - ? other - .allies() - .map((p) => p.name()) - .join(", ") - : translateText("player_panel.none")} + ${ + other.allies().length > 0 + ? other + .allies() + .map((p) => p.name()) + .join(", ") + : translateText("player_panel.none") + }
- ${this.allianceExpiryText !== null - ? html` + ${ + this.allianceExpiryText !== null + ? html`
${translateText("player_panel.alliance_time_remaining")} @@ -393,7 +402,8 @@ export class PlayerPanel extends LitElement implements Layer {
` - : ""} + : "" + }
@@ -406,8 +416,9 @@ export class PlayerPanel extends LitElement implements Layer { > Target - ${canTarget - ? html`` - : ""} - ${canBreakAlliance - ? html`` - : ""} - ${canSendAllianceRequest - ? html`` - : ""} - ${canDonateTroops - ? html`` - : ""} - ${canDonateGold - ? html`` - : ""} - ${canSendEmoji - ? html`` - : ""} + : "" + }
- ${canEmbargo && other !== myPlayer - ? html`` - : ""} - ${!canEmbargo && other !== myPlayer - ? html`` - : ""} + : "" + }
diff --git a/src/client/graphics/layers/RadialMenu.ts b/src/client/graphics/layers/RadialMenu.ts index d9740d5ba6..5d7c305055 100644 --- a/src/client/graphics/layers/RadialMenu.ts +++ b/src/client/graphics/layers/RadialMenu.ts @@ -1,16 +1,16 @@ /* eslint-disable max-lines */ import * as d3 from "d3"; +import backIcon from "../../../../resources/images/BackIconWhite.svg"; +import { EventBus, GameEvent } from "../../../core/EventBus"; +import { CloseViewEvent } from "../../InputHandler"; +import { translateText } from "../../Utils"; +import { Layer } from "./Layer"; import { CenterButtonElement, MenuElement, MenuElementParams, TooltipKey, } from "./RadialMenuElements"; -import { EventBus, GameEvent } from "../../../core/EventBus"; -import { CloseViewEvent } from "../../InputHandler"; -import { Layer } from "./Layer"; -import backIcon from "../../../../resources/images/BackIconWhite.svg"; -import { translateText } from "../../Utils"; export class CloseRadialMenuEvent implements GameEvent { constructor() {} @@ -41,7 +41,9 @@ type CenterButtonState = "default" | "back"; type RequiredRadialMenuConfig = Required; export class RadialMenu implements Layer { - private menuElement: d3.Selection | undefined; + private menuElement: + | d3.Selection + | undefined; private tooltipElement: HTMLDivElement | null = null; private isVisible = false; @@ -89,17 +91,17 @@ export class RadialMenu implements Layer { config: RadialMenuConfig = {}, ) { this.config = { - menuSize: config.menuSize ?? 190, - submenuScale: config.submenuScale ?? 1.5, + centerButtonIcon: config.centerButtonIcon ?? "", centerButtonSize: config.centerButtonSize ?? 30, - iconSize: config.iconSize ?? 32, centerIconSize: config.centerIconSize ?? 48, disabledColor: config.disabledColor ?? d3.rgb(128, 128, 128).toString(), - menuTransitionDuration: config.menuTransitionDuration ?? 300, + iconSize: config.iconSize ?? 32, + innerRadiusIncrement: config.innerRadiusIncrement ?? 20, mainMenuInnerRadius: config.mainMenuInnerRadius ?? 40, - centerButtonIcon: config.centerButtonIcon ?? "", maxNestedLevels: config.maxNestedLevels ?? 3, - innerRadiusIncrement: config.innerRadiusIncrement ?? 20, + menuSize: config.menuSize ?? 190, + menuTransitionDuration: config.menuTransitionDuration ?? 300, + submenuScale: config.submenuScale ?? 1.5, tooltipStyle: config.tooltipStyle ?? "", }; this.originalCenterButtonIcon = this.config.centerButtonIcon; @@ -150,9 +152,12 @@ export class RadialMenu implements Layer { .style("position", "absolute") .style("top", "50%") .style("left", "50%") - .style("transition", `top ${ - this.config.menuTransitionDuration}ms ease, left ${ - this.config.menuTransitionDuration}ms ease`) + .style( + "transition", + `top ${this.config.menuTransitionDuration}ms ease, left ${ + this.config.menuTransitionDuration + }ms ease`, + ) .style("transform", "translate(-50%, -50%)") .style("pointer-events", "all") .on("click", (event) => this.hideRadialMenu()); @@ -392,9 +397,10 @@ export class RadialMenu implements Layer { >, level: number, ) { - const onHover = (d: d3.PieArcDatum, path: d3.Selection< - d3.BaseType, unknown, HTMLElement, unknown - >) => { + const onHover = ( + d: d3.PieArcDatum, + path: d3.Selection, + ) => { const disabled = this.params === null || d.data.disabled(this.params); if (d.data.tooltipItems && d.data.tooltipItems.length > 0) { this.showTooltip(d.data.tooltipItems); @@ -413,9 +419,10 @@ export class RadialMenu implements Layer { path.attr("stroke-width", "3"); }; - const onMouseOut = (d: d3.PieArcDatum, path: d3.Selection< - d3.BaseType, unknown, HTMLElement, unknown - >) => { + const onMouseOut = ( + d: d3.PieArcDatum, + path: d3.Selection, + ) => { const disabled = this.params === null || d.data.disabled(this.params); if (this.submenuHoverTimeout !== null) { window.clearTimeout(this.submenuHoverTimeout); @@ -1072,14 +1079,18 @@ export class RadialMenu implements Layer { const vh = window.innerHeight; // If the menu cannot fully fit on an axis, pin it to the viewport center on that axis. - const clampedX = 2 * margin > vw ? vw / 2 : Math.min(Math.max(this.anchorX, margin), vw - margin); - const clampedY = 2 * margin > vh ? vh / 2 : Math.min(Math.max(this.anchorY, margin), vh - margin); + const clampedX = + 2 * margin > vw + ? vw / 2 + : Math.min(Math.max(this.anchorX, margin), vw - margin); + const clampedY = + 2 * margin > vh + ? vh / 2 + : Math.min(Math.max(this.anchorY, margin), vh - margin); if (this.menuElement === undefined) throw new Error("Not initialized"); const svgSel = this.menuElement.select("svg"); - svgSel - .style("top", `${clampedY}px`) - .style("left", `${clampedX}px`); + svgSel.style("top", `${clampedY}px`).style("left", `${clampedX}px`); } private readonly handleResize = () => { diff --git a/src/client/graphics/layers/RadialMenuElements.ts b/src/client/graphics/layers/RadialMenuElements.ts index 2db9e4f97f..a63da03826 100644 --- a/src/client/graphics/layers/RadialMenuElements.ts +++ b/src/client/graphics/layers/RadialMenuElements.ts @@ -1,15 +1,3 @@ -import { AllPlayers, PlayerActions, UnitType } from "../../../core/game/Game"; -import { BuildItemDisplay, BuildMenu, flattenedBuildTable } from "./BuildMenu"; -import { GameView, PlayerView } from "../../../core/game/GameView"; -import { renderNumber, translateText } from "../../Utils"; -import { ChatIntegration } from "./ChatIntegration"; -import { Config } from "../../../core/configuration/Config"; -import { EmojiTable } from "./EmojiTable"; -import { EventBus } from "../../../core/EventBus"; -import { PlayerActionHandler } from "./PlayerActionHandler"; -import { PlayerPanel } from "./PlayerPanel"; -import { TileRef } from "../../../core/game/GameMap"; -import { TooltipItem } from "./RadialMenu"; import allianceIcon from "../../../../resources/images/AllianceIconWhite.svg"; import boatIcon from "../../../../resources/images/BoatIconWhite.svg"; import buildIcon from "../../../../resources/images/BuildIconWhite.svg"; @@ -17,12 +5,24 @@ import chatIcon from "../../../../resources/images/ChatIconWhite.svg"; import donateGoldIcon from "../../../../resources/images/DonateGoldIconWhite.svg"; import donateTroopIcon from "../../../../resources/images/DonateTroopIconWhite.svg"; import emojiIcon from "../../../../resources/images/EmojiIconWhite.svg"; -import { flattenedEmojiTable } from "../../../core/Util"; import infoIcon from "../../../../resources/images/InfoIcon.svg"; import swordIcon from "../../../../resources/images/SwordIconWhite.svg"; import targetIcon from "../../../../resources/images/TargetIconWhite.svg"; import traitorIcon from "../../../../resources/images/TraitorIconWhite.svg"; import xIcon from "../../../../resources/images/XIcon.svg"; +import { Config } from "../../../core/configuration/Config"; +import { EventBus } from "../../../core/EventBus"; +import { AllPlayers, PlayerActions, UnitType } from "../../../core/game/Game"; +import { TileRef } from "../../../core/game/GameMap"; +import { GameView, PlayerView } from "../../../core/game/GameView"; +import { flattenedEmojiTable } from "../../../core/Util"; +import { renderNumber, translateText } from "../../Utils"; +import { BuildItemDisplay, BuildMenu, flattenedBuildTable } from "./BuildMenu"; +import { ChatIntegration } from "./ChatIntegration"; +import { EmojiTable } from "./EmojiTable"; +import { PlayerActionHandler } from "./PlayerActionHandler"; +import { PlayerPanel } from "./PlayerPanel"; +import { TooltipItem } from "./RadialMenu"; export type MenuElementParams = { myPlayer: PlayerView; @@ -67,32 +67,32 @@ export type CenterButtonElement = { }; export const COLORS = { - build: "#ebe250", - building: "#2c2c2c", - boat: "#3f6ab1", ally: "#53ac75", + attack: "#ff0000", + boat: "#3f6ab1", breakAlly: "#c74848", + build: "#ebe250", + building: "#2c2c2c", + chat: { + attack: "#f44336", + default: "#66c", + defend: "#2196f3", + greet: "#ff9800", + help: "#4caf50", + misc: "#9c27b0", + warnings: "#e3c532", + }, delete: "#ff0000", + embargo: "#6600cc", info: "#64748B", - target: "#ff0000", - attack: "#ff0000", infoDetails: "#7f8c8d", infoEmoji: "#f1c40f", - trade: "#008080", - embargo: "#6600cc", + target: "#ff0000", tooltip: { cost: "#ffd700", count: "#aaa", }, - chat: { - default: "#66c", - help: "#4caf50", - attack: "#f44336", - defend: "#2196f3", - greet: "#ff9800", - misc: "#9c27b0", - warnings: "#e3c532", - }, + trade: "#008080", }; export enum Slot { @@ -107,11 +107,11 @@ export enum Slot { /* eslint-disable @typescript-eslint/no-non-null-assertion */ const infoChatElement: MenuElement = { - id: "info_chat", - name: "chat", - disabled: () => false, color: COLORS.chat.default, + disabled: () => false, icon: chatIcon, + id: "info_chat", + name: "chat", subMenu: (params: MenuElementParams) => params.chatIntegration .createQuickChatMenu(params.selected!) @@ -124,59 +124,51 @@ const infoChatElement: MenuElement = { }; const allyTargetElement: MenuElement = { - id: "ally_target", - name: "target", + action: (params: MenuElementParams) => { + params.playerActionHandler.handleTargetPlayer(params.selected!.id()); + params.closeMenu(); + }, + color: COLORS.target, disabled: (params: MenuElementParams): boolean => { if (params.selected === null) return true; return !params.playerActions.interaction?.canTarget; }, - color: COLORS.target, icon: targetIcon, - action: (params: MenuElementParams) => { - params.playerActionHandler.handleTargetPlayer(params.selected!.id()); - params.closeMenu(); - }, + id: "ally_target", + name: "target", }; const allyTradeElement: MenuElement = { - id: "ally_trade", - name: "trade", + action: (params: MenuElementParams) => { + params.playerActionHandler.handleEmbargo(params.selected!, "stop"); + params.closeMenu(); + }, + color: COLORS.trade, disabled: (params: MenuElementParams) => !!params.playerActions?.interaction?.canEmbargo, displayed: (params: MenuElementParams) => !params.playerActions?.interaction?.canEmbargo, - color: COLORS.trade, + id: "ally_trade", + name: "trade", text: translateText("player_panel.start_trade"), - action: (params: MenuElementParams) => { - params.playerActionHandler.handleEmbargo(params.selected!, "stop"); - params.closeMenu(); - }, }; const allyEmbargoElement: MenuElement = { - id: "ally_embargo", - name: "embargo", + action: (params: MenuElementParams) => { + params.playerActionHandler.handleEmbargo(params.selected!, "start"); + params.closeMenu(); + }, + color: COLORS.embargo, disabled: (params: MenuElementParams) => !params.playerActions?.interaction?.canEmbargo, displayed: (params: MenuElementParams) => !!params.playerActions?.interaction?.canEmbargo, - color: COLORS.embargo, + id: "ally_embargo", + name: "embargo", text: translateText("player_panel.stop_trade"), - action: (params: MenuElementParams) => { - params.playerActionHandler.handleEmbargo(params.selected!, "start"); - params.closeMenu(); - }, }; const allyRequestElement: MenuElement = { - id: "ally_request", - name: "request", - disabled: (params: MenuElementParams) => - !params.playerActions?.interaction?.canSendAllianceRequest, - displayed: (params: MenuElementParams) => - !params.playerActions?.interaction?.canBreakAlliance, - color: COLORS.ally, - icon: allianceIcon, action: (params: MenuElementParams) => { params.playerActionHandler.handleAllianceRequest( params.myPlayer, @@ -184,17 +176,17 @@ const allyRequestElement: MenuElement = { ); params.closeMenu(); }, + color: COLORS.ally, + disabled: (params: MenuElementParams) => + !params.playerActions?.interaction?.canSendAllianceRequest, + displayed: (params: MenuElementParams) => + !params.playerActions?.interaction?.canBreakAlliance, + icon: allianceIcon, + id: "ally_request", + name: "request", }; const allyBreakElement: MenuElement = { - id: "ally_break", - name: "break", - disabled: (params: MenuElementParams) => - !params.playerActions?.interaction?.canBreakAlliance, - displayed: (params: MenuElementParams) => - !!params.playerActions?.interaction?.canBreakAlliance, - color: COLORS.breakAlly, - icon: traitorIcon, action: (params: MenuElementParams) => { params.playerActionHandler.handleBreakAlliance( params.myPlayer, @@ -202,59 +194,62 @@ const allyBreakElement: MenuElement = { ); params.closeMenu(); }, + color: COLORS.breakAlly, + disabled: (params: MenuElementParams) => + !params.playerActions?.interaction?.canBreakAlliance, + displayed: (params: MenuElementParams) => + !!params.playerActions?.interaction?.canBreakAlliance, + icon: traitorIcon, + id: "ally_break", + name: "break", }; const allyDonateGoldElement: MenuElement = { - id: "ally_donate_gold", - name: "donate gold", - disabled: (params: MenuElementParams) => - !params.playerActions?.interaction?.canDonateGold, - color: COLORS.ally, - icon: donateGoldIcon, action: (params: MenuElementParams) => { params.playerActionHandler.handleDonateGold(params.selected!); params.closeMenu(); }, + color: COLORS.ally, + disabled: (params: MenuElementParams) => + !params.playerActions?.interaction?.canDonateGold, + icon: donateGoldIcon, + id: "ally_donate_gold", + name: "donate gold", }; const allyDonateTroopsElement: MenuElement = { - id: "ally_donate_troops", - name: "donate troops", - disabled: (params: MenuElementParams) => - !params.playerActions?.interaction?.canDonateTroops, - color: COLORS.ally, - icon: donateTroopIcon, action: (params: MenuElementParams) => { params.playerActionHandler.handleDonateTroops(params.selected!); params.closeMenu(); }, + color: COLORS.ally, + disabled: (params: MenuElementParams) => + !params.playerActions?.interaction?.canDonateTroops, + icon: donateTroopIcon, + id: "ally_donate_troops", + name: "donate troops", }; const infoPlayerElement: MenuElement = { - id: "info_player", - name: "player", - disabled: () => false, - color: COLORS.info, - icon: infoIcon, action: (params: MenuElementParams) => { params.playerPanel.show(params.playerActions, params.tile); }, + color: COLORS.info, + disabled: () => false, + icon: infoIcon, + id: "info_player", + name: "player", }; const infoEmojiElement: MenuElement = { - id: "info_emoji", - name: "emoji", - disabled: () => false, color: COLORS.infoEmoji, + disabled: () => false, icon: emojiIcon, + id: "info_emoji", + name: "emoji", subMenu: (params: MenuElementParams) => { const emojiElements: MenuElement[] = [ { - id: "emoji_more", - name: "more", - disabled: () => false, - color: COLORS.infoEmoji, - icon: emojiIcon, action: (params: MenuElementParams) => { params.emojiTable.showTable((emoji) => { const targetPlayer = @@ -268,17 +263,17 @@ const infoEmojiElement: MenuElement = { params.emojiTable.hideTable(); }); }, + color: COLORS.infoEmoji, + disabled: () => false, + icon: emojiIcon, + id: "emoji_more", + name: "more", }, ]; const emojiCount = 8; for (let i = 0; i < emojiCount; i++) { emojiElements.push({ - id: `emoji_${i}`, - name: flattenedEmojiTable[i], - text: flattenedEmojiTable[i], - disabled: () => false, - fontSize: "25px", action: (params: MenuElementParams) => { const targetPlayer = params.selected === params.game.myPlayer() @@ -287,6 +282,11 @@ const infoEmojiElement: MenuElement = { params.playerActionHandler.handleEmoji(targetPlayer!, i); params.closeMenu(); }, + disabled: () => false, + fontSize: "25px", + id: `emoji_${i}`, + name: flattenedEmojiTable[i], + text: flattenedEmojiTable[i], }); } @@ -296,15 +296,15 @@ const infoEmojiElement: MenuElement = { /* eslint-enable @typescript-eslint/no-non-null-assertion */ export const infoMenuElement: MenuElement = { - id: Slot.Info, - name: "info", - disabled: (params: MenuElementParams) => - !params.selected || params.game.inSpawnPhase(), - icon: infoIcon, - color: COLORS.info, action: (params: MenuElementParams) => { params.playerPanel.show(params.playerActions, params.tile); }, + color: COLORS.info, + disabled: (params: MenuElementParams) => + !params.selected || params.game.inSpawnPhase(), + icon: infoIcon, + id: Slot.Info, + name: "info", }; function getAllEnabledUnits(myPlayer: boolean, config: Config): Set { @@ -359,55 +359,55 @@ function createMenuElements( : !ATTACK_UNIT_TYPES.includes(item.unitType)), ) .map((item: BuildItemDisplay) => ({ - id: `${elementIdPrefix}_${item.unitType}`, - name: item.key - ? item.key.replace("unit_type.", "") - : item.unitType.toString(), - disabled: (params: MenuElementParams) => - !params.buildMenu.canBuildOrUpgrade(item), + action: (params: MenuElementParams) => { + const buildableUnit = params.playerActions.buildableUnits.find( + (bu) => bu.type === item.unitType, + ); + if (buildableUnit === undefined) { + return; + } + if (params.buildMenu.canBuildOrUpgrade(item)) { + params.buildMenu.sendBuildOrUpgrade(buildableUnit, params.tile); + } + params.closeMenu(); + }, color: params.buildMenu.canBuildOrUpgrade(item) ? filterType === "attack" ? COLORS.attack : COLORS.building : undefined, + disabled: (params: MenuElementParams) => + !params.buildMenu.canBuildOrUpgrade(item), icon: item.icon, + id: `${elementIdPrefix}_${item.unitType}`, + name: item.key + ? item.key.replace("unit_type.", "") + : item.unitType.toString(), tooltipItems: [ - { text: translateText(item.key ?? ""), className: "title" }, + { className: "title", text: translateText(item.key ?? "") }, { - text: translateText(item.description ?? ""), className: "description", + text: translateText(item.description ?? ""), }, { - text: `${renderNumber(params.buildMenu.cost(item))} ${translateText("player_panel.gold")}`, className: "cost", + text: `${renderNumber(params.buildMenu.cost(item))} ${translateText("player_panel.gold")}`, }, item.countable - ? { text: `${params.buildMenu.count(item)}x`, className: "count" } + ? { className: "count", text: `${params.buildMenu.count(item)}x` } : null, ].filter( (tooltipItem): tooltipItem is TooltipItem => tooltipItem !== null, ), - action: (params: MenuElementParams) => { - const buildableUnit = params.playerActions.buildableUnits.find( - (bu) => bu.type === item.unitType, - ); - if (buildableUnit === undefined) { - return; - } - if (params.buildMenu.canBuildOrUpgrade(item)) { - params.buildMenu.sendBuildOrUpgrade(buildableUnit, params.tile); - } - params.closeMenu(); - }, })); } export const attackMenuElement: MenuElement = { - id: Slot.Attack, - name: "radial_attack", + color: COLORS.attack, disabled: (params: MenuElementParams) => params.game.inSpawnPhase(), icon: swordIcon, - color: COLORS.attack, + id: Slot.Attack, + name: "radial_attack", subMenu: (params: MenuElementParams) => { if (params === undefined) return []; @@ -416,8 +416,29 @@ export const attackMenuElement: MenuElement = { }; export const deleteUnitElement: MenuElement = { - id: Slot.Delete, - name: "delete", + action: (params: MenuElementParams) => { + const DELETE_SELECTION_RADIUS = 5; + const myUnits = params.myPlayer + .units() + .filter( + (unit) => + params.game.manhattanDist(unit.tile(), params.tile) <= + DELETE_SELECTION_RADIUS, + ); + + if (myUnits.length > 0) { + myUnits.sort( + (a, b) => + params.game.manhattanDist(a.tile(), params.tile) - + params.game.manhattanDist(b.tile(), params.tile), + ); + + params.playerActionHandler.handleDeleteUnit(myUnits[0].id()); + } + + params.closeMenu(); + }, + color: COLORS.delete, disabled: (params: MenuElementParams) => { const tileOwner = params.game.owner(params.tile); const isLand = params.game.isLand(params.tile); @@ -450,47 +471,26 @@ export const deleteUnitElement: MenuElement = { return myUnits.length === 0; }, icon: xIcon, - color: COLORS.delete, + id: Slot.Delete, + name: "delete", tooltipKeys: [ { - key: "radial_menu.delete_unit_title", className: "title", + key: "radial_menu.delete_unit_title", }, { - key: "radial_menu.delete_unit_description", className: "description", + key: "radial_menu.delete_unit_description", }, ], - action: (params: MenuElementParams) => { - const DELETE_SELECTION_RADIUS = 5; - const myUnits = params.myPlayer - .units() - .filter( - (unit) => - params.game.manhattanDist(unit.tile(), params.tile) <= - DELETE_SELECTION_RADIUS, - ); - - if (myUnits.length > 0) { - myUnits.sort( - (a, b) => - params.game.manhattanDist(a.tile(), params.tile) - - params.game.manhattanDist(b.tile(), params.tile), - ); - - params.playerActionHandler.handleDeleteUnit(myUnits[0].id()); - } - - params.closeMenu(); - }, }; export const buildMenuElement: MenuElement = { - id: Slot.Build, - name: "build", + color: COLORS.build, disabled: (params: MenuElementParams) => params.game.inSpawnPhase(), icon: buildIcon, - color: COLORS.build, + id: Slot.Build, + name: "build", subMenu: (params: MenuElementParams) => { if (params === undefined) return []; @@ -499,15 +499,6 @@ export const buildMenuElement: MenuElement = { }; export const boatMenuElement: MenuElement = { - id: Slot.Boat, - name: "boat", - disabled: (params: MenuElementParams) => - !params.playerActions.buildableUnits.some( - (unit) => unit.type === UnitType.TransportShip && unit.canBuild, - ), - icon: boatIcon, - color: COLORS.boat, - action: async (params: MenuElementParams) => { const spawn = await params.playerActionHandler.findBestTransportShipSpawn( params.myPlayer, @@ -523,9 +514,28 @@ export const boatMenuElement: MenuElement = { params.closeMenu(); }, + color: COLORS.boat, + disabled: (params: MenuElementParams) => + !params.playerActions.buildableUnits.some( + (unit) => unit.type === UnitType.TransportShip && unit.canBuild, + ), + icon: boatIcon, + id: Slot.Boat, + name: "boat", }; export const centerButtonElement: CenterButtonElement = { + action: (params: MenuElementParams) => { + if (params.game.inSpawnPhase()) { + params.playerActionHandler.handleSpawn(params.tile); + } else { + params.playerActionHandler.handleAttack( + params.myPlayer, + params.selected?.id() ?? null, + ); + } + params.closeMenu(); + }, disabled: (params: MenuElementParams): boolean => { const tileOwner = params.game.owner(params.tile); const isLand = params.game.isLand(params.tile); @@ -540,25 +550,14 @@ export const centerButtonElement: CenterButtonElement = { } return !params.playerActions.canAttack; }, - action: (params: MenuElementParams) => { - if (params.game.inSpawnPhase()) { - params.playerActionHandler.handleSpawn(params.tile); - } else { - params.playerActionHandler.handleAttack( - params.myPlayer, - params.selected?.id() ?? null, - ); - } - params.closeMenu(); - }, }; export const rootMenuElement: MenuElement = { - id: "root", - name: "root", + color: COLORS.info, disabled: () => false, icon: infoIcon, - color: COLORS.info, + id: "root", + name: "root", subMenu: (params: MenuElementParams) => { let ally = allyRequestElement; if (params.selected?.isAlliedWith(params.myPlayer)) { diff --git a/src/client/graphics/layers/RailroadLayer.ts b/src/client/graphics/layers/RailroadLayer.ts index d69494a368..e0be4d7d4c 100644 --- a/src/client/graphics/layers/RailroadLayer.ts +++ b/src/client/graphics/layers/RailroadLayer.ts @@ -1,15 +1,15 @@ +import { Colord } from "colord"; +import { Theme } from "../../../core/configuration/Config"; +import { PlayerID } from "../../../core/game/Game"; +import { TileRef } from "../../../core/game/GameMap"; import { GameUpdateType, + RailroadUpdate, RailTile, RailType, - RailroadUpdate, } from "../../../core/game/GameUpdates"; -import { Colord } from "colord"; import { GameView } from "../../../core/game/GameView"; import { Layer } from "./Layer"; -import { PlayerID } from "../../../core/game/Game"; -import { Theme } from "../../../core/configuration/Config"; -import { TileRef } from "../../../core/game/GameMap"; import { getRailroadRects } from "./RailroadSprites"; type RailRef = { @@ -123,9 +123,9 @@ export class RailroadLayer implements Layer { railTile.lastOwnerId = currentOwner; } else { this.existingRailroads.set(railRoad.tile, { - tile: railRoad, - numOccurence: 1, lastOwnerId: currentOwner, + numOccurence: 1, + tile: railRoad, }); this.railTileList.push(railRoad.tile); this.paintRail(railRoad); @@ -156,7 +156,7 @@ export class RailroadLayer implements Layer { const recipient = owner.isPlayer() ? owner : null; const color = recipient ? this.theme.railroadColor(recipient) - : new Colord({ r: 255, g: 255, b: 255, a: 1 }); + : new Colord({ a: 1, b: 255, g: 255, r: 255 }); if (this.context === undefined) throw new Error("Not initialized"); this.context.fillStyle = color.toRgbString(); this.paintRailRects(x, y, railRoad.railType); diff --git a/src/client/graphics/layers/ReplayPanel.ts b/src/client/graphics/layers/ReplayPanel.ts index fc8ca7700a..b0963de89e 100644 --- a/src/client/graphics/layers/ReplayPanel.ts +++ b/src/client/graphics/layers/ReplayPanel.ts @@ -1,14 +1,14 @@ -import { LitElement, html } from "lit"; -import { - ReplaySpeedMultiplier, - defaultReplaySpeedMultiplier, -} from "../../utilities/ReplaySpeedMultiplier"; +import { html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators.js"; import { EventBus } from "../../../core/EventBus"; import { GameView } from "../../../core/game/GameView"; -import { Layer } from "./Layer"; import { ReplaySpeedChangeEvent } from "../../InputHandler"; import { translateText } from "../../Utils"; +import { + defaultReplaySpeedMultiplier, + ReplaySpeedMultiplier, +} from "../../utilities/ReplaySpeedMultiplier"; +import { Layer } from "./Layer"; export class ShowReplayPanelEvent { constructor( @@ -71,9 +71,11 @@ export class ReplayPanel extends LitElement implements Layer { @contextmenu=${(e: Event) => e.preventDefault()} >
${this.renderSpeedButton(ReplaySpeedMultiplier.slow, "×0.5")} @@ -92,9 +94,9 @@ export class ReplayPanel extends LitElement implements Layer { const isActive = this._replaySpeedMultiplier === value; return html`
- ${this.alternateView - ? translateText("user_setting.terrain_enabled") - : translateText("user_setting.terrain_disabled")} + ${ + this.alternateView + ? translateText("user_setting.terrain_enabled") + : translateText("user_setting.terrain_disabled") + }
- ${this.alternateView - ? translateText("user_setting.on") - : translateText("user_setting.off")} + ${ + this.alternateView + ? translateText("user_setting.on") + : translateText("user_setting.off") + }
@@ -222,15 +226,19 @@ export class SettingsModal extends LitElement implements Layer { ${translateText("user_setting.emojis_label")}
- ${this.userSettings?.emojis() - ? translateText("user_setting.emojis_visible") - : translateText("user_setting.emojis_hidden")} + ${ + this.userSettings?.emojis() + ? translateText("user_setting.emojis_visible") + : translateText("user_setting.emojis_hidden") + }
- ${this.userSettings?.emojis() - ? translateText("user_setting.on") - : translateText("user_setting.off")} + ${ + this.userSettings?.emojis() + ? translateText("user_setting.on") + : translateText("user_setting.off") + }
@@ -250,15 +258,19 @@ export class SettingsModal extends LitElement implements Layer { ${translateText("user_setting.dark_mode_label")}
- ${this.userSettings?.darkMode() - ? translateText("user_setting.dark_mode_enabled") - : translateText("user_setting.light_mode_enabled")} + ${ + this.userSettings?.darkMode() + ? translateText("user_setting.dark_mode_enabled") + : translateText("user_setting.light_mode_enabled") + }
- ${this.userSettings?.darkMode() - ? translateText("user_setting.on") - : translateText("user_setting.off")} + ${ + this.userSettings?.darkMode() + ? translateText("user_setting.on") + : translateText("user_setting.off") + }
@@ -278,15 +290,19 @@ export class SettingsModal extends LitElement implements Layer { ${translateText("user_setting.special_effects_label")}
- ${this.userSettings?.fxLayer() - ? translateText("user_setting.special_effects_enabled") - : translateText("user_setting.special_effects_disabled")} + ${ + this.userSettings?.fxLayer() + ? translateText("user_setting.special_effects_enabled") + : translateText("user_setting.special_effects_disabled") + }
- ${this.userSettings?.fxLayer() - ? translateText("user_setting.on") - : translateText("user_setting.off")} + ${ + this.userSettings?.fxLayer() + ? translateText("user_setting.on") + : translateText("user_setting.off") + }
@@ -306,15 +322,19 @@ export class SettingsModal extends LitElement implements Layer { ${translateText("user_setting.structure_sprites_label")}
- ${this.userSettings?.structureSprites() - ? translateText("user_setting.structure_sprites_enabled") - : translateText("user_setting.structure_sprites_disabled")} + ${ + this.userSettings?.structureSprites() + ? translateText("user_setting.structure_sprites_enabled") + : translateText("user_setting.structure_sprites_disabled") + }
- ${this.userSettings?.structureSprites() - ? translateText("user_setting.on") - : translateText("user_setting.off")} + ${ + this.userSettings?.structureSprites() + ? translateText("user_setting.on") + : translateText("user_setting.off") + }
@@ -329,15 +349,19 @@ export class SettingsModal extends LitElement implements Layer { ${translateText("user_setting.anonymous_names_label")}
- ${this.userSettings?.anonymousNames() - ? translateText("user_setting.anonymous_names_enabled") - : translateText("user_setting.real_names_shown")} + ${ + this.userSettings?.anonymousNames() + ? translateText("user_setting.anonymous_names_enabled") + : translateText("user_setting.real_names_shown") + }
- ${this.userSettings?.anonymousNames() - ? translateText("user_setting.on") - : translateText("user_setting.off")} + ${ + this.userSettings?.anonymousNames() + ? translateText("user_setting.on") + : translateText("user_setting.off") + }
@@ -352,15 +376,19 @@ export class SettingsModal extends LitElement implements Layer { ${translateText("user_setting.left_click_menu")}
- ${this.userSettings?.leftClickOpensMenu() - ? translateText("user_setting.left_click_opens_menu") - : translateText("user_setting.right_click_opens_menu")} + ${ + this.userSettings?.leftClickOpensMenu() + ? translateText("user_setting.left_click_opens_menu") + : translateText("user_setting.right_click_opens_menu") + }
- ${this.userSettings?.leftClickOpensMenu() - ? translateText("user_setting.on") - : translateText("user_setting.off")} + ${ + this.userSettings?.leftClickOpensMenu() + ? translateText("user_setting.on") + : translateText("user_setting.off") + }
@@ -380,17 +408,23 @@ export class SettingsModal extends LitElement implements Layer { ${translateText("user_setting.performance_overlay_label")}
- ${this.userSettings?.performanceOverlay() - ? translateText("user_setting.performance_overlay_enabled") - : translateText( - "user_setting.performance_overlay_disabled", - )} + ${ + this.userSettings?.performanceOverlay() + ? translateText( + "user_setting.performance_overlay_enabled", + ) + : translateText( + "user_setting.performance_overlay_disabled", + ) + }
- ${this.userSettings?.performanceOverlay() - ? translateText("user_setting.on") - : translateText("user_setting.off")} + ${ + this.userSettings?.performanceOverlay() + ? translateText("user_setting.on") + : translateText("user_setting.off") + }
diff --git a/src/client/graphics/layers/SpawnAd.ts b/src/client/graphics/layers/SpawnAd.ts index 960260af5c..9dd4f32f12 100644 --- a/src/client/graphics/layers/SpawnAd.ts +++ b/src/client/graphics/layers/SpawnAd.ts @@ -1,9 +1,9 @@ -import { LitElement, css, html } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, state } from "lit/decorators.js"; +import { translateText } from "../../../client/Utils"; import { GameView } from "../../../core/game/GameView"; -import { Layer } from "./Layer"; import { getGamesPlayed } from "../../Utils"; -import { translateText } from "../../../client/Utils"; +import { Layer } from "./Layer"; const AD_TYPE = "bottom_rail"; const AD_CONTAINER_ID = "bottom-rail-ad-container"; @@ -79,8 +79,8 @@ export class SpawnAd extends LitElement implements Layer { window.ramp.que.push(() => { window.ramp.spaAddAds([ { - type: AD_TYPE, selectorId: AD_CONTAINER_ID, + type: AD_TYPE, }, ]); this.adLoaded = true; @@ -125,11 +125,13 @@ export class SpawnAd extends LitElement implements Layer { id="${AD_CONTAINER_ID}" class="w-full h-full flex items-center justify-center" > - ${!this.adLoaded - ? html`${translateText("spawn_ad.loading")}` - : ""} + : "" + } `; diff --git a/src/client/graphics/layers/SpawnTimer.ts b/src/client/graphics/layers/SpawnTimer.ts index bd21e3d40b..409efbec15 100644 --- a/src/client/graphics/layers/SpawnTimer.ts +++ b/src/client/graphics/layers/SpawnTimer.ts @@ -1,7 +1,7 @@ import { GameMode, Team } from "../../../core/game/Game"; import { GameView } from "../../../core/game/GameView"; -import { Layer } from "./Layer"; import { TransformHandler } from "../TransformHandler"; +import { Layer } from "./Layer"; export class SpawnTimer implements Layer { private ratios = [0]; diff --git a/src/client/graphics/layers/StructureIconsLayer.ts b/src/client/graphics/layers/StructureIconsLayer.ts index 71f6d1ebdc..cd2a4059d8 100644 --- a/src/client/graphics/layers/StructureIconsLayer.ts +++ b/src/client/graphics/layers/StructureIconsLayer.ts @@ -1,20 +1,20 @@ import * as PIXI from "pixi.js"; -import { Cell, PlayerID, UnitType } from "../../../core/game/Game"; -import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; -import { EventBus } from "../../../core/EventBus"; -import { GameUpdateType } from "../../../core/game/GameUpdates"; -import { Layer } from "./Layer"; import { OutlineFilter } from "pixi-filters"; -import SAMMissileIcon from "../../../../resources/images/SamLauncherUnit.png"; -import { Theme } from "../../../core/configuration/Config"; -import { ToggleStructureEvent } from "../../InputHandler"; -import { TransformHandler } from "../TransformHandler"; -import anchorIcon from "../../../../resources/images/AnchorIcon.png"; import bitmapFont from "../../../../resources/fonts/round_6x6_modified.xml"; +import anchorIcon from "../../../../resources/images/AnchorIcon.png"; import cityIcon from "../../../../resources/images/CityIcon.png"; import factoryIcon from "../../../../resources/images/FactoryUnit.png"; import missileSiloIcon from "../../../../resources/images/MissileSiloUnit.png"; +import SAMMissileIcon from "../../../../resources/images/SamLauncherUnit.png"; import shieldIcon from "../../../../resources/images/ShieldIcon.png"; +import { Theme } from "../../../core/configuration/Config"; +import { EventBus } from "../../../core/EventBus"; +import { Cell, PlayerID, UnitType } from "../../../core/game/Game"; +import { GameUpdateType } from "../../../core/game/GameUpdates"; +import { GameView, PlayerView, UnitView } from "../../../core/game/GameView"; +import { ToggleStructureEvent } from "../../InputHandler"; +import { TransformHandler } from "../TransformHandler"; +import { Layer } from "./Layer"; type ShapeType = "triangle" | "square" | "pentagon" | "octagon" | "circle"; @@ -68,20 +68,20 @@ export class StructureIconsLayer implements Layer { UnitType, { visible: boolean; iconPath: string; image: HTMLImageElement | null } > = new Map([ - [UnitType.City, { visible: true, iconPath: cityIcon, image: null }], - [UnitType.Factory, { visible: true, iconPath: factoryIcon, image: null }], + [UnitType.City, { iconPath: cityIcon, image: null, visible: true }], + [UnitType.Factory, { iconPath: factoryIcon, image: null, visible: true }], [ UnitType.DefensePost, - { visible: true, iconPath: shieldIcon, image: null }, + { iconPath: shieldIcon, image: null, visible: true }, ], - [UnitType.Port, { visible: true, iconPath: anchorIcon, image: null }], + [UnitType.Port, { iconPath: anchorIcon, image: null, visible: true }], [ UnitType.MissileSilo, - { visible: true, iconPath: missileSiloIcon, image: null }, + { iconPath: missileSiloIcon, image: null, visible: true }, ], [ UnitType.SAMLauncher, - { visible: true, iconPath: SAMMissileIcon, image: null }, + { iconPath: SAMMissileIcon, image: null, visible: true }, ], ]); private renderSprites = true; @@ -119,14 +119,14 @@ export class StructureIconsLayer implements Layer { this.dotsStage.setSize(this.pixicanvas.width, this.pixicanvas.height); await this.renderer.init({ - canvas: this.pixicanvas, - resolution: 1, - width: this.pixicanvas.width, - height: this.pixicanvas.height, antialias: false, - clearBeforeRender: true, backgroundAlpha: 0, backgroundColor: 0x00000000, + canvas: this.pixicanvas, + clearBeforeRender: true, + height: this.pixicanvas.height, + resolution: 1, + width: this.pixicanvas.width, }); } @@ -249,10 +249,10 @@ export class StructureIconsLayer implements Layer { render.dotContainer.alpha = structureInfos.visible ? 1 : 0.3; if (structureInfos.visible && focusStructure) { render.iconContainer.filters = [ - new OutlineFilter({ thickness: 2, color: "rgb(255, 255, 255)" }), + new OutlineFilter({ color: "rgb(255, 255, 255)", thickness: 2 }), ]; render.dotContainer.filters = [ - new OutlineFilter({ thickness: 2, color: "rgb(255, 255, 255)" }), + new OutlineFilter({ color: "rgb(255, 255, 255)", thickness: 2 }), ]; } else { render.iconContainer.filters = []; @@ -357,12 +357,12 @@ export class StructureIconsLayer implements Layer { const shape = STRUCTURE_SHAPES[structureType]; const texture = shape ? this.createIcon( - unit.owner(), - structureType, - isConstruction, - shape, - renderIcon, - ) + unit.owner(), + structureType, + isConstruction, + shape, + renderIcon, + ) : PIXI.Texture.EMPTY; this.textureCache.set(cacheKey, texture); @@ -490,11 +490,11 @@ export class StructureIconsLayer implements Layer { if (renderIcon) { const SHAPE_OFFSETS = { - triangle: [6, 11], - square: [5, 5], + circle: [6, 6], octagon: [6, 6], pentagon: [7, 7], - circle: [6, 6], + square: [5, 5], + triangle: [6, 11], }; const [offsetX, offsetY] = SHAPE_OFFSETS[shape] || [0, 0]; context.drawImage( @@ -509,24 +509,24 @@ export class StructureIconsLayer implements Layer { private createLevelSprite(unit: UnitView): PIXI.Container { if (this.levelsStage === undefined) throw new Error("Not initialized"); return this.createUnitContainer(unit, { - type: "level", stage: this.levelsStage, + type: "level", }); } private createDotSprite(unit: UnitView): PIXI.Container { if (this.dotsStage === undefined) throw new Error("Not initialized"); return this.createUnitContainer(unit, { - type: "dot", stage: this.dotsStage, + type: "dot", }); } private createIconSprite(unit: UnitView): PIXI.Container { if (this.iconsStage === undefined) throw new Error("Not initialized"); return this.createUnitContainer(unit, { - type: "icon", stage: this.iconsStage, + type: "icon", }); } @@ -557,11 +557,11 @@ export class StructureIconsLayer implements Layer { // Add level text if needed if ((type === "icon" || type === "level") && unit.level() > 1) { const text = new PIXI.BitmapText({ - text: unit.level().toString(), style: { fontFamily: "round_6x6_modified", fontSize: 14, }, + text: unit.level().toString(), }); text.anchor.set(0.5); diff --git a/src/client/graphics/layers/StructureLayer.ts b/src/client/graphics/layers/StructureLayer.ts index b02d21402e..02a69b6df7 100644 --- a/src/client/graphics/layers/StructureLayer.ts +++ b/src/client/graphics/layers/StructureLayer.ts @@ -1,20 +1,20 @@ -import { Cell, UnitType } from "../../../core/game/Game"; import { Colord, colord } from "colord"; -import { GameView, UnitView } from "../../../core/game/GameView"; -import { euclDistFN, isometricDistFN } from "../../../core/game/GameMap"; -import { EventBus } from "../../../core/EventBus"; -import { GameUpdateType } from "../../../core/game/GameUpdates"; -import { Layer } from "./Layer"; -import SAMMissileIcon from "../../../../resources/non-commercial/images/buildings/silo4.png"; -import { Theme } from "../../../core/configuration/Config"; -import { TransformHandler } from "../TransformHandler"; -import anchorIcon from "../../../../resources/non-commercial/images/buildings/port1.png"; import cityIcon from "../../../../resources/non-commercial/images/buildings/cityAlt1.png"; import factoryIcon from "../../../../resources/non-commercial/images/buildings/factoryAlt1.png"; -import missileSiloIcon from "../../../../resources/non-commercial/images/buildings/silo1.png"; import shieldIcon from "../../../../resources/non-commercial/images/buildings/fortAlt3.png"; +import anchorIcon from "../../../../resources/non-commercial/images/buildings/port1.png"; +import missileSiloIcon from "../../../../resources/non-commercial/images/buildings/silo1.png"; +import SAMMissileIcon from "../../../../resources/non-commercial/images/buildings/silo4.png"; +import { Theme } from "../../../core/configuration/Config"; +import { EventBus } from "../../../core/EventBus"; +import { Cell, UnitType } from "../../../core/game/Game"; +import { euclDistFN, isometricDistFN } from "../../../core/game/GameMap"; +import { GameUpdateType } from "../../../core/game/GameUpdates"; +import { GameView, UnitView } from "../../../core/game/GameView"; +import { TransformHandler } from "../TransformHandler"; +import { Layer } from "./Layer"; -const underConstructionColor = colord({ r: 150, g: 150, b: 150 }); +const underConstructionColor = colord({ b: 150, g: 150, r: 150 }); // Base radius values and scaling factor for unit borders and territories const BASE_BORDER_RADIUS = 16.5; @@ -39,33 +39,33 @@ export class StructureLayer implements Layer { // Configuration for supported unit types only private readonly unitConfigs: Partial> = { [UnitType.Port]: { - icon: anchorIcon, borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR, + icon: anchorIcon, territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR, }, [UnitType.City]: { - icon: cityIcon, borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR, + icon: cityIcon, territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR, }, [UnitType.Factory]: { - icon: factoryIcon, borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR, + icon: factoryIcon, territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR, }, [UnitType.MissileSilo]: { - icon: missileSiloIcon, borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR, + icon: missileSiloIcon, territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR, }, [UnitType.DefensePost]: { - icon: shieldIcon, borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR, + icon: shieldIcon, territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR, }, [UnitType.SAMLauncher]: { - icon: SAMMissileIcon, borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR, + icon: SAMMissileIcon, territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR, }, }; diff --git a/src/client/graphics/layers/TeamStats.ts b/src/client/graphics/layers/TeamStats.ts index 8d3830633b..7f2f223f56 100644 --- a/src/client/graphics/layers/TeamStats.ts +++ b/src/client/graphics/layers/TeamStats.ts @@ -1,9 +1,9 @@ +import { html, LitElement } from "lit"; +import { customElement, property } from "lit/decorators.js"; +import { EventBus } from "../../../core/EventBus"; import { GameMode, Team, UnitType } from "../../../core/game/Game"; import { GameView, PlayerView } from "../../../core/game/GameView"; -import { LitElement, html } from "lit"; -import { customElement, property } from "lit/decorators.js"; import { renderNumber, translateText } from "../../Utils"; -import { EventBus } from "../../../core/EventBus"; import { Layer } from "./Layer"; type TeamEntry = { @@ -89,17 +89,17 @@ export class TeamStats extends LitElement implements Layer { const totalScorePercent = totalScoreSort / this.game.numLandTiles(); return { + players: teamPlayers, teamName: teamStr, - totalScoreStr: formatPercentage(totalScorePercent), - totalScoreSort, + totalCities: renderNumber(totalCities), totalGold: renderNumber(totalGold), - totalTroops: renderNumber(totalTroops / 10), - players: teamPlayers, totalLaunchers: renderNumber(totalLaunchers), totalSAMs: renderNumber(totalSAMs), + totalScoreSort, + totalScoreStr: formatPercentage(totalScorePercent), + totalTroops: renderNumber(totalTroops / 10), totalWarShips: renderNumber(totalWarShips), - totalCities: renderNumber(totalCities), }; }) .sort((a, b) => b.totalScoreSort - a.totalScoreSort); @@ -130,8 +130,9 @@ export class TeamStats extends LitElement implements Layer {
${translateText("leaderboard.team")}
- ${this.showUnits - ? html` + ${ + this.showUnits + ? html`
${translateText("leaderboard.launchers")}
@@ -145,7 +146,7 @@ export class TeamStats extends LitElement implements Layer { ${translateText("leaderboard.cities")} ` - : html` + : html`
${translateText("leaderboard.owned")}
@@ -155,7 +156,8 @@ export class TeamStats extends LitElement implements Layer {
${translateText("leaderboard.troops")}
- `} + ` + } diff --git a/src/client/graphics/layers/TerrainLayer.ts b/src/client/graphics/layers/TerrainLayer.ts index e5d4d6b99c..a88fe8904a 100644 --- a/src/client/graphics/layers/TerrainLayer.ts +++ b/src/client/graphics/layers/TerrainLayer.ts @@ -1,7 +1,7 @@ -import { GameView } from "../../../core/game/GameView"; -import { Layer } from "./Layer"; import { Theme } from "../../../core/configuration/Config"; +import { GameView } from "../../../core/game/GameView"; import { TransformHandler } from "../TransformHandler"; +import { Layer } from "./Layer"; export class TerrainLayer implements Layer { private canvas: HTMLCanvasElement | undefined; diff --git a/src/client/graphics/layers/TerritoryLayer.ts b/src/client/graphics/layers/TerritoryLayer.ts index 58d77526ba..508fc8c612 100644 --- a/src/client/graphics/layers/TerritoryLayer.ts +++ b/src/client/graphics/layers/TerritoryLayer.ts @@ -1,21 +1,21 @@ +import { PriorityQueue } from "@datastructures-js/priority-queue"; +import { Colord } from "colord"; +import { Theme } from "../../../core/configuration/Config"; +import { EventBus } from "../../../core/EventBus"; +import { Cell, PlayerType, UnitType } from "../../../core/game/Game"; +import { euclDistFN, TileRef } from "../../../core/game/GameMap"; +import { GameUpdateType } from "../../../core/game/GameUpdates"; +import { GameView, PlayerView } from "../../../core/game/GameView"; +import { UserSettings } from "../../../core/game/UserSettings"; +import { PseudoRandom } from "../../../core/PseudoRandom"; import { AlternateViewEvent, DragEvent, MouseOverEvent, RedrawGraphicsEvent, } from "../../InputHandler"; -import { Cell, PlayerType, UnitType } from "../../../core/game/Game"; -import { GameView, PlayerView } from "../../../core/game/GameView"; -import { TileRef, euclDistFN } from "../../../core/game/GameMap"; -import { Colord } from "colord"; -import { EventBus } from "../../../core/EventBus"; -import { GameUpdateType } from "../../../core/game/GameUpdates"; -import { Layer } from "./Layer"; -import { PriorityQueue } from "@datastructures-js/priority-queue"; -import { PseudoRandom } from "../../../core/PseudoRandom"; -import { Theme } from "../../../core/configuration/Config"; import { TransformHandler } from "../TransformHandler"; -import { UserSettings } from "../../../core/game/UserSettings"; +import { Layer } from "./Layer"; export class TerritoryLayer implements Layer { private readonly userSettings: UserSettings; @@ -323,7 +323,8 @@ export class TerritoryLayer implements Layer { initImageData() { this.game.forEachTile((tile) => { if (this.imageData === undefined) throw new Error("Not initialized"); - if (this.alternativeImageData === undefined) throw new Error("Not initialized"); + if (this.alternativeImageData === undefined) + throw new Error("Not initialized"); const cell = new Cell(this.game.x(tile), this.game.y(tile)); const index = cell.y * this.game.width() + cell.x; const offset = index * 4; @@ -337,7 +338,8 @@ export class TerritoryLayer implements Layer { if (this.highlightCanvas === undefined) throw new Error("Not initialized"); if (this.context === undefined) throw new Error("Not initialized"); if (this.imageData === undefined) throw new Error("Not initialized"); - if (this.alternativeImageData === undefined) throw new Error("Not initialized"); + if (this.alternativeImageData === undefined) + throw new Error("Not initialized"); const now = Date.now(); if ( now > this.lastDragTime + this.nodrawDragDuration && @@ -413,7 +415,8 @@ export class TerritoryLayer implements Layer { return; } if (this.imageData === undefined) throw new Error("Not initialized"); - if (this.alternativeImageData === undefined) throw new Error("Not initialized"); + if (this.alternativeImageData === undefined) + throw new Error("Not initialized"); if (!this.game.hasOwner(tile)) { if (this.game.hasFallout(tile)) { @@ -509,7 +512,8 @@ export class TerritoryLayer implements Layer { } paintAlternateViewTile(tile: TileRef, other: PlayerView) { - if (this.alternativeImageData === undefined) throw new Error("Not initialized"); + if (this.alternativeImageData === undefined) + throw new Error("Not initialized"); const color = this.alternateViewColor(other); this.paintTile(this.alternativeImageData, tile, color, 255); } @@ -525,21 +529,23 @@ export class TerritoryLayer implements Layer { clearTile(tile: TileRef) { const offset = tile * 4; if (this.imageData === undefined) throw new Error("Not initialized"); - if (this.alternativeImageData === undefined) throw new Error("Not initialized"); + if (this.alternativeImageData === undefined) + throw new Error("Not initialized"); this.imageData.data[offset + 3] = 0; // Set alpha to 0 (fully transparent) this.alternativeImageData.data[offset + 3] = 0; // Set alpha to 0 (fully transparent) } clearAlternativeTile(tile: TileRef) { const offset = tile * 4; - if (this.alternativeImageData === undefined) throw new Error("Not initialized"); + if (this.alternativeImageData === undefined) + throw new Error("Not initialized"); this.alternativeImageData.data[offset + 3] = 0; // Set alpha to 0 (fully transparent) } enqueueTile(tile: TileRef) { this.tileToRenderQueue.push({ - tile, lastUpdate: this.game.ticks() + this.random.nextFloat(0, 0.5), + tile, }); } diff --git a/src/client/graphics/layers/UILayer.ts b/src/client/graphics/layers/UILayer.ts index bfaa63b168..9215302dad 100644 --- a/src/client/graphics/layers/UILayer.ts +++ b/src/client/graphics/layers/UILayer.ts @@ -1,14 +1,14 @@ -import { GameView, UnitView } from "../../../core/game/GameView"; import { Colord } from "colord"; +import { Theme } from "../../../core/configuration/Config"; import { EventBus } from "../../../core/EventBus"; +import { UnitType } from "../../../core/game/Game"; import { GameUpdateType } from "../../../core/game/GameUpdates"; -import { Layer } from "./Layer"; +import { GameView, UnitView } from "../../../core/game/GameView"; +import { UserSettings } from "../../../core/game/UserSettings"; +import { UnitSelectionEvent } from "../../InputHandler"; import { ProgressBar } from "../ProgressBar"; -import { Theme } from "../../../core/configuration/Config"; import { TransformHandler } from "../TransformHandler"; -import { UnitSelectionEvent } from "../../InputHandler"; -import { UnitType } from "../../../core/game/Game"; -import { UserSettings } from "../../../core/game/UserSettings"; +import { Layer } from "./Layer"; const COLOR_PROGRESSION = [ "rgb(232, 25, 25)", @@ -69,8 +69,7 @@ export class UILayer implements Layer { this.game .updatesSinceLastTick() - ?.[GameUpdateType.Unit] - ?.map((unit) => this.game.unit(unit.id)) + ?.[GameUpdateType.Unit]?.map((unit) => this.game.unit(unit.id)) ?.forEach((unitView) => { if (unitView === undefined) return; this.onUnitEvent(unitView); @@ -253,9 +252,9 @@ export class UILayer implements Layer { // Store current selection box position for next cleanup this.lastSelectionBoxCenter = { + size: selectionSize, x: centerX, y: centerY, - size: selectionSize, }; } @@ -350,8 +349,8 @@ export class UILayer implements Layer { 0, ); this.allProgressBars.set(unit.id(), { - unit, progressBar, + unit, }); } } diff --git a/src/client/graphics/layers/UnitDisplay.ts b/src/client/graphics/layers/UnitDisplay.ts index 2876d1e9a4..a4df6fd316 100644 --- a/src/client/graphics/layers/UnitDisplay.ts +++ b/src/client/graphics/layers/UnitDisplay.ts @@ -1,17 +1,17 @@ -import { LitElement, html } from "lit"; -import { EventBus } from "../../../core/EventBus"; -import { GameView } from "../../../core/game/GameView"; -import { Layer } from "./Layer"; -import { ToggleStructureEvent } from "../../InputHandler"; -import { UnitType } from "../../../core/game/Game"; -import cityIcon from "../../../../resources/images/CityIconWhite.svg"; +import { html, LitElement } from "lit"; import { customElement } from "lit/decorators.js"; -import defensePostIcon from "../../../../resources/images/ShieldIconWhite.svg"; +import portIcon from "../../../../resources/images/AnchorIcon.png"; +import cityIcon from "../../../../resources/images/CityIconWhite.svg"; import factoryIcon from "../../../../resources/images/FactoryIconWhite.svg"; import missileSiloIcon from "../../../../resources/images/MissileSiloUnit.png"; -import portIcon from "../../../../resources/images/AnchorIcon.png"; -import { renderNumber } from "../../Utils"; +import defensePostIcon from "../../../../resources/images/ShieldIconWhite.svg"; import samLauncherIcon from "../../../../resources/non-commercial/svg/SamLauncherIconWhite.svg"; +import { EventBus } from "../../../core/EventBus"; +import { UnitType } from "../../../core/game/Game"; +import { GameView } from "../../../core/game/GameView"; +import { ToggleStructureEvent } from "../../InputHandler"; +import { renderNumber } from "../../Utils"; +import { Layer } from "./Layer"; @customElement("unit-display") export class UnitDisplay extends LitElement implements Layer { @@ -69,9 +69,9 @@ export class UnitDisplay extends LitElement implements Layer { return html`
unit.id())); this.unitToTrail.forEach((trail, unit) => { - if (this.unitTrailContext === undefined) throw new Error("Not initialized"); + if (this.unitTrailContext === undefined) + throw new Error("Not initialized"); for (const t of trail) { this.paintCell( this.game.x(t), @@ -298,7 +300,7 @@ export class UnitLayer implements Layer { private handleWarShipEvent(unit: UnitView) { if (unit.targetUnitId()) { - this.drawSprite(unit, colord({ r: 200, b: 0, g: 0 })); + this.drawSprite(unit, colord({ b: 0, g: 0, r: 200 })); } else { this.drawSprite(unit); } @@ -309,7 +311,11 @@ export class UnitLayer implements Layer { const rel = this.relationship(unit); // Clear current and previous positions - this.clearCell(this.game.x(unit.lastTile()), this.game.y(unit.lastTile()), this.context); + this.clearCell( + this.game.x(unit.lastTile()), + this.game.y(unit.lastTile()), + this.context, + ); const oldTile = this.oldShellTile.get(unit); if (oldTile !== undefined) { this.clearCell(this.game.x(oldTile), this.game.y(oldTile), this.context); @@ -432,7 +438,11 @@ export class UnitLayer implements Layer { const rel = this.relationship(unit); if (this.context === undefined) throw new Error("Not initialized"); - this.clearCell(this.game.x(unit.lastTile()), this.game.y(unit.lastTile()), this.context); + this.clearCell( + this.game.x(unit.lastTile()), + this.game.y(unit.lastTile()), + this.context, + ); if (unit.isActive()) { // Paint area @@ -504,11 +514,7 @@ export class UnitLayer implements Layer { context.fillRect(x, y, 1, 1); } - clearCell( - x: number, - y: number, - context: CanvasRenderingContext2D, - ) { + clearCell(x: number, y: number, context: CanvasRenderingContext2D) { context.clearRect(x, y, 1, 1); } diff --git a/src/client/graphics/layers/WinModal.ts b/src/client/graphics/layers/WinModal.ts index 1f86056414..e8ce74b977 100644 --- a/src/client/graphics/layers/WinModal.ts +++ b/src/client/graphics/layers/WinModal.ts @@ -1,12 +1,12 @@ -import { LitElement, css, html } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, state } from "lit/decorators.js"; +import { translateText } from "../../../client/Utils"; import { EventBus } from "../../../core/EventBus"; import { GameUpdateType } from "../../../core/game/GameUpdates"; import { GameView } from "../../../core/game/GameView"; +import { SendWinnerEvent } from "../../Transport"; import { GutterAdModalEvent } from "./GutterAdModal"; import { Layer } from "./Layer"; -import { SendWinnerEvent } from "../../Transport"; -import { translateText } from "../../../client/Utils"; @customElement("win-modal") export class WinModal extends LitElement implements Layer { diff --git a/src/client/jwt.ts b/src/client/jwt.ts index 68a230bcae..019aa47916 100644 --- a/src/client/jwt.ts +++ b/src/client/jwt.ts @@ -1,3 +1,5 @@ +import { decodeJwt } from "jose"; +import { z } from "zod"; import { RefreshResponseSchema, TokenPayload, @@ -5,9 +7,7 @@ import { UserMeResponse, UserMeResponseSchema, } from "../core/ApiSchemas"; -import { decodeJwt } from "jose"; import { getServerConfigFromClient } from "../core/configuration/ConfigLoader"; -import { z } from "zod"; function getAudience() { const { hostname } = new URL(window.location.href); @@ -85,10 +85,10 @@ export async function logOut(allSessions = false) { const response = await fetch( getApiBase() + (allSessions ? "/revoke" : "/logout"), { - method: "POST", headers: { authorization: `Bearer ${token}`, }, + method: "POST", }, ); @@ -179,7 +179,7 @@ function _isLoggedIn(): IsLoggedInResponse { } const claims = result.data; - return { token, claims }; + return { claims, token }; } catch (e) { console.log(e); return false; @@ -193,10 +193,10 @@ export async function postRefresh(): Promise { // Refresh the JWT const response = await fetch(getApiBase() + "/refresh", { - method: "POST", headers: { authorization: `Bearer ${token}`, }, + method: "POST", }); if (response.status === 401) { clearToken(); diff --git a/src/client/styles.css b/src/client/styles.css index 6afaafe464..fe34dc08d8 100644 --- a/src/client/styles.css +++ b/src/client/styles.css @@ -134,7 +134,7 @@ .option-image { width: 100%; - aspect-ratio: 4/2; + aspect-ratio: 4 / 2; color: #aaa; transition: transform 0.2s ease-in-out; border-radius: 8px; @@ -373,17 +373,18 @@ label.option-card:hover { } #helpModal .city-icon { - mask: url("../../resources/images/CityIconWhite.svg") no-repeat center / cover; + mask: + url("../../resources/images/CityIconWhite.svg") no-repeat center / cover; } #helpModal .factory-icon { - mask: url("../../resources/images/FactoryIconWhite.svg") no-repeat center / - cover; + mask: + url("../../resources/images/FactoryIconWhite.svg") no-repeat center / cover; } #helpModal .defense-post-icon { - mask: url("../../resources/images/ShieldIconWhite.svg") no-repeat center / - cover; + mask: + url("../../resources/images/ShieldIconWhite.svg") no-repeat center / cover; } #helpModal .port-icon { @@ -391,27 +392,32 @@ label.option-card:hover { } #helpModal .warship-icon { - mask: url("../../resources/images/BattleshipIconWhite.svg") no-repeat center / + mask: + url("../../resources/images/BattleshipIconWhite.svg") no-repeat center / cover; } #helpModal .missile-silo-icon { - mask: url("../../resources/non-commercial/svg/MissileSiloIconWhite.svg") - no-repeat center / cover; + mask: + url("../../resources/non-commercial/svg/MissileSiloIconWhite.svg") no-repeat + center / cover; } #helpModal .sam-launcher-icon { - mask: url("../../resources/non-commercial/svg/SamLauncherIconWhite.svg") - no-repeat center / cover; + mask: + url("../../resources/non-commercial/svg/SamLauncherIconWhite.svg") no-repeat + center / cover; } #helpModal .atom-bomb-icon { - mask: url("../../resources/images/NukeIconWhite.svg") no-repeat center / cover; + mask: + url("../../resources/images/NukeIconWhite.svg") no-repeat center / cover; } #helpModal .hydrogen-bomb-icon { - mask: url("../../resources/images/MushroomCloudIconWhite.svg") no-repeat - center / cover; + mask: + url("../../resources/images/MushroomCloudIconWhite.svg") no-repeat center / + cover; } #helpModal .mirv-icon { @@ -419,7 +425,8 @@ label.option-card:hover { } #helpModal .chat-icon { - mask: url("../../resources/images/ChatIconWhite.svg") no-repeat center / cover; + mask: + url("../../resources/images/ChatIconWhite.svg") no-repeat center / cover; } #helpModal .target-icon { @@ -427,33 +434,35 @@ label.option-card:hover { } #helpModal .alliance-icon { - mask: url("../../resources/images/AllianceIconWhite.svg") no-repeat center / - cover; + mask: + url("../../resources/images/AllianceIconWhite.svg") no-repeat center / cover; } #helpModal .emoji-icon { - mask: url("../../resources/images/EmojiIconWhite.svg") no-repeat center / - cover; + mask: + url("../../resources/images/EmojiIconWhite.svg") no-repeat center / cover; } #helpModal .betray-icon { - mask: url("../../resources/images/TraitorIconWhite.svg") no-repeat center / - cover; + mask: + url("../../resources/images/TraitorIconWhite.svg") no-repeat center / cover; } #helpModal .donate-icon { - mask: url("../../resources/images/DonateTroopIconWhite.svg") no-repeat - center / cover; + mask: + url("../../resources/images/DonateTroopIconWhite.svg") no-repeat center / + cover; } #helpModal .donate-gold-icon { - mask: url("../../resources/images/DonateGoldIconWhite.svg") no-repeat center / + mask: + url("../../resources/images/DonateGoldIconWhite.svg") no-repeat center / cover; } #helpModal .build-icon { - mask: url("../../resources/images/BuildIconWhite.svg") no-repeat center / - cover; + mask: + url("../../resources/images/BuildIconWhite.svg") no-repeat center / cover; } #helpModal .info-icon { diff --git a/src/client/utilities/RenderUnitTypeOptions.ts b/src/client/utilities/RenderUnitTypeOptions.ts index 02569e654c..d777b4f0cf 100644 --- a/src/client/utilities/RenderUnitTypeOptions.ts +++ b/src/client/utilities/RenderUnitTypeOptions.ts @@ -1,5 +1,5 @@ // renderUnitTypeOptions.ts -import { TemplateResult, html } from "lit"; +import { html, TemplateResult } from "lit"; import { UnitType } from "../../core/game/Game"; import { translateText } from "../Utils"; @@ -9,16 +9,16 @@ export type UnitTypeRenderContext = { }; const unitOptions: { type: UnitType; translationKey: string }[] = [ - { type: UnitType.City, translationKey: "unit_type.city" }, - { type: UnitType.DefensePost, translationKey: "unit_type.defense_post" }, - { type: UnitType.Port, translationKey: "unit_type.port" }, - { type: UnitType.Warship, translationKey: "unit_type.warship" }, - { type: UnitType.MissileSilo, translationKey: "unit_type.missile_silo" }, - { type: UnitType.SAMLauncher, translationKey: "unit_type.sam_launcher" }, - { type: UnitType.AtomBomb, translationKey: "unit_type.atom_bomb" }, - { type: UnitType.HydrogenBomb, translationKey: "unit_type.hydrogen_bomb" }, - { type: UnitType.MIRV, translationKey: "unit_type.mirv" }, - { type: UnitType.Factory, translationKey: "unit_type.factory" }, + { translationKey: "unit_type.city", type: UnitType.City }, + { translationKey: "unit_type.defense_post", type: UnitType.DefensePost }, + { translationKey: "unit_type.port", type: UnitType.Port }, + { translationKey: "unit_type.warship", type: UnitType.Warship }, + { translationKey: "unit_type.missile_silo", type: UnitType.MissileSilo }, + { translationKey: "unit_type.sam_launcher", type: UnitType.SAMLauncher }, + { translationKey: "unit_type.atom_bomb", type: UnitType.AtomBomb }, + { translationKey: "unit_type.hydrogen_bomb", type: UnitType.HydrogenBomb }, + { translationKey: "unit_type.mirv", type: UnitType.MIRV }, + { translationKey: "unit_type.factory", type: UnitType.Factory }, ]; export function renderUnitTypeOptions({ @@ -36,7 +36,7 @@ export function renderUnitTypeOptions({ type="checkbox" .checked=${disabledUnits.includes(type)} @change=${(e: Event) => { - const { checked } = (e.target as HTMLInputElement); + const { checked } = e.target as HTMLInputElement; toggleUnit(type, checked); }} /> diff --git a/src/core/ApiSchemas.ts b/src/core/ApiSchemas.ts index a46f2976a6..4ab8089fe4 100644 --- a/src/core/ApiSchemas.ts +++ b/src/core/ApiSchemas.ts @@ -1,14 +1,18 @@ // This file contains schemas for api.openfront.io -import { base64urlToUuid } from "./Base64"; + import { z } from "zod"; +import { base64urlToUuid } from "./Base64"; export const RefreshResponseSchema = z.object({ token: z.string(), }); export type RefreshResponse = z.infer; -/* eslint-disable sort-keys */ export const TokenPayloadSchema = z.object({ + aud: z.string(), + exp: z.number(), + iat: z.number(), + iss: z.string(), jti: z.string(), sub: z .string() @@ -26,41 +30,37 @@ export const TokenPayloadSchema = z.object({ if (!uuid) throw new Error("Invalid base64 UUID"); return uuid; }), - iat: z.number(), - iss: z.string(), - aud: z.string(), - exp: z.number(), }); export type TokenPayload = z.infer; export const UserMeResponseSchema = z.object({ + player: z.object({ + flares: z.string().array().optional(), + publicId: z.string(), + roles: z.string().array().optional(), + }), user: z.object({ - id: z.string(), avatar: z.string().nullable(), - username: z.string(), - global_name: z.string().nullable(), discriminator: z.string(), + global_name: z.string().nullable(), + id: z.string(), locale: z.string().optional(), - }), - player: z.object({ - publicId: z.string(), - roles: z.string().array().optional(), - flares: z.string().array().optional(), + username: z.string(), }), }); export type UserMeResponse = z.infer; export const StripeCreateCheckoutSessionResponseSchema = z.object({ + client_reference_id: z.string().optional(), + customer: z.string().optional(), id: z.string(), + metadata: z.partialRecord(z.string(), z.string()), object: z.literal("checkout.session"), - url: z.string(), + payment_intent: z.string().optional(), payment_status: z.enum(["paid", "unpaid", "no_payment_required"]), status: z.enum(["open", "complete", "expired"]), - client_reference_id: z.string().optional(), - customer: z.string().optional(), - payment_intent: z.string().optional(), subscription: z.string().optional(), - metadata: z.partialRecord(z.string(), z.string()), + url: z.string(), }); export type StripeCreateCheckoutSessionResponse = z.infer< typeof StripeCreateCheckoutSessionResponseSchema diff --git a/src/core/CosmeticSchemas.ts b/src/core/CosmeticSchemas.ts index c8950fcc1c..35ef1ce13f 100644 --- a/src/core/CosmeticSchemas.ts +++ b/src/core/CosmeticSchemas.ts @@ -1,12 +1,11 @@ -import { RequiredPatternSchema } from "./Schemas"; import { z } from "zod"; +import { RequiredPatternSchema } from "./Schemas"; export const ProductSchema = z.object({ - productId: z.string(), - /* eslint-disable sort-keys */ - priceId: z.string(), price: z.string(), - /* eslint-enable sort-keys */ + + priceId: z.string(), + productId: z.string(), }); const PatternSchema = z.object({ @@ -17,28 +16,26 @@ const PatternSchema = z.object({ // Schema for resources/cosmetics/cosmetics.json export const CosmeticsSchema = z.object({ - patterns: z.record(z.string(), PatternSchema), - /* eslint-disable sort-keys */ flag: z .object({ - layers: z.record( + color: z.record( z.string(), z.object({ - name: z.string(), + color: z.string(), flares: z.string().array().optional(), + name: z.string(), }), ), - color: z.record( + layers: z.record( z.string(), z.object({ - color: z.string(), - name: z.string(), flares: z.string().array().optional(), + name: z.string(), }), ), }) .optional(), - /* eslint-enable sort-keys */ + patterns: z.record(z.string(), PatternSchema), }); export type Cosmetics = z.infer; export type Pattern = z.infer; diff --git a/src/core/CustomFlag.ts b/src/core/CustomFlag.ts index 16f44a6c2b..4df27b1fea 100644 --- a/src/core/CustomFlag.ts +++ b/src/core/CustomFlag.ts @@ -4,11 +4,11 @@ const ANIMATION_DURATIONS: Record = { "bright-rainbow": 4000, "copper-glow": 3000, "gold-glow": 3000, - "lava": 6000, - "neon": 3000, - "rainbow": 4000, + lava: 6000, + neon: 3000, + rainbow: 4000, "silver-glow": 3000, - "water": 6200, + water: 6200, }; // TODO: Pass in cosmetics as a parameter when @@ -28,8 +28,8 @@ export function renderPlayerFlag( const code = flag.slice("!".length); const layers = code.split("_").map((segment) => { const [layerKey, colorKey] = segment.split("-"); - // eslint-disable-next-line sort-keys - return { layerKey, colorKey }; + + return { colorKey, layerKey }; }); target.innerHTML = ""; diff --git a/src/core/EventBus.ts b/src/core/EventBus.ts index 60e351d8fc..4f42480463 100644 --- a/src/core/EventBus.ts +++ b/src/core/EventBus.ts @@ -6,8 +6,10 @@ export type EventConstructor = new ( ) => T; export class EventBus { - private readonly listeners: Map void>> = - new Map(); + private readonly listeners: Map< + EventConstructor, + Array<(event: GameEvent) => void> + > = new Map(); emit(event: T): void { const eventConstructor = event.constructor as EventConstructor; diff --git a/src/core/ExpressSchemas.ts b/src/core/ExpressSchemas.ts index 23491e4707..36aa87b9a6 100644 --- a/src/core/ExpressSchemas.ts +++ b/src/core/ExpressSchemas.ts @@ -1,6 +1,7 @@ // This file contians schemas for the primary openfront express server -import { GameInfoSchema } from "./Schemas"; + import { z } from "zod"; +import { GameInfoSchema } from "./Schemas"; export const ApiEnvResponseSchema = z.object({ game_env: z.string(), diff --git a/src/core/GameRunner.ts b/src/core/GameRunner.ts index b9c23465f3..da26c867d8 100644 --- a/src/core/GameRunner.ts +++ b/src/core/GameRunner.ts @@ -1,3 +1,7 @@ +import { placeName } from "../client/graphics/NameBoxCalculator"; +import { getConfig } from "./configuration/ConfigLoader"; +import { Executor } from "./execution/ExecutionManager"; +import { WinCheckExecution } from "./execution/WinCheckExecution"; import { AllPlayers, Attack, @@ -14,23 +18,19 @@ import { PlayerProfile, PlayerType, } from "./game/Game"; -import { ClientID, GameStartInfo, Turn } from "./Schemas"; +import { createGame } from "./game/GameImpl"; +import { TileRef } from "./game/GameMap"; +import { GameMapLoader } from "./game/GameMapLoader"; import { ErrorUpdate, GameUpdateType, GameUpdateViewData, } from "./game/GameUpdates"; -import { sanitize, simpleHash } from "./Util"; -import { Executor } from "./execution/ExecutionManager"; -import { GameMapLoader } from "./game/GameMapLoader"; +import { loadTerrainMap } from "./game/TerrainMapLoader"; import { PseudoRandom } from "./PseudoRandom"; -import { TileRef } from "./game/GameMap"; -import { WinCheckExecution } from "./execution/WinCheckExecution"; -import { createGame } from "./game/GameImpl"; +import { ClientID, GameStartInfo, Turn } from "./Schemas"; +import { sanitize, simpleHash } from "./Util"; import { fixProfaneUsername } from "./validations/username"; -import { getConfig } from "./configuration/ConfigLoader"; -import { loadTerrainMap } from "./game/TerrainMapLoader"; -import { placeName } from "../client/graphics/NameBoxCalculator"; export async function createGameRunner( gameStart: GameStartInfo, @@ -57,13 +57,13 @@ export async function createGameRunner( const nations = gameStart.config.disableNPCs ? [] : gameMap.manifest.nations.map( - (n) => - new Nation( - new Cell(n.coordinates[0], n.coordinates[1]), - n.strength, - new PlayerInfo(n.name, PlayerType.FakeHuman, null, random.nextID()), - ), - ); + (n) => + new Nation( + new Cell(n.coordinates[0], n.coordinates[1]), + n.strength, + new PlayerInfo(n.name, PlayerType.FakeHuman, null, random.nextID()), + ), + ); const game: Game = createGame( humans, diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index e70b9c9601..46bf7c4ebb 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -1,3 +1,10 @@ +import { base64url } from "jose"; +import { z } from "zod"; +import quickChatData from "../../resources/QuickChat.json" with { + type: "json", +}; +import countries from "../client/data/countries.json" with { type: "json" }; +import { ID } from "./BaseSchemas"; import { AllPlayers, Difficulty, @@ -10,14 +17,9 @@ import { Trios, UnitType, } from "./game/Game"; -import { ID } from "./BaseSchemas"; import { PatternDecoder } from "./PatternDecoder"; import { PlayerStatsSchema } from "./StatsSchemas"; -import { base64url } from "jose"; -import countries from "../client/data/countries.json" with { type: "json" }; import { flattenedEmojiTable } from "./Util"; -import quickChatData from "../../resources/QuickChat.json" with { type: "json" }; -import { z } from "zod"; export type GameID = string; export type ClientID = string; @@ -136,8 +138,8 @@ export type TeamCountConfig = z.infer; export const GameConfigSchema = z.object({ bots: z.number().int().min(0).max(400), difficulty: z.enum(Difficulty), - disableNPCs: z.boolean(), disabledUnits: z.enum(UnitType).array().optional(), + disableNPCs: z.boolean(), donateGold: z.boolean(), donateTroops: z.boolean(), gameMap: z.enum(GameMapType), diff --git a/src/core/StatsSchemas.ts b/src/core/StatsSchemas.ts index 6f58d84f28..41cb3a47ed 100644 --- a/src/core/StatsSchemas.ts +++ b/src/core/StatsSchemas.ts @@ -1,5 +1,5 @@ -import { UnitType } from "./game/Game"; import { z } from "zod"; +import { UnitType } from "./game/Game"; export const BombUnitSchema = z.union([ z.literal("abomb"), diff --git a/src/core/Util.ts b/src/core/Util.ts index cb919dd720..3f3ab14c06 100644 --- a/src/core/Util.ts +++ b/src/core/Util.ts @@ -1,8 +1,13 @@ +import DOMPurify from "dompurify"; +import { customAlphabet } from "nanoid"; +import { ID } from "./BaseSchemas"; +import { ServerConfig } from "./configuration/Config"; import { BOT_NAME_PREFIXES, BOT_NAME_SUFFIXES, } from "./execution/utils/BotNames"; import { Cell, Unit } from "./game/Game"; +import { GameMap, TileRef } from "./game/GameMap"; import { ClientID, GameConfig, @@ -12,11 +17,6 @@ import { Turn, Winner, } from "./Schemas"; -import { GameMap, TileRef } from "./game/GameMap"; -import DOMPurify from "dompurify"; -import { ID } from "./BaseSchemas"; -import { ServerConfig } from "./configuration/Config"; -import { customAlphabet } from "nanoid"; export function manhattanDistWrapped( c1: Cell, @@ -89,8 +89,7 @@ export function calculateBoundingBox( maxY = Math.max(maxY, cell.y); }); - // eslint-disable-next-line sort-keys - return { min: new Cell(minX, minY), max: new Cell(maxX, maxY) }; + return { max: new Cell(maxX, maxY), min: new Cell(minX, minY) }; } export function calculateBoundingBoxCenter( @@ -233,7 +232,12 @@ export function getClientID(gameID: GameID): ClientID { const cachedGame = localStorage.getItem("game_id"); const cachedClient = localStorage.getItem("client_id"); - if (gameID === cachedGame && cachedClient && ID.safeParse(cachedClient).success) return cachedClient; + if ( + gameID === cachedGame && + cachedClient && + ID.safeParse(cachedClient).success + ) + return cachedClient; const clientId = generateID(); localStorage.setItem("game_id", gameID); diff --git a/src/core/WorkerSchemas.ts b/src/core/WorkerSchemas.ts index f8ce257786..94b8ae08af 100644 --- a/src/core/WorkerSchemas.ts +++ b/src/core/WorkerSchemas.ts @@ -1,6 +1,7 @@ // This file contians schemas for the openfront worker express server -import { GameConfigSchema, GameRecordSchema } from "./Schemas"; + import { z } from "zod"; +import { GameConfigSchema, GameRecordSchema } from "./Schemas"; export const CreateGameInputSchema = GameConfigSchema.or( z diff --git a/src/core/configuration/ColorAllocator.ts b/src/core/configuration/ColorAllocator.ts index e4bef8d361..a95e261591 100644 --- a/src/core/configuration/ColorAllocator.ts +++ b/src/core/configuration/ColorAllocator.ts @@ -1,5 +1,10 @@ import { Colord, extend } from "colord"; +import labPlugin from "colord/plugins/lab"; +import lchPlugin from "colord/plugins/lch"; +import Color from "colorjs.io"; import { ColoredTeams, Team } from "../game/Game"; +import { PseudoRandom } from "../PseudoRandom"; +import { simpleHash } from "../Util"; import { blueTeamColors, botTeamColors, @@ -10,11 +15,7 @@ import { tealTeamColors, yellowTeamColors, } from "./Colors"; -import Color from "colorjs.io"; -import { PseudoRandom } from "../PseudoRandom"; -import labPlugin from "colord/plugins/lab"; -import lchPlugin from "colord/plugins/lch"; -import { simpleHash } from "../Util"; + extend([lchPlugin]); extend([labPlugin]); diff --git a/src/core/configuration/Colors.ts b/src/core/configuration/Colors.ts index afee46dc47..2bfa66bac1 100644 --- a/src/core/configuration/Colors.ts +++ b/src/core/configuration/Colors.ts @@ -5,15 +5,14 @@ import lchPlugin from "colord/plugins/lch"; extend([lchPlugin]); extend([labPlugin]); -/* eslint-disable sort-keys */ -export const red = colord({ h: 0, s: 82, l: 56 }); -export const blue = colord({ h: 224, s: 100, l: 58 }); -export const teal = colord({ h: 172, s: 66, l: 50 }); -export const purple = colord({ h: 271, s: 81, l: 56 }); -export const yellow = colord({ h: 45, s: 93, l: 47 }); -export const orange = colord({ h: 25, s: 95, l: 53 }); -export const green = colord({ h: 128, s: 49, l: 50 }); -export const botColor = colord({ h: 36, s: 10, l: 80 }); +export const red = colord({ h: 0, l: 56, s: 82 }); +export const blue = colord({ h: 224, l: 58, s: 100 }); +export const teal = colord({ h: 172, l: 50, s: 66 }); +export const purple = colord({ h: 271, l: 56, s: 81 }); +export const yellow = colord({ h: 45, l: 47, s: 93 }); +export const orange = colord({ h: 25, l: 53, s: 95 }); +export const green = colord({ h: 128, l: 50, s: 49 }); +export const botColor = colord({ h: 36, l: 80, s: 10 }); export const redTeamColors: Colord[] = generateTeamColors(red); export const blueTeamColors: Colord[] = generateTeamColors(blue); @@ -36,331 +35,331 @@ function generateTeamColors(baseColor: Colord): Colord[] { return colord({ h: baseHue, - s: saturation, l: lightness, + s: saturation, }); }); } export const nationColors: Colord[] = [ - colord({ r: 230, g: 100, b: 100 }), // Bright Red - colord({ r: 100, g: 180, b: 230 }), // Sky Blue - colord({ r: 230, g: 180, b: 80 }), // Golden Yellow - colord({ r: 180, g: 100, b: 230 }), // Purple - colord({ r: 80, g: 200, b: 120 }), // Emerald Green - colord({ r: 230, g: 130, b: 180 }), // Pink - colord({ r: 100, g: 160, b: 80 }), // Olive Green - colord({ r: 230, g: 150, b: 100 }), // Peach - colord({ r: 80, g: 130, b: 190 }), // Navy Blue - colord({ r: 210, g: 210, b: 100 }), // Lime Yellow - colord({ r: 190, g: 100, b: 130 }), // Maroon - colord({ r: 100, g: 210, b: 210 }), // Turquoise - colord({ r: 210, g: 140, b: 80 }), // Light Orange - colord({ r: 150, g: 110, b: 190 }), // Lavender - colord({ r: 180, g: 210, b: 120 }), // Light Green - colord({ r: 210, g: 100, b: 160 }), // Hot Pink - colord({ r: 100, g: 140, b: 110 }), // Sea Green - colord({ r: 230, g: 180, b: 180 }), // Light Pink - colord({ r: 120, g: 120, b: 190 }), // Periwinkle - colord({ r: 190, g: 170, b: 100 }), // Sand - colord({ r: 100, g: 180, b: 160 }), // Aquamarine - colord({ r: 210, g: 160, b: 200 }), // Orchid - colord({ r: 170, g: 190, b: 100 }), // Yellow Green - colord({ r: 100, g: 130, b: 150 }), // Steel Blue - colord({ r: 230, g: 140, b: 140 }), // Salmon - colord({ r: 140, g: 180, b: 220 }), // Light Blue - colord({ r: 200, g: 160, b: 110 }), // Tan - colord({ r: 180, g: 130, b: 180 }), // Plum - colord({ r: 130, g: 200, b: 130 }), // Light Sea Green - colord({ r: 220, g: 120, b: 120 }), // Coral - colord({ r: 120, g: 160, b: 200 }), // Cornflower Blue - colord({ r: 200, g: 200, b: 140 }), // Khaki - colord({ r: 160, g: 120, b: 160 }), // Purple Gray - colord({ r: 140, g: 180, b: 140 }), // Dark Sea Green - colord({ r: 200, g: 130, b: 110 }), // Dark Salmon - colord({ r: 130, g: 170, b: 190 }), // Cadet Blue - colord({ r: 190, g: 180, b: 160 }), // Tan Gray - colord({ r: 170, g: 140, b: 190 }), // Medium Purple - colord({ r: 160, g: 190, b: 160 }), // Pale Green - colord({ r: 190, g: 150, b: 130 }), // Rosy Brown - colord({ r: 140, g: 150, b: 180 }), // Light Slate Gray - colord({ r: 180, g: 170, b: 140 }), // Dark Khaki - colord({ r: 150, g: 130, b: 150 }), // Thistle - colord({ r: 170, g: 190, b: 180 }), // Pale Blue Green - colord({ r: 190, g: 140, b: 150 }), // Puce - colord({ r: 130, g: 180, b: 170 }), // Medium Aquamarine - colord({ r: 180, g: 160, b: 180 }), // Mauve - colord({ r: 160, g: 180, b: 140 }), // Dark Olive Green - colord({ r: 170, g: 150, b: 170 }), // Dusty Rose - colord({ r: 100, g: 180, b: 230 }), // Sky Blue - colord({ r: 230, g: 180, b: 80 }), // Golden Yellow - colord({ r: 180, g: 100, b: 230 }), // Purple - colord({ r: 80, g: 200, b: 120 }), // Emerald Green - colord({ r: 230, g: 130, b: 180 }), // Pink - colord({ r: 100, g: 160, b: 80 }), // Olive Green - colord({ r: 230, g: 150, b: 100 }), // Peach - colord({ r: 80, g: 130, b: 190 }), // Navy Blue - colord({ r: 210, g: 210, b: 100 }), // Lime Yellow - colord({ r: 190, g: 100, b: 130 }), // Maroon - colord({ r: 100, g: 210, b: 210 }), // Turquoise - colord({ r: 210, g: 140, b: 80 }), // Light Orange - colord({ r: 150, g: 110, b: 190 }), // Lavender - colord({ r: 180, g: 210, b: 120 }), // Light Green - colord({ r: 210, g: 100, b: 160 }), // Hot Pink - colord({ r: 100, g: 140, b: 110 }), // Sea Green - colord({ r: 230, g: 180, b: 180 }), // Light Pink - colord({ r: 120, g: 120, b: 190 }), // Periwinkle - colord({ r: 190, g: 170, b: 100 }), // Sand - colord({ r: 100, g: 180, b: 160 }), // Aquamarine - colord({ r: 210, g: 160, b: 200 }), // Orchid - colord({ r: 170, g: 190, b: 100 }), // Yellow Green - colord({ r: 100, g: 130, b: 150 }), // Steel Blue - colord({ r: 230, g: 140, b: 140 }), // Salmon - colord({ r: 140, g: 180, b: 220 }), // Light Blue - colord({ r: 200, g: 160, b: 110 }), // Tan - colord({ r: 180, g: 130, b: 180 }), // Plum - colord({ r: 130, g: 200, b: 130 }), // Light Sea Green - colord({ r: 220, g: 120, b: 120 }), // Coral - colord({ r: 120, g: 160, b: 200 }), // Cornflower Blue - colord({ r: 200, g: 200, b: 140 }), // Khaki - colord({ r: 160, g: 120, b: 160 }), // Purple Gray - colord({ r: 140, g: 180, b: 140 }), // Dark Sea Green - colord({ r: 200, g: 130, b: 110 }), // Dark Salmon - colord({ r: 130, g: 170, b: 190 }), // Cadet Blue - colord({ r: 190, g: 180, b: 160 }), // Tan Gray - colord({ r: 170, g: 140, b: 190 }), // Medium Purple - colord({ r: 160, g: 190, b: 160 }), // Pale Green - colord({ r: 190, g: 150, b: 130 }), // Rosy Brown - colord({ r: 140, g: 150, b: 180 }), // Light Slate Gray - colord({ r: 180, g: 170, b: 140 }), // Dark Khaki - colord({ r: 150, g: 130, b: 150 }), // Thistle - colord({ r: 170, g: 190, b: 180 }), // Pale Blue Green - colord({ r: 190, g: 140, b: 150 }), // Puce - colord({ r: 130, g: 180, b: 170 }), // Medium Aquamarine - colord({ r: 180, g: 160, b: 180 }), // Mauve - colord({ r: 160, g: 180, b: 140 }), // Dark Olive Green - colord({ r: 170, g: 150, b: 170 }), // Dusty Rose + colord({ b: 100, g: 100, r: 230 }), // Bright Red + colord({ b: 230, g: 180, r: 100 }), // Sky Blue + colord({ b: 80, g: 180, r: 230 }), // Golden Yellow + colord({ b: 230, g: 100, r: 180 }), // Purple + colord({ b: 120, g: 200, r: 80 }), // Emerald Green + colord({ b: 180, g: 130, r: 230 }), // Pink + colord({ b: 80, g: 160, r: 100 }), // Olive Green + colord({ b: 100, g: 150, r: 230 }), // Peach + colord({ b: 190, g: 130, r: 80 }), // Navy Blue + colord({ b: 100, g: 210, r: 210 }), // Lime Yellow + colord({ b: 130, g: 100, r: 190 }), // Maroon + colord({ b: 210, g: 210, r: 100 }), // Turquoise + colord({ b: 80, g: 140, r: 210 }), // Light Orange + colord({ b: 190, g: 110, r: 150 }), // Lavender + colord({ b: 120, g: 210, r: 180 }), // Light Green + colord({ b: 160, g: 100, r: 210 }), // Hot Pink + colord({ b: 110, g: 140, r: 100 }), // Sea Green + colord({ b: 180, g: 180, r: 230 }), // Light Pink + colord({ b: 190, g: 120, r: 120 }), // Periwinkle + colord({ b: 100, g: 170, r: 190 }), // Sand + colord({ b: 160, g: 180, r: 100 }), // Aquamarine + colord({ b: 200, g: 160, r: 210 }), // Orchid + colord({ b: 100, g: 190, r: 170 }), // Yellow Green + colord({ b: 150, g: 130, r: 100 }), // Steel Blue + colord({ b: 140, g: 140, r: 230 }), // Salmon + colord({ b: 220, g: 180, r: 140 }), // Light Blue + colord({ b: 110, g: 160, r: 200 }), // Tan + colord({ b: 180, g: 130, r: 180 }), // Plum + colord({ b: 130, g: 200, r: 130 }), // Light Sea Green + colord({ b: 120, g: 120, r: 220 }), // Coral + colord({ b: 200, g: 160, r: 120 }), // Cornflower Blue + colord({ b: 140, g: 200, r: 200 }), // Khaki + colord({ b: 160, g: 120, r: 160 }), // Purple Gray + colord({ b: 140, g: 180, r: 140 }), // Dark Sea Green + colord({ b: 110, g: 130, r: 200 }), // Dark Salmon + colord({ b: 190, g: 170, r: 130 }), // Cadet Blue + colord({ b: 160, g: 180, r: 190 }), // Tan Gray + colord({ b: 190, g: 140, r: 170 }), // Medium Purple + colord({ b: 160, g: 190, r: 160 }), // Pale Green + colord({ b: 130, g: 150, r: 190 }), // Rosy Brown + colord({ b: 180, g: 150, r: 140 }), // Light Slate Gray + colord({ b: 140, g: 170, r: 180 }), // Dark Khaki + colord({ b: 150, g: 130, r: 150 }), // Thistle + colord({ b: 180, g: 190, r: 170 }), // Pale Blue Green + colord({ b: 150, g: 140, r: 190 }), // Puce + colord({ b: 170, g: 180, r: 130 }), // Medium Aquamarine + colord({ b: 180, g: 160, r: 180 }), // Mauve + colord({ b: 140, g: 180, r: 160 }), // Dark Olive Green + colord({ b: 170, g: 150, r: 170 }), // Dusty Rose + colord({ b: 230, g: 180, r: 100 }), // Sky Blue + colord({ b: 80, g: 180, r: 230 }), // Golden Yellow + colord({ b: 230, g: 100, r: 180 }), // Purple + colord({ b: 120, g: 200, r: 80 }), // Emerald Green + colord({ b: 180, g: 130, r: 230 }), // Pink + colord({ b: 80, g: 160, r: 100 }), // Olive Green + colord({ b: 100, g: 150, r: 230 }), // Peach + colord({ b: 190, g: 130, r: 80 }), // Navy Blue + colord({ b: 100, g: 210, r: 210 }), // Lime Yellow + colord({ b: 130, g: 100, r: 190 }), // Maroon + colord({ b: 210, g: 210, r: 100 }), // Turquoise + colord({ b: 80, g: 140, r: 210 }), // Light Orange + colord({ b: 190, g: 110, r: 150 }), // Lavender + colord({ b: 120, g: 210, r: 180 }), // Light Green + colord({ b: 160, g: 100, r: 210 }), // Hot Pink + colord({ b: 110, g: 140, r: 100 }), // Sea Green + colord({ b: 180, g: 180, r: 230 }), // Light Pink + colord({ b: 190, g: 120, r: 120 }), // Periwinkle + colord({ b: 100, g: 170, r: 190 }), // Sand + colord({ b: 160, g: 180, r: 100 }), // Aquamarine + colord({ b: 200, g: 160, r: 210 }), // Orchid + colord({ b: 100, g: 190, r: 170 }), // Yellow Green + colord({ b: 150, g: 130, r: 100 }), // Steel Blue + colord({ b: 140, g: 140, r: 230 }), // Salmon + colord({ b: 220, g: 180, r: 140 }), // Light Blue + colord({ b: 110, g: 160, r: 200 }), // Tan + colord({ b: 180, g: 130, r: 180 }), // Plum + colord({ b: 130, g: 200, r: 130 }), // Light Sea Green + colord({ b: 120, g: 120, r: 220 }), // Coral + colord({ b: 200, g: 160, r: 120 }), // Cornflower Blue + colord({ b: 140, g: 200, r: 200 }), // Khaki + colord({ b: 160, g: 120, r: 160 }), // Purple Gray + colord({ b: 140, g: 180, r: 140 }), // Dark Sea Green + colord({ b: 110, g: 130, r: 200 }), // Dark Salmon + colord({ b: 190, g: 170, r: 130 }), // Cadet Blue + colord({ b: 160, g: 180, r: 190 }), // Tan Gray + colord({ b: 190, g: 140, r: 170 }), // Medium Purple + colord({ b: 160, g: 190, r: 160 }), // Pale Green + colord({ b: 130, g: 150, r: 190 }), // Rosy Brown + colord({ b: 180, g: 150, r: 140 }), // Light Slate Gray + colord({ b: 140, g: 170, r: 180 }), // Dark Khaki + colord({ b: 150, g: 130, r: 150 }), // Thistle + colord({ b: 180, g: 190, r: 170 }), // Pale Blue Green + colord({ b: 150, g: 140, r: 190 }), // Puce + colord({ b: 170, g: 180, r: 130 }), // Medium Aquamarine + colord({ b: 180, g: 160, r: 180 }), // Mauve + colord({ b: 140, g: 180, r: 160 }), // Dark Olive Green + colord({ b: 170, g: 150, r: 170 }), // Dusty Rose ]; // Bright pastel theme with 64 colors export const humanColors: Colord[] = [ - colord({ r: 16, g: 185, b: 129 }), // Sea Green - colord({ r: 34, g: 197, b: 94 }), // Emerald - colord({ r: 45, g: 212, b: 191 }), // Turquoise - colord({ r: 48, g: 178, b: 180 }), // Teal - colord({ r: 52, g: 211, b: 153 }), // Spearmint - colord({ r: 56, g: 189, b: 248 }), // Light Blue - colord({ r: 59, g: 130, b: 246 }), // Royal Blue - colord({ r: 67, g: 190, b: 84 }), // Fresh Green - colord({ r: 74, g: 222, b: 128 }), // Mint - colord({ r: 79, g: 70, b: 229 }), // Indigo - colord({ r: 82, g: 183, b: 136 }), // Jade - colord({ r: 96, g: 165, b: 250 }), // Sky Blue - colord({ r: 99, g: 202, b: 253 }), // Azure - colord({ r: 110, g: 231, b: 183 }), // Seafoam - colord({ r: 124, g: 58, b: 237 }), // Royal Purple - colord({ r: 125, g: 211, b: 252 }), // Crystal Blue - colord({ r: 132, g: 204, b: 22 }), // Lime - colord({ r: 133, g: 77, b: 14 }), // Chocolate - colord({ r: 134, g: 239, b: 172 }), // Light Green - colord({ r: 147, g: 51, b: 234 }), // Bright Purple - colord({ r: 147, g: 197, b: 253 }), // Powder Blue - colord({ r: 151, g: 255, b: 187 }), // Fresh Mint - colord({ r: 163, g: 230, b: 53 }), // Yellow Green - colord({ r: 167, g: 139, b: 250 }), // Periwinkle - colord({ r: 168, g: 85, b: 247 }), // Vibrant Purple - colord({ r: 179, g: 136, b: 255 }), // Light Purple - colord({ r: 186, g: 255, b: 201 }), // Pale Emerald - colord({ r: 190, g: 92, b: 251 }), // Amethyst - colord({ r: 192, g: 132, b: 252 }), // Lavender - colord({ r: 202, g: 138, b: 4 }), // Rich Gold - colord({ r: 202, g: 225, b: 255 }), // Baby Blue - colord({ r: 204, g: 204, b: 255 }), // Soft Lavender Blue - colord({ r: 217, g: 70, b: 239 }), // Fuchsia - colord({ r: 220, g: 38, b: 38 }), // Ruby - colord({ r: 220, g: 220, b: 255 }), // Meringue Blue - colord({ r: 220, g: 240, b: 250 }), // Ice Blue - colord({ r: 230, g: 250, b: 210 }), // Pastel Lime - colord({ r: 230, g: 255, b: 250 }), // Mint Whisper - colord({ r: 233, g: 213, b: 255 }), // Light Lilac - colord({ r: 234, g: 88, b: 12 }), // Burnt Orange - colord({ r: 234, g: 179, b: 8 }), // Sunflower - colord({ r: 235, g: 75, b: 75 }), // Bright Red - colord({ r: 236, g: 72, b: 153 }), // Deep Pink - colord({ r: 239, g: 68, b: 68 }), // Crimson - colord({ r: 240, g: 171, b: 252 }), // Orchid - colord({ r: 240, g: 240, b: 200 }), // Light Khaki - colord({ r: 244, g: 114, b: 182 }), // Rose - colord({ r: 245, g: 101, b: 101 }), // Coral - colord({ r: 245, g: 158, b: 11 }), // Amber - colord({ r: 248, g: 113, b: 113 }), // Warm Red - colord({ r: 249, g: 115, b: 22 }), // Tangerine - colord({ r: 250, g: 215, b: 225 }), // Cotton Candy - colord({ r: 250, g: 250, b: 210 }), // Pastel Lemon - colord({ r: 251, g: 113, b: 133 }), // Watermelon - colord({ r: 251, g: 146, b: 60 }), // Light Orange - colord({ r: 251, g: 191, b: 36 }), // Marigold - colord({ r: 251, g: 235, b: 245 }), // Rose Powder - colord({ r: 252, g: 165, b: 165 }), // Peach - colord({ r: 252, g: 211, b: 77 }), // Golden - colord({ r: 253, g: 164, b: 175 }), // Salmon Pink - colord({ r: 255, g: 204, b: 229 }), // Blush Pink - colord({ r: 255, g: 223, b: 186 }), // Apricot Cream - colord({ r: 255, g: 240, b: 200 }), // Vanilla + colord({ b: 129, g: 185, r: 16 }), // Sea Green + colord({ b: 94, g: 197, r: 34 }), // Emerald + colord({ b: 191, g: 212, r: 45 }), // Turquoise + colord({ b: 180, g: 178, r: 48 }), // Teal + colord({ b: 153, g: 211, r: 52 }), // Spearmint + colord({ b: 248, g: 189, r: 56 }), // Light Blue + colord({ b: 246, g: 130, r: 59 }), // Royal Blue + colord({ b: 84, g: 190, r: 67 }), // Fresh Green + colord({ b: 128, g: 222, r: 74 }), // Mint + colord({ b: 229, g: 70, r: 79 }), // Indigo + colord({ b: 136, g: 183, r: 82 }), // Jade + colord({ b: 250, g: 165, r: 96 }), // Sky Blue + colord({ b: 253, g: 202, r: 99 }), // Azure + colord({ b: 183, g: 231, r: 110 }), // Seafoam + colord({ b: 237, g: 58, r: 124 }), // Royal Purple + colord({ b: 252, g: 211, r: 125 }), // Crystal Blue + colord({ b: 22, g: 204, r: 132 }), // Lime + colord({ b: 14, g: 77, r: 133 }), // Chocolate + colord({ b: 172, g: 239, r: 134 }), // Light Green + colord({ b: 234, g: 51, r: 147 }), // Bright Purple + colord({ b: 253, g: 197, r: 147 }), // Powder Blue + colord({ b: 187, g: 255, r: 151 }), // Fresh Mint + colord({ b: 53, g: 230, r: 163 }), // Yellow Green + colord({ b: 250, g: 139, r: 167 }), // Periwinkle + colord({ b: 247, g: 85, r: 168 }), // Vibrant Purple + colord({ b: 255, g: 136, r: 179 }), // Light Purple + colord({ b: 201, g: 255, r: 186 }), // Pale Emerald + colord({ b: 251, g: 92, r: 190 }), // Amethyst + colord({ b: 252, g: 132, r: 192 }), // Lavender + colord({ b: 4, g: 138, r: 202 }), // Rich Gold + colord({ b: 255, g: 225, r: 202 }), // Baby Blue + colord({ b: 255, g: 204, r: 204 }), // Soft Lavender Blue + colord({ b: 239, g: 70, r: 217 }), // Fuchsia + colord({ b: 38, g: 38, r: 220 }), // Ruby + colord({ b: 255, g: 220, r: 220 }), // Meringue Blue + colord({ b: 250, g: 240, r: 220 }), // Ice Blue + colord({ b: 210, g: 250, r: 230 }), // Pastel Lime + colord({ b: 250, g: 255, r: 230 }), // Mint Whisper + colord({ b: 255, g: 213, r: 233 }), // Light Lilac + colord({ b: 12, g: 88, r: 234 }), // Burnt Orange + colord({ b: 8, g: 179, r: 234 }), // Sunflower + colord({ b: 75, g: 75, r: 235 }), // Bright Red + colord({ b: 153, g: 72, r: 236 }), // Deep Pink + colord({ b: 68, g: 68, r: 239 }), // Crimson + colord({ b: 252, g: 171, r: 240 }), // Orchid + colord({ b: 200, g: 240, r: 240 }), // Light Khaki + colord({ b: 182, g: 114, r: 244 }), // Rose + colord({ b: 101, g: 101, r: 245 }), // Coral + colord({ b: 11, g: 158, r: 245 }), // Amber + colord({ b: 113, g: 113, r: 248 }), // Warm Red + colord({ b: 22, g: 115, r: 249 }), // Tangerine + colord({ b: 225, g: 215, r: 250 }), // Cotton Candy + colord({ b: 210, g: 250, r: 250 }), // Pastel Lemon + colord({ b: 133, g: 113, r: 251 }), // Watermelon + colord({ b: 60, g: 146, r: 251 }), // Light Orange + colord({ b: 36, g: 191, r: 251 }), // Marigold + colord({ b: 245, g: 235, r: 251 }), // Rose Powder + colord({ b: 165, g: 165, r: 252 }), // Peach + colord({ b: 77, g: 211, r: 252 }), // Golden + colord({ b: 175, g: 164, r: 253 }), // Salmon Pink + colord({ b: 229, g: 204, r: 255 }), // Blush Pink + colord({ b: 186, g: 223, r: 255 }), // Apricot Cream + colord({ b: 200, g: 240, r: 255 }), // Vanilla ]; export const botColors: Colord[] = [ - colord({ r: 190, g: 120, b: 120 }), // Muted Red - colord({ r: 120, g: 160, b: 190 }), // Muted Sky Blue - colord({ r: 190, g: 160, b: 100 }), // Muted Golden Yellow - colord({ r: 160, g: 120, b: 190 }), // Muted Purple - colord({ r: 100, g: 170, b: 130 }), // Muted Emerald Green - colord({ r: 190, g: 130, b: 160 }), // Muted Pink - colord({ r: 120, g: 150, b: 100 }), // Muted Olive Green - colord({ r: 190, g: 140, b: 120 }), // Muted Peach - colord({ r: 100, g: 120, b: 160 }), // Muted Navy Blue - colord({ r: 170, g: 170, b: 120 }), // Muted Lime Yellow - colord({ r: 160, g: 120, b: 130 }), // Muted Maroon - colord({ r: 120, g: 170, b: 170 }), // Muted Turquoise - colord({ r: 170, g: 140, b: 100 }), // Muted Light Orange - colord({ r: 140, g: 120, b: 160 }), // Muted Lavender - colord({ r: 150, g: 170, b: 130 }), // Muted Light Green - colord({ r: 170, g: 120, b: 140 }), // Muted Hot Pink - colord({ r: 120, g: 140, b: 120 }), // Muted Sea Green - colord({ r: 180, g: 160, b: 160 }), // Muted Light Pink - colord({ r: 130, g: 130, b: 160 }), // Muted Periwinkle - colord({ r: 160, g: 150, b: 120 }), // Muted Sand - colord({ r: 120, g: 160, b: 150 }), // Muted Aquamarine - colord({ r: 170, g: 150, b: 170 }), // Muted Orchid - colord({ r: 150, g: 160, b: 120 }), // Muted Yellow Green - colord({ r: 120, g: 130, b: 140 }), // Muted Steel Blue - colord({ r: 180, g: 140, b: 140 }), // Muted Salmon - colord({ r: 140, g: 160, b: 170 }), // Muted Light Blue - colord({ r: 170, g: 150, b: 130 }), // Muted Tan - colord({ r: 160, g: 130, b: 160 }), // Muted Plum - colord({ r: 130, g: 170, b: 130 }), // Muted Light Sea Green - colord({ r: 170, g: 130, b: 130 }), // Muted Coral - colord({ r: 130, g: 150, b: 170 }), // Muted Cornflower Blue - colord({ r: 170, g: 170, b: 140 }), // Muted Khaki - colord({ r: 150, g: 130, b: 150 }), // Muted Purple Gray - colord({ r: 140, g: 160, b: 140 }), // Muted Dark Sea Green - colord({ r: 170, g: 130, b: 120 }), // Muted Dark Salmon - colord({ r: 130, g: 150, b: 160 }), // Muted Cadet Blue - colord({ r: 160, g: 160, b: 150 }), // Muted Tan Gray - colord({ r: 150, g: 140, b: 160 }), // Muted Medium Purple - colord({ r: 150, g: 170, b: 150 }), // Muted Pale Green - colord({ r: 160, g: 140, b: 130 }), // Muted Rosy Brown - colord({ r: 140, g: 150, b: 160 }), // Muted Light Slate Gray - colord({ r: 160, g: 150, b: 140 }), // Muted Dark Khaki - colord({ r: 140, g: 130, b: 140 }), // Muted Thistle - colord({ r: 150, g: 160, b: 160 }), // Muted Pale Blue Green - colord({ r: 160, g: 140, b: 150 }), // Muted Puce - colord({ r: 130, g: 160, b: 150 }), // Muted Medium Aquamarine - colord({ r: 160, g: 150, b: 160 }), // Muted Mauve - colord({ r: 150, g: 160, b: 140 }), // Muted Dark Olive Green - colord({ r: 150, g: 140, b: 150 }), // Muted Dusty Rose + colord({ b: 120, g: 120, r: 190 }), // Muted Red + colord({ b: 190, g: 160, r: 120 }), // Muted Sky Blue + colord({ b: 100, g: 160, r: 190 }), // Muted Golden Yellow + colord({ b: 190, g: 120, r: 160 }), // Muted Purple + colord({ b: 130, g: 170, r: 100 }), // Muted Emerald Green + colord({ b: 160, g: 130, r: 190 }), // Muted Pink + colord({ b: 100, g: 150, r: 120 }), // Muted Olive Green + colord({ b: 120, g: 140, r: 190 }), // Muted Peach + colord({ b: 160, g: 120, r: 100 }), // Muted Navy Blue + colord({ b: 120, g: 170, r: 170 }), // Muted Lime Yellow + colord({ b: 130, g: 120, r: 160 }), // Muted Maroon + colord({ b: 170, g: 170, r: 120 }), // Muted Turquoise + colord({ b: 100, g: 140, r: 170 }), // Muted Light Orange + colord({ b: 160, g: 120, r: 140 }), // Muted Lavender + colord({ b: 130, g: 170, r: 150 }), // Muted Light Green + colord({ b: 140, g: 120, r: 170 }), // Muted Hot Pink + colord({ b: 120, g: 140, r: 120 }), // Muted Sea Green + colord({ b: 160, g: 160, r: 180 }), // Muted Light Pink + colord({ b: 160, g: 130, r: 130 }), // Muted Periwinkle + colord({ b: 120, g: 150, r: 160 }), // Muted Sand + colord({ b: 150, g: 160, r: 120 }), // Muted Aquamarine + colord({ b: 170, g: 150, r: 170 }), // Muted Orchid + colord({ b: 120, g: 160, r: 150 }), // Muted Yellow Green + colord({ b: 140, g: 130, r: 120 }), // Muted Steel Blue + colord({ b: 140, g: 140, r: 180 }), // Muted Salmon + colord({ b: 170, g: 160, r: 140 }), // Muted Light Blue + colord({ b: 130, g: 150, r: 170 }), // Muted Tan + colord({ b: 160, g: 130, r: 160 }), // Muted Plum + colord({ b: 130, g: 170, r: 130 }), // Muted Light Sea Green + colord({ b: 130, g: 130, r: 170 }), // Muted Coral + colord({ b: 170, g: 150, r: 130 }), // Muted Cornflower Blue + colord({ b: 140, g: 170, r: 170 }), // Muted Khaki + colord({ b: 150, g: 130, r: 150 }), // Muted Purple Gray + colord({ b: 140, g: 160, r: 140 }), // Muted Dark Sea Green + colord({ b: 120, g: 130, r: 170 }), // Muted Dark Salmon + colord({ b: 160, g: 150, r: 130 }), // Muted Cadet Blue + colord({ b: 150, g: 160, r: 160 }), // Muted Tan Gray + colord({ b: 160, g: 140, r: 150 }), // Muted Medium Purple + colord({ b: 150, g: 170, r: 150 }), // Muted Pale Green + colord({ b: 130, g: 140, r: 160 }), // Muted Rosy Brown + colord({ b: 160, g: 150, r: 140 }), // Muted Light Slate Gray + colord({ b: 140, g: 150, r: 160 }), // Muted Dark Khaki + colord({ b: 140, g: 130, r: 140 }), // Muted Thistle + colord({ b: 160, g: 160, r: 150 }), // Muted Pale Blue Green + colord({ b: 150, g: 140, r: 160 }), // Muted Puce + colord({ b: 150, g: 160, r: 130 }), // Muted Medium Aquamarine + colord({ b: 160, g: 150, r: 160 }), // Muted Mauve + colord({ b: 140, g: 160, r: 150 }), // Muted Dark Olive Green + colord({ b: 150, g: 140, r: 150 }), // Muted Dusty Rose ]; // Fallback colors for when the color palette is exhausted. Currently 100 colors. export const fallbackColors: Colord[] = [ - colord({ r: 0, g: 5, b: 0 }), // Black Mint - colord({ r: 0, g: 15, b: 0 }), // Deep Forest - colord({ r: 0, g: 25, b: 0 }), // Jungle - colord({ r: 0, g: 35, b: 0 }), // Dark Emerald - colord({ r: 0, g: 45, b: 0 }), // Green Moss - colord({ r: 0, g: 55, b: 0 }), // Moss Shadow - colord({ r: 0, g: 65, b: 0 }), // Dark Meadow - colord({ r: 0, g: 75, b: 0 }), // Forest Fern - colord({ r: 0, g: 85, b: 0 }), // Pine Leaf - colord({ r: 0, g: 95, b: 0 }), // Shadow Grass - colord({ r: 0, g: 105, b: 0 }), // Classic Green - colord({ r: 0, g: 115, b: 0 }), // Deep Lime - colord({ r: 0, g: 125, b: 0 }), // Dense Leaf - colord({ r: 0, g: 135, b: 0 }), // Basil Green - colord({ r: 0, g: 145, b: 0 }), // Organic Green - colord({ r: 0, g: 155, b: 0 }), // Bitter Herb - colord({ r: 0, g: 165, b: 0 }), // Raw Spinach - colord({ r: 0, g: 175, b: 0 }), // Woodland - colord({ r: 0, g: 185, b: 0 }), // Spring Weed - colord({ r: 0, g: 195, b: 5 }), // Apple Stem - colord({ r: 0, g: 205, b: 10 }), // Crisp Lettuce - colord({ r: 0, g: 215, b: 15 }), // Vibrant Green - colord({ r: 0, g: 225, b: 20 }), // Bright Herb - colord({ r: 0, g: 235, b: 25 }), // Green Splash - colord({ r: 0, g: 245, b: 30 }), // Mint Leaf - colord({ r: 0, g: 255, b: 35 }), // Fresh Mint - colord({ r: 10, g: 255, b: 45 }), // Neon Grass - colord({ r: 20, g: 255, b: 55 }), // Lemon Balm - colord({ r: 30, g: 255, b: 65 }), // Juicy Green - colord({ r: 40, g: 255, b: 75 }), // Pear Tint - colord({ r: 50, g: 255, b: 85 }), // Avocado Pastel - colord({ r: 60, g: 255, b: 95 }), // Lime Glow - colord({ r: 70, g: 255, b: 105 }), // Light Leaf - colord({ r: 80, g: 255, b: 115 }), // Soft Fern - colord({ r: 90, g: 255, b: 125 }), // Pastel Green - colord({ r: 100, g: 255, b: 135 }), // Green Melon - colord({ r: 110, g: 255, b: 145 }), // Herbal Mist - colord({ r: 120, g: 255, b: 155 }), // Kiwi Foam - colord({ r: 130, g: 255, b: 165 }), // Aloe Fresh - colord({ r: 140, g: 255, b: 175 }), // Light Mint - colord({ r: 150, g: 200, b: 255 }), // Cornflower Mist - colord({ r: 150, g: 255, b: 185 }), // Green Sorbet - colord({ r: 160, g: 215, b: 255 }), // Powder Blue - colord({ r: 160, g: 255, b: 195 }), // Pastel Apple - colord({ r: 170, g: 190, b: 255 }), // Periwinkle Ice - colord({ r: 170, g: 225, b: 255 }), // Baby Sky - colord({ r: 170, g: 255, b: 205 }), // Aloe Breeze - colord({ r: 180, g: 180, b: 255 }), // Pale Indigo - colord({ r: 180, g: 235, b: 250 }), // Aqua Pastel - colord({ r: 180, g: 255, b: 215 }), // Pale Mint - colord({ r: 190, g: 140, b: 195 }), // Fuchsia Tint - colord({ r: 190, g: 245, b: 240 }), // Ice Mint - colord({ r: 190, g: 255, b: 225 }), // Mint Water - colord({ r: 195, g: 145, b: 200 }), // Dusky Rose - colord({ r: 200, g: 150, b: 205 }), // Plum Frost - colord({ r: 200, g: 170, b: 255 }), // Lilac Bloom - colord({ r: 200, g: 255, b: 215 }), // Cool Aloe - colord({ r: 200, g: 255, b: 235 }), // Cool Mist - colord({ r: 205, g: 155, b: 210 }), // Berry Foam - colord({ r: 210, g: 160, b: 215 }), // Grape Cloud - colord({ r: 210, g: 255, b: 245 }), // Sea Mist - colord({ r: 215, g: 165, b: 220 }), // Light Bloom - colord({ r: 215, g: 255, b: 200 }), // Fresh Mint - colord({ r: 220, g: 160, b: 255 }), // Violet Mist - colord({ r: 220, g: 170, b: 225 }), // Cherry Blossom - colord({ r: 220, g: 255, b: 255 }), // Pale Aqua - colord({ r: 225, g: 175, b: 230 }), // Faded Rose - colord({ r: 225, g: 255, b: 175 }), // Soft Lime - colord({ r: 230, g: 180, b: 235 }), // Dreamy Mauve - colord({ r: 230, g: 250, b: 255 }), // Sky Haze - colord({ r: 235, g: 150, b: 255 }), // Orchid Glow - colord({ r: 235, g: 185, b: 240 }), // Powder Violet - colord({ r: 240, g: 190, b: 245 }), // Pastel Violet - colord({ r: 240, g: 240, b: 255 }), // Frosted Lilac - colord({ r: 240, g: 250, b: 160 }), // Citrus Wash - colord({ r: 245, g: 160, b: 240 }), // Rose Lilac - colord({ r: 245, g: 195, b: 250 }), // Soft Magenta - colord({ r: 245, g: 245, b: 175 }), // Lemon Mist - colord({ r: 250, g: 200, b: 255 }), // Lilac Cream - colord({ r: 250, g: 230, b: 255 }), // Misty Mauve - colord({ r: 255, g: 170, b: 225 }), // Bubblegum Pink - colord({ r: 255, g: 185, b: 215 }), // Blush Mist - colord({ r: 255, g: 195, b: 235 }), // Faded Fuchsia - colord({ r: 255, g: 200, b: 220 }), // Cotton Rose - colord({ r: 255, g: 205, b: 245 }), // Pastel Orchid - colord({ r: 255, g: 205, b: 255 }), // Violet Bloom - colord({ r: 255, g: 210, b: 230 }), // Pastel Blush - colord({ r: 255, g: 210, b: 250 }), // Lavender Mist - colord({ r: 255, g: 210, b: 255 }), // Orchid Mist - colord({ r: 255, g: 215, b: 195 }), // Apricot Glow - colord({ r: 255, g: 215, b: 245 }), // Rose Whisper - colord({ r: 255, g: 220, b: 235 }), // Pink Mist - colord({ r: 255, g: 220, b: 250 }), // Powder Petal - colord({ r: 255, g: 225, b: 180 }), // Butter Peach - colord({ r: 255, g: 225, b: 255 }), // Petal Mist - colord({ r: 255, g: 230, b: 245 }), // Light Rose - colord({ r: 255, g: 235, b: 200 }), // Cream Peach - colord({ r: 255, g: 235, b: 235 }), // Blushed Petal - colord({ r: 255, g: 240, b: 220 }), // Pastel Sand - colord({ r: 255, g: 245, b: 210 }), // Soft Banana + colord({ b: 0, g: 5, r: 0 }), // Black Mint + colord({ b: 0, g: 15, r: 0 }), // Deep Forest + colord({ b: 0, g: 25, r: 0 }), // Jungle + colord({ b: 0, g: 35, r: 0 }), // Dark Emerald + colord({ b: 0, g: 45, r: 0 }), // Green Moss + colord({ b: 0, g: 55, r: 0 }), // Moss Shadow + colord({ b: 0, g: 65, r: 0 }), // Dark Meadow + colord({ b: 0, g: 75, r: 0 }), // Forest Fern + colord({ b: 0, g: 85, r: 0 }), // Pine Leaf + colord({ b: 0, g: 95, r: 0 }), // Shadow Grass + colord({ b: 0, g: 105, r: 0 }), // Classic Green + colord({ b: 0, g: 115, r: 0 }), // Deep Lime + colord({ b: 0, g: 125, r: 0 }), // Dense Leaf + colord({ b: 0, g: 135, r: 0 }), // Basil Green + colord({ b: 0, g: 145, r: 0 }), // Organic Green + colord({ b: 0, g: 155, r: 0 }), // Bitter Herb + colord({ b: 0, g: 165, r: 0 }), // Raw Spinach + colord({ b: 0, g: 175, r: 0 }), // Woodland + colord({ b: 0, g: 185, r: 0 }), // Spring Weed + colord({ b: 5, g: 195, r: 0 }), // Apple Stem + colord({ b: 10, g: 205, r: 0 }), // Crisp Lettuce + colord({ b: 15, g: 215, r: 0 }), // Vibrant Green + colord({ b: 20, g: 225, r: 0 }), // Bright Herb + colord({ b: 25, g: 235, r: 0 }), // Green Splash + colord({ b: 30, g: 245, r: 0 }), // Mint Leaf + colord({ b: 35, g: 255, r: 0 }), // Fresh Mint + colord({ b: 45, g: 255, r: 10 }), // Neon Grass + colord({ b: 55, g: 255, r: 20 }), // Lemon Balm + colord({ b: 65, g: 255, r: 30 }), // Juicy Green + colord({ b: 75, g: 255, r: 40 }), // Pear Tint + colord({ b: 85, g: 255, r: 50 }), // Avocado Pastel + colord({ b: 95, g: 255, r: 60 }), // Lime Glow + colord({ b: 105, g: 255, r: 70 }), // Light Leaf + colord({ b: 115, g: 255, r: 80 }), // Soft Fern + colord({ b: 125, g: 255, r: 90 }), // Pastel Green + colord({ b: 135, g: 255, r: 100 }), // Green Melon + colord({ b: 145, g: 255, r: 110 }), // Herbal Mist + colord({ b: 155, g: 255, r: 120 }), // Kiwi Foam + colord({ b: 165, g: 255, r: 130 }), // Aloe Fresh + colord({ b: 175, g: 255, r: 140 }), // Light Mint + colord({ b: 255, g: 200, r: 150 }), // Cornflower Mist + colord({ b: 185, g: 255, r: 150 }), // Green Sorbet + colord({ b: 255, g: 215, r: 160 }), // Powder Blue + colord({ b: 195, g: 255, r: 160 }), // Pastel Apple + colord({ b: 255, g: 190, r: 170 }), // Periwinkle Ice + colord({ b: 255, g: 225, r: 170 }), // Baby Sky + colord({ b: 205, g: 255, r: 170 }), // Aloe Breeze + colord({ b: 255, g: 180, r: 180 }), // Pale Indigo + colord({ b: 250, g: 235, r: 180 }), // Aqua Pastel + colord({ b: 215, g: 255, r: 180 }), // Pale Mint + colord({ b: 195, g: 140, r: 190 }), // Fuchsia Tint + colord({ b: 240, g: 245, r: 190 }), // Ice Mint + colord({ b: 225, g: 255, r: 190 }), // Mint Water + colord({ b: 200, g: 145, r: 195 }), // Dusky Rose + colord({ b: 205, g: 150, r: 200 }), // Plum Frost + colord({ b: 255, g: 170, r: 200 }), // Lilac Bloom + colord({ b: 215, g: 255, r: 200 }), // Cool Aloe + colord({ b: 235, g: 255, r: 200 }), // Cool Mist + colord({ b: 210, g: 155, r: 205 }), // Berry Foam + colord({ b: 215, g: 160, r: 210 }), // Grape Cloud + colord({ b: 245, g: 255, r: 210 }), // Sea Mist + colord({ b: 220, g: 165, r: 215 }), // Light Bloom + colord({ b: 200, g: 255, r: 215 }), // Fresh Mint + colord({ b: 255, g: 160, r: 220 }), // Violet Mist + colord({ b: 225, g: 170, r: 220 }), // Cherry Blossom + colord({ b: 255, g: 255, r: 220 }), // Pale Aqua + colord({ b: 230, g: 175, r: 225 }), // Faded Rose + colord({ b: 175, g: 255, r: 225 }), // Soft Lime + colord({ b: 235, g: 180, r: 230 }), // Dreamy Mauve + colord({ b: 255, g: 250, r: 230 }), // Sky Haze + colord({ b: 255, g: 150, r: 235 }), // Orchid Glow + colord({ b: 240, g: 185, r: 235 }), // Powder Violet + colord({ b: 245, g: 190, r: 240 }), // Pastel Violet + colord({ b: 255, g: 240, r: 240 }), // Frosted Lilac + colord({ b: 160, g: 250, r: 240 }), // Citrus Wash + colord({ b: 240, g: 160, r: 245 }), // Rose Lilac + colord({ b: 250, g: 195, r: 245 }), // Soft Magenta + colord({ b: 175, g: 245, r: 245 }), // Lemon Mist + colord({ b: 255, g: 200, r: 250 }), // Lilac Cream + colord({ b: 255, g: 230, r: 250 }), // Misty Mauve + colord({ b: 225, g: 170, r: 255 }), // Bubblegum Pink + colord({ b: 215, g: 185, r: 255 }), // Blush Mist + colord({ b: 235, g: 195, r: 255 }), // Faded Fuchsia + colord({ b: 220, g: 200, r: 255 }), // Cotton Rose + colord({ b: 245, g: 205, r: 255 }), // Pastel Orchid + colord({ b: 255, g: 205, r: 255 }), // Violet Bloom + colord({ b: 230, g: 210, r: 255 }), // Pastel Blush + colord({ b: 250, g: 210, r: 255 }), // Lavender Mist + colord({ b: 255, g: 210, r: 255 }), // Orchid Mist + colord({ b: 195, g: 215, r: 255 }), // Apricot Glow + colord({ b: 245, g: 215, r: 255 }), // Rose Whisper + colord({ b: 235, g: 220, r: 255 }), // Pink Mist + colord({ b: 250, g: 220, r: 255 }), // Powder Petal + colord({ b: 180, g: 225, r: 255 }), // Butter Peach + colord({ b: 255, g: 225, r: 255 }), // Petal Mist + colord({ b: 245, g: 230, r: 255 }), // Light Rose + colord({ b: 200, g: 235, r: 255 }), // Cream Peach + colord({ b: 235, g: 235, r: 255 }), // Blushed Petal + colord({ b: 220, g: 240, r: 255 }), // Pastel Sand + colord({ b: 210, g: 245, r: 255 }), // Soft Banana ]; diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts index fe1e2525aa..4e32ca37c6 100644 --- a/src/core/configuration/Config.ts +++ b/src/core/configuration/Config.ts @@ -1,3 +1,5 @@ +import { Colord } from "colord"; +import { JWK } from "jose"; import { Difficulty, Game, @@ -12,13 +14,11 @@ import { UnitInfo, UnitType, } from "../game/Game"; -import { GameConfig, GameID, TeamCountConfig } from "../Schemas"; import { GameMap, TileRef } from "../game/GameMap"; -import { Colord } from "colord"; -import { JWK } from "jose"; -import { NukeType } from "../StatsSchemas"; import { PlayerView } from "../game/GameView"; import { UserSettings } from "../game/UserSettings"; +import { GameConfig, GameID, TeamCountConfig } from "../Schemas"; +import { NukeType } from "../StatsSchemas"; export enum GameEnv { Dev, diff --git a/src/core/configuration/ConfigLoader.ts b/src/core/configuration/ConfigLoader.ts index 19667b45ec..3bb7f53fc7 100644 --- a/src/core/configuration/ConfigLoader.ts +++ b/src/core/configuration/ConfigLoader.ts @@ -1,9 +1,9 @@ -import { Config, GameEnv, ServerConfig } from "./Config"; -import { DevConfig, DevServerConfig } from "./DevConfig"; import { ApiEnvResponseSchema } from "../ExpressSchemas"; -import { DefaultConfig } from "./DefaultConfig"; -import { GameConfig } from "../Schemas"; import { UserSettings } from "../game/UserSettings"; +import { GameConfig } from "../Schemas"; +import { Config, GameEnv, ServerConfig } from "./Config"; +import { DefaultConfig } from "./DefaultConfig"; +import { DevConfig, DevServerConfig } from "./DevConfig"; import { preprodConfig } from "./PreprodConfig"; import { prodConfig } from "./ProdConfig"; diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index eddf481fe2..9f6eb23759 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -1,5 +1,7 @@ /* eslint-disable max-lines */ -import { Config, GameEnv, NukeMagnitude, ServerConfig, Theme } from "./Config"; + +import { JWK } from "jose"; +import { z } from "zod"; import { Difficulty, Duos, @@ -12,23 +14,22 @@ import { PlayerInfo, PlayerType, Quads, - TerraNullius, TerrainType, + TerraNullius, Tick, Trios, UnitInfo, UnitType, } from "../game/Game"; +import { TileRef } from "../game/GameMap"; +import { PlayerView } from "../game/GameView"; +import { UserSettings } from "../game/UserSettings"; import { GameConfig, GameID, TeamCountConfig } from "../Schemas"; -import { assertNever, simpleHash, within } from "../Util"; -import { JWK } from "jose"; import { NukeType } from "../StatsSchemas"; +import { assertNever, simpleHash, within } from "../Util"; +import { Config, GameEnv, NukeMagnitude, ServerConfig, Theme } from "./Config"; import { PastelTheme } from "./PastelTheme"; import { PastelThemeDark } from "./PastelThemeDark"; -import { PlayerView } from "../game/GameView"; -import { TileRef } from "../game/GameMap"; -import { UserSettings } from "../game/UserSettings"; -import { z } from "zod"; const JwksSchema = z.object({ keys: z @@ -375,7 +376,6 @@ export class DefaultConfig implements Config { return 1_000_000; } - /* eslint-disable sort-keys */ unitInfo(type: UnitType): UnitInfo { switch (type) { case UnitType.TransportShip: @@ -388,14 +388,14 @@ export class DefaultConfig implements Config { cost: this.costWrapper(UnitType.Warship, (numUnits: number) => Math.min(1_000_000, (numUnits + 1) * 250_000), ), - territoryBound: false, maxHealth: 1000, + territoryBound: false, }; case UnitType.Shell: return { cost: () => 0n, - territoryBound: false, damage: 250, + territoryBound: false, }; case UnitType.SAMMissile: return { @@ -404,13 +404,13 @@ export class DefaultConfig implements Config { }; case UnitType.Port: return { + canBuildTrainStation: true, + constructionDuration: this.instantBuild() ? 0 : 2 * 10, cost: this.costWrapper(UnitType.Port, (numUnits: number) => Math.min(1_000_000, Math.pow(2, numUnits) * 125_000), ), territoryBound: true, - constructionDuration: this.instantBuild() ? 0 : 2 * 10, upgradable: true, - canBuildTrainStation: true, }; case UnitType.AtomBomb: return { @@ -439,47 +439,47 @@ export class DefaultConfig implements Config { }; case UnitType.MissileSilo: return { + constructionDuration: this.instantBuild() ? 0 : 10 * 10, cost: this.costWrapper(UnitType.MissileSilo, () => 1_000_000), territoryBound: true, - constructionDuration: this.instantBuild() ? 0 : 10 * 10, upgradable: true, }; case UnitType.DefensePost: return { + constructionDuration: this.instantBuild() ? 0 : 5 * 10, cost: this.costWrapper(UnitType.DefensePost, (numUnits: number) => Math.min(250_000, (numUnits + 1) * 50_000), ), territoryBound: true, - constructionDuration: this.instantBuild() ? 0 : 5 * 10, }; case UnitType.SAMLauncher: return { + constructionDuration: this.instantBuild() ? 0 : 30 * 10, cost: this.costWrapper(UnitType.SAMLauncher, (numUnits: number) => Math.min(3_000_000, (numUnits + 1) * 1_500_000), ), territoryBound: true, - constructionDuration: this.instantBuild() ? 0 : 30 * 10, upgradable: true, }; case UnitType.City: return { + canBuildTrainStation: true, + constructionDuration: this.instantBuild() ? 0 : 2 * 10, cost: this.costWrapper(UnitType.City, (numUnits: number) => Math.min(1_000_000, Math.pow(2, numUnits) * 125_000), ), territoryBound: true, - constructionDuration: this.instantBuild() ? 0 : 2 * 10, upgradable: true, - canBuildTrainStation: true, }; case UnitType.Factory: return { + canBuildTrainStation: true, + constructionDuration: this.instantBuild() ? 0 : 2 * 10, cost: this.costWrapper(UnitType.Factory, (numUnits: number) => Math.min(1_000_000, Math.pow(2, numUnits) * 125_000), ), - territoryBound: true, - constructionDuration: this.instantBuild() ? 0 : 2 * 10, - canBuildTrainStation: true, experimental: true, + territoryBound: true, upgradable: true, }; case UnitType.Construction: @@ -490,14 +490,13 @@ export class DefaultConfig implements Config { case UnitType.Train: return { cost: () => 0n, - territoryBound: false, experimental: true, + territoryBound: false, }; default: assertNever(type); } } - /* eslint-enable sort-keys */ private costWrapper( type: UnitType, diff --git a/src/core/configuration/DevConfig.ts b/src/core/configuration/DevConfig.ts index 6f20d5df69..3a14b39bba 100644 --- a/src/core/configuration/DevConfig.ts +++ b/src/core/configuration/DevConfig.ts @@ -1,8 +1,8 @@ -import { DefaultConfig, DefaultServerConfig } from "./DefaultConfig"; -import { GameEnv, ServerConfig } from "./Config"; import { UnitInfo, UnitType } from "../game/Game"; -import { GameConfig } from "../Schemas"; import { UserSettings } from "../game/UserSettings"; +import { GameConfig } from "../Schemas"; +import { GameEnv, ServerConfig } from "./Config"; +import { DefaultConfig, DefaultServerConfig } from "./DefaultConfig"; export class DevServerConfig extends DefaultServerConfig { adminToken(): string { diff --git a/src/core/configuration/PastelTheme.ts b/src/core/configuration/PastelTheme.ts index 0c72cfe139..29964a26c1 100644 --- a/src/core/configuration/PastelTheme.ts +++ b/src/core/configuration/PastelTheme.ts @@ -1,10 +1,10 @@ import { Colord, colord } from "colord"; -import { GameMap, TileRef } from "../game/GameMap"; import { PlayerType, Team, TerrainType } from "../game/Game"; -import { botColors, fallbackColors, humanColors, nationColors } from "./Colors"; -import { ColorAllocator } from "./ColorAllocator"; +import { GameMap, TileRef } from "../game/GameMap"; import { PlayerView } from "../game/GameView"; import { PseudoRandom } from "../PseudoRandom"; +import { ColorAllocator } from "./ColorAllocator"; +import { botColors, fallbackColors, humanColors, nationColors } from "./Colors"; import { Theme } from "./Config"; type ColorCache = Map; @@ -12,31 +12,38 @@ type ColorCache = Map; export class PastelTheme implements Theme { private readonly borderColorCache: ColorCache = new Map(); private readonly rand = new PseudoRandom(123); - private readonly humanColorAllocator = new ColorAllocator(humanColors, fallbackColors); + private readonly humanColorAllocator = new ColorAllocator( + humanColors, + fallbackColors, + ); private readonly botColorAllocator = new ColorAllocator(botColors, botColors); - private readonly teamColorAllocator = new ColorAllocator(humanColors, fallbackColors); - private readonly nationColorAllocator = new ColorAllocator(nationColors, nationColors); - - /* eslint-disable sort-keys */ - private readonly background = colord({ r: 60, g: 60, b: 60 }); - private readonly shore = colord({ r: 204, g: 203, b: 158 }); + private readonly teamColorAllocator = new ColorAllocator( + humanColors, + fallbackColors, + ); + private readonly nationColorAllocator = new ColorAllocator( + nationColors, + nationColors, + ); + + private readonly background = colord({ b: 60, g: 60, r: 60 }); + private readonly shore = colord({ b: 158, g: 203, r: 204 }); private readonly falloutColors = [ - colord({ r: 120, g: 255, b: 71 }), // Original color - colord({ r: 130, g: 255, b: 85 }), // Slightly lighter - colord({ r: 110, g: 245, b: 65 }), // Slightly darker - colord({ r: 125, g: 255, b: 75 }), // Warmer tint - colord({ r: 115, g: 250, b: 68 }), // Cooler tint + colord({ b: 71, g: 255, r: 120 }), // Original color + colord({ b: 85, g: 255, r: 130 }), // Slightly lighter + colord({ b: 65, g: 245, r: 110 }), // Slightly darker + colord({ b: 75, g: 255, r: 125 }), // Warmer tint + colord({ b: 68, g: 250, r: 115 }), // Cooler tint ]; - private readonly water = colord({ r: 70, g: 132, b: 180 }); - private readonly shorelineWater = colord({ r: 100, g: 143, b: 255 }); + private readonly water = colord({ b: 180, g: 132, r: 70 }); + private readonly shorelineWater = colord({ b: 255, g: 143, r: 100 }); - private readonly _selfColor = colord({ r: 0, g: 255, b: 0 }); - private readonly _allyColor = colord({ r: 255, g: 255, b: 0 }); - private readonly _neutralColor = colord({ r: 128, g: 128, b: 128 }); - private readonly _enemyColor = colord({ r: 255, g: 0, b: 0 }); + private readonly _selfColor = colord({ b: 0, g: 255, r: 0 }); + private readonly _allyColor = colord({ b: 0, g: 255, r: 255 }); + private readonly _neutralColor = colord({ b: 128, g: 128, r: 128 }); + private readonly _enemyColor = colord({ b: 0, g: 0, r: 255 }); - private readonly _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 }); - /* eslint-enable sort-keys */ + private readonly _spawnHighlightColor = colord({ b: 79, g: 213, r: 255 }); teamColor(team: Team): Colord { return this.teamColorAllocator.assignTeamColor(team); @@ -62,24 +69,23 @@ export class PastelTheme implements Theme { specialBuildingColor(player: PlayerView): Colord { const tc = this.territoryColor(player).rgba; - /* eslint-disable sort-keys */ + return colord({ - r: Math.max(tc.r - 50, 0), - g: Math.max(tc.g - 50, 0), b: Math.max(tc.b - 50, 0), + g: Math.max(tc.g - 50, 0), + r: Math.max(tc.r - 50, 0), }); - /* eslint-enable sort-keys */ } railroadColor(player: PlayerView): Colord { const tc = this.territoryColor(player).rgba; - /* eslint-disable sort-keys */ + const color = colord({ - r: Math.max(tc.r - 10, 0), - g: Math.max(tc.g - 10, 0), b: Math.max(tc.b - 10, 0), + g: Math.max(tc.g - 10, 0), + r: Math.max(tc.r - 10, 0), }); - /* eslint-enable sort-keys */ + return color; } @@ -88,28 +94,26 @@ export class PastelTheme implements Theme { if (cached !== undefined) return cached; const tc = this.territoryColor(player).rgba; - /* eslint-disable sort-keys */ + const color = colord({ - r: Math.max(tc.r - 40, 0), - g: Math.max(tc.g - 40, 0), b: Math.max(tc.b - 40, 0), + g: Math.max(tc.g - 40, 0), + r: Math.max(tc.r - 40, 0), }); - /* eslint-enable sort-keys */ this.borderColorCache.set(player.id(), color); return color; } - /* eslint-disable sort-keys */ defendedBorderColors(player: PlayerView): { light: Colord; dark: Colord } { return { - light: this.territoryColor(player).darken(0.2), dark: this.territoryColor(player).darken(0.4), + light: this.territoryColor(player).darken(0.2), }; } focusedBorderColor(): Colord { - return colord({ r: 230, g: 230, b: 230 }); + return colord({ b: 230, g: 230, r: 230 }); } terrainColor(gm: GameMap, tile: TileRef): Colord { @@ -125,32 +129,31 @@ export class PastelTheme implements Theme { return this.shorelineWater; } return colord({ - r: Math.max(w.r - 10 + (11 - Math.min(mag, 10)), 0), - g: Math.max(w.g - 10 + (11 - Math.min(mag, 10)), 0), b: Math.max(w.b - 10 + (11 - Math.min(mag, 10)), 0), + g: Math.max(w.g - 10 + (11 - Math.min(mag, 10)), 0), + r: Math.max(w.r - 10 + (11 - Math.min(mag, 10)), 0), }); case TerrainType.Plains: return colord({ - r: 190, - g: 220 - 2 * mag, b: 138, + g: 220 - 2 * mag, + r: 190, }); case TerrainType.Highland: return colord({ - r: 200 + 2 * mag, - g: 183 + 2 * mag, b: 138 + 2 * mag, + g: 183 + 2 * mag, + r: 200 + 2 * mag, }); case TerrainType.Mountain: return colord({ - r: 230 + mag / 2, - g: 230 + mag / 2, b: 230 + mag / 2, + g: 230 + mag / 2, + r: 230 + mag / 2, }); } } - /* eslint-enable sort-keys */ backgroundColor(): Colord { return this.background; diff --git a/src/core/configuration/PastelThemeDark.ts b/src/core/configuration/PastelThemeDark.ts index cfed90ba55..9d12b5d946 100644 --- a/src/core/configuration/PastelThemeDark.ts +++ b/src/core/configuration/PastelThemeDark.ts @@ -1,10 +1,10 @@ import { Colord, colord } from "colord"; -import { GameMap, TileRef } from "../game/GameMap"; import { PlayerType, Team, TerrainType } from "../game/Game"; -import { botColors, fallbackColors, humanColors, nationColors } from "./Colors"; -import { ColorAllocator } from "./ColorAllocator"; +import { GameMap, TileRef } from "../game/GameMap"; import { PlayerView } from "../game/GameView"; import { PseudoRandom } from "../PseudoRandom"; +import { ColorAllocator } from "./ColorAllocator"; +import { botColors, fallbackColors, humanColors, nationColors } from "./Colors"; import { Theme } from "./Config"; type ColorCache = Map; @@ -12,31 +12,38 @@ type ColorCache = Map; export class PastelThemeDark implements Theme { private readonly borderColorCache: ColorCache = new Map(); private readonly rand = new PseudoRandom(123); - private readonly humanColorAllocator = new ColorAllocator(humanColors, fallbackColors); + private readonly humanColorAllocator = new ColorAllocator( + humanColors, + fallbackColors, + ); private readonly botColorAllocator = new ColorAllocator(botColors, botColors); - private readonly teamColorAllocator = new ColorAllocator(humanColors, fallbackColors); - private readonly nationColorAllocator = new ColorAllocator(nationColors, nationColors); - - /* eslint-disable sort-keys */ - private readonly background = colord({ r: 0, g: 0, b: 0 }); - private readonly shore = colord({ r: 134, g: 133, b: 88 }); + private readonly teamColorAllocator = new ColorAllocator( + humanColors, + fallbackColors, + ); + private readonly nationColorAllocator = new ColorAllocator( + nationColors, + nationColors, + ); + + private readonly background = colord({ b: 0, g: 0, r: 0 }); + private readonly shore = colord({ b: 88, g: 133, r: 134 }); private readonly falloutColors = [ - colord({ r: 120, g: 255, b: 71 }), // Original color - colord({ r: 130, g: 255, b: 85 }), // Slightly lighter - colord({ r: 110, g: 245, b: 65 }), // Slightly darker - colord({ r: 125, g: 255, b: 75 }), // Warmer tint - colord({ r: 115, g: 250, b: 68 }), // Cooler tint + colord({ b: 71, g: 255, r: 120 }), // Original color + colord({ b: 85, g: 255, r: 130 }), // Slightly lighter + colord({ b: 65, g: 245, r: 110 }), // Slightly darker + colord({ b: 75, g: 255, r: 125 }), // Warmer tint + colord({ b: 68, g: 250, r: 115 }), // Cooler tint ]; - private readonly water = colord({ r: 14, g: 11, b: 30 }); - private readonly shorelineWater = colord({ r: 50, g: 50, b: 50 }); + private readonly water = colord({ b: 30, g: 11, r: 14 }); + private readonly shorelineWater = colord({ b: 50, g: 50, r: 50 }); - private readonly _selfColor = colord({ r: 0, g: 255, b: 0 }); - private readonly _allyColor = colord({ r: 255, g: 255, b: 0 }); - private readonly _neutralColor = colord({ r: 128, g: 128, b: 128 }); - private readonly _enemyColor = colord({ r: 255, g: 0, b: 0 }); + private readonly _selfColor = colord({ b: 0, g: 255, r: 0 }); + private readonly _allyColor = colord({ b: 0, g: 255, r: 255 }); + private readonly _neutralColor = colord({ b: 128, g: 128, r: 128 }); + private readonly _enemyColor = colord({ b: 0, g: 0, r: 255 }); - private readonly _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 }); - /* eslint-enable sort-keys */ + private readonly _spawnHighlightColor = colord({ b: 79, g: 213, r: 255 }); teamColor(team: Team): Colord { return this.teamColorAllocator.assignTeamColor(team); @@ -60,22 +67,21 @@ export class PastelThemeDark implements Theme { return player.type() === PlayerType.Human ? "#ffffff" : "#e6e6e6"; } - /* eslint-disable sort-keys */ specialBuildingColor(player: PlayerView): Colord { const tc = this.territoryColor(player).rgba; return colord({ - r: Math.max(tc.r - 50, 0), - g: Math.max(tc.g - 50, 0), b: Math.max(tc.b - 50, 0), + g: Math.max(tc.g - 50, 0), + r: Math.max(tc.r - 50, 0), }); } railroadColor(player: PlayerView): Colord { const tc = this.territoryColor(player).rgba; const color = colord({ - r: Math.max(tc.r - 10, 0), - g: Math.max(tc.g - 10, 0), b: Math.max(tc.b - 10, 0), + g: Math.max(tc.g - 10, 0), + r: Math.max(tc.r - 10, 0), }); return color; } @@ -86,9 +92,9 @@ export class PastelThemeDark implements Theme { const tc = this.territoryColor(player).rgba; const color = colord({ - r: Math.max(tc.r - 40, 0), - g: Math.max(tc.g - 40, 0), b: Math.max(tc.b - 40, 0), + g: Math.max(tc.g - 40, 0), + r: Math.max(tc.r - 40, 0), }); this.borderColorCache.set(player.id(), color); @@ -97,13 +103,13 @@ export class PastelThemeDark implements Theme { defendedBorderColors(player: PlayerView): { light: Colord; dark: Colord } { return { - light: this.territoryColor(player).darken(0.2), dark: this.territoryColor(player).darken(0.4), + light: this.territoryColor(player).darken(0.2), }; } focusedBorderColor(): Colord { - return colord({ r: 255, g: 255, b: 255 }); + return colord({ b: 255, g: 255, r: 255 }); } terrainColor(gm: GameMap, tile: TileRef): Colord { @@ -120,33 +126,32 @@ export class PastelThemeDark implements Theme { } if (gm.magnitude(tile) < 10) { return colord({ - r: Math.max(w.r + 9 - mag, 0), - g: Math.max(w.g + 9 - mag, 0), b: Math.max(w.b + 9 - mag, 0), + g: Math.max(w.g + 9 - mag, 0), + r: Math.max(w.r + 9 - mag, 0), }); } return this.water; case TerrainType.Plains: return colord({ - r: 140, - g: 170 - 2 * mag, b: 88, + g: 170 - 2 * mag, + r: 140, }); case TerrainType.Highland: return colord({ - r: 150 + 2 * mag, - g: 133 + 2 * mag, b: 88 + 2 * mag, + g: 133 + 2 * mag, + r: 150 + 2 * mag, }); case TerrainType.Mountain: return colord({ - r: 180 + mag / 2, - g: 180 + mag / 2, b: 180 + mag / 2, + g: 180 + mag / 2, + r: 180 + mag / 2, }); } } - /* eslint-enable sort-keys */ backgroundColor(): Colord { return this.background; diff --git a/src/core/configuration/PreprodConfig.ts b/src/core/configuration/PreprodConfig.ts index 75467cecb0..a0567aaa98 100644 --- a/src/core/configuration/PreprodConfig.ts +++ b/src/core/configuration/PreprodConfig.ts @@ -1,5 +1,5 @@ -import { DefaultServerConfig } from "./DefaultConfig"; import { GameEnv } from "./Config"; +import { DefaultServerConfig } from "./DefaultConfig"; export const preprodConfig = new (class extends DefaultServerConfig { env(): GameEnv { diff --git a/src/core/configuration/ProdConfig.ts b/src/core/configuration/ProdConfig.ts index 24a6719332..7bea841a84 100644 --- a/src/core/configuration/ProdConfig.ts +++ b/src/core/configuration/ProdConfig.ts @@ -1,5 +1,5 @@ -import { DefaultServerConfig } from "./DefaultConfig"; import { GameEnv } from "./Config"; +import { DefaultServerConfig } from "./DefaultConfig"; export const prodConfig = new (class extends DefaultServerConfig { numWorkers(): number { diff --git a/src/core/execution/AttackExecution.ts b/src/core/execution/AttackExecution.ts index 44bddccda0..0991a1e2b8 100644 --- a/src/core/execution/AttackExecution.ts +++ b/src/core/execution/AttackExecution.ts @@ -1,3 +1,4 @@ +import { renderTroops } from "../../client/Utils"; import { Attack, Execution, @@ -6,13 +7,12 @@ import { Player, PlayerID, PlayerType, - TerraNullius, TerrainType, + TerraNullius, } from "../game/Game"; -import { FlatBinaryHeap } from "./utils/FlatBinaryHeap"; // adjust path if needed -import { PseudoRandom } from "../PseudoRandom"; import { TileRef } from "../game/GameMap"; -import { renderTroops } from "../../client/Utils"; +import { PseudoRandom } from "../PseudoRandom"; +import { FlatBinaryHeap } from "./utils/FlatBinaryHeap"; // adjust path if needed const malusForRetreat = 25; export class AttackExecution implements Execution { @@ -209,7 +209,9 @@ export class AttackExecution implements Execution { throw new Error("Attack not initialized"); } let troopCount = this.attack.troops(); // cache troop count - const targetPlayer: Player | null = this.target.isPlayer() ? this.target : null; // cache target player + const targetPlayer: Player | null = this.target.isPlayer() + ? this.target + : null; // cache target player if (this.attack.retreated()) { if (targetPlayer !== null) { @@ -230,9 +232,10 @@ export class AttackExecution implements Execution { return; } - const alliance = this.target && this.target.isPlayer() - ? this._owner.allianceWith(this.target) - : null; + const alliance = + this.target && this.target.isPlayer() + ? this._owner.allianceWith(this.target) + : null; if (this.breakAlliance && alliance !== null) { this.breakAlliance = false; this._owner.breakAlliance(alliance); diff --git a/src/core/execution/BotExecution.ts b/src/core/execution/BotExecution.ts index 75d8436324..f1685ddbb8 100644 --- a/src/core/execution/BotExecution.ts +++ b/src/core/execution/BotExecution.ts @@ -1,7 +1,7 @@ import { Execution, Game, Player } from "../game/Game"; -import { BotBehavior } from "./utils/BotBehavior"; import { PseudoRandom } from "../PseudoRandom"; import { simpleHash } from "../Util"; +import { BotBehavior } from "./utils/BotBehavior"; export class BotExecution implements Execution { private active = true; diff --git a/src/core/execution/BotSpawner.ts b/src/core/execution/BotSpawner.ts index a0e9eeaafe..67b13eb95c 100644 --- a/src/core/execution/BotSpawner.ts +++ b/src/core/execution/BotSpawner.ts @@ -1,10 +1,10 @@ -import { BOT_NAME_PREFIXES, BOT_NAME_SUFFIXES } from "./utils/BotNames"; import { Game, PlayerInfo, PlayerType } from "../game/Game"; -import { GameID } from "../Schemas"; -import { PseudoRandom } from "../PseudoRandom"; -import { SpawnExecution } from "./SpawnExecution"; import { TileRef } from "../game/GameMap"; +import { PseudoRandom } from "../PseudoRandom"; +import { GameID } from "../Schemas"; import { simpleHash } from "../Util"; +import { SpawnExecution } from "./SpawnExecution"; +import { BOT_NAME_PREFIXES, BOT_NAME_SUFFIXES } from "./utils/BotNames"; export class BotSpawner { private readonly random: PseudoRandom; diff --git a/src/core/execution/ConstructionExecution.ts b/src/core/execution/ConstructionExecution.ts index 033505ff4e..9f08a660c4 100644 --- a/src/core/execution/ConstructionExecution.ts +++ b/src/core/execution/ConstructionExecution.ts @@ -7,6 +7,7 @@ import { Unit, UnitType, } from "../game/Game"; +import { TileRef } from "../game/GameMap"; import { CityExecution } from "./CityExecution"; import { DefensePostExecution } from "./DefensePostExecution"; import { FactoryExecution } from "./FactoryExecution"; @@ -15,7 +16,6 @@ import { MissileSiloExecution } from "./MissileSiloExecution"; import { NukeExecution } from "./NukeExecution"; import { PortExecution } from "./PortExecution"; import { SAMLauncherExecution } from "./SAMLauncherExecution"; -import { TileRef } from "../game/GameMap"; import { WarshipExecution } from "./WarshipExecution"; export class ConstructionExecution implements Execution { @@ -97,7 +97,8 @@ export class ConstructionExecution implements Execution { this.active = false; return; } - if (this.ticksUntilComplete === undefined) throw new Error("Not initialized"); + if (this.ticksUntilComplete === undefined) + throw new Error("Not initialized"); this.ticksUntilComplete--; } diff --git a/src/core/execution/DefensePostExecution.ts b/src/core/execution/DefensePostExecution.ts index 62243c3fcd..0cb2a99694 100644 --- a/src/core/execution/DefensePostExecution.ts +++ b/src/core/execution/DefensePostExecution.ts @@ -1,6 +1,6 @@ import { Execution, Game, Player, Unit, UnitType } from "../game/Game"; -import { ShellExecution } from "./ShellExecution"; import { TileRef } from "../game/GameMap"; +import { ShellExecution } from "./ShellExecution"; export class DefensePostExecution implements Execution { private mg: Game | undefined; diff --git a/src/core/execution/ExecutionManager.ts b/src/core/execution/ExecutionManager.ts index 122448eda7..11c76fafa2 100644 --- a/src/core/execution/ExecutionManager.ts +++ b/src/core/execution/ExecutionManager.ts @@ -1,12 +1,14 @@ -import { ClientID, GameID, Intent, Turn } from "../Schemas"; import { Execution, Game } from "../game/Game"; +import { PseudoRandom } from "../PseudoRandom"; +import { ClientID, GameID, Intent, Turn } from "../Schemas"; +import { simpleHash } from "../Util"; +import { AttackExecution } from "./AttackExecution"; import { AllianceExtensionExecution } from "./alliance/AllianceExtensionExecution"; import { AllianceRequestExecution } from "./alliance/AllianceRequestExecution"; import { AllianceRequestReplyExecution } from "./alliance/AllianceRequestReplyExecution"; -import { AttackExecution } from "./AttackExecution"; +import { BreakAllianceExecution } from "./alliance/BreakAllianceExecution"; import { BoatRetreatExecution } from "./BoatRetreatExecution"; import { BotSpawner } from "./BotSpawner"; -import { BreakAllianceExecution } from "./alliance/BreakAllianceExecution"; import { ConstructionExecution } from "./ConstructionExecution"; import { DeleteUnitExecution } from "./DeleteUnitExecution"; import { DonateGoldExecution } from "./DonateGoldExecution"; @@ -17,14 +19,12 @@ import { FakeHumanExecution } from "./FakeHumanExecution"; import { MarkDisconnectedExecution } from "./MarkDisconnectedExecution"; import { MoveWarshipExecution } from "./MoveWarshipExecution"; import { NoOpExecution } from "./NoOpExecution"; -import { PseudoRandom } from "../PseudoRandom"; import { QuickChatExecution } from "./QuickChatExecution"; import { RetreatExecution } from "./RetreatExecution"; import { SpawnExecution } from "./SpawnExecution"; import { TargetPlayerExecution } from "./TargetPlayerExecution"; import { TransportShipExecution } from "./TransportShipExecution"; import { UpgradeStructureExecution } from "./UpgradeStructureExecution"; -import { simpleHash } from "../Util"; export class Executor { // private random = new PseudoRandom(999) diff --git a/src/core/execution/FakeHumanExecution.ts b/src/core/execution/FakeHumanExecution.ts index dac000a3d9..c266cf129d 100644 --- a/src/core/execution/FakeHumanExecution.ts +++ b/src/core/execution/FakeHumanExecution.ts @@ -14,17 +14,17 @@ import { Unit, UnitType, } from "../game/Game"; -import { TileRef, euclDistFN, manhattanDistFN } from "../game/GameMap"; +import { euclDistFN, manhattanDistFN, TileRef } from "../game/GameMap"; +import { PseudoRandom } from "../PseudoRandom"; +import { GameID } from "../Schemas"; import { calculateBoundingBox, flattenedEmojiTable, simpleHash } from "../Util"; -import { BotBehavior } from "./utils/BotBehavior"; import { ConstructionExecution } from "./ConstructionExecution"; import { EmojiExecution } from "./EmojiExecution"; -import { GameID } from "../Schemas"; import { NukeExecution } from "./NukeExecution"; -import { PseudoRandom } from "../PseudoRandom"; import { SpawnExecution } from "./SpawnExecution"; import { TransportShipExecution } from "./TransportShipExecution"; import { closestTwoTiles } from "./Util"; +import { BotBehavior } from "./utils/BotBehavior"; export class FakeHumanExecution implements Execution { private active = true; @@ -174,8 +174,7 @@ export class FakeHumanExecution implements Execution { const enemyborder = Array.from(this.player.borderTiles()) .flatMap((t) => game.neighbors(t)) .filter( - (t) => - game.isLand(t) && game.ownerID(t) !== this.player?.smallID(), + (t) => game.isLand(t) && game.ownerID(t) !== this.player?.smallID(), ); if (enemyborder.length === 0) { @@ -306,7 +305,9 @@ export class FakeHumanExecution implements Execution { UnitType.SAMLauncher, ); const structureTiles = structures.map((u) => u.tile()); - const randomTiles: (TileRef | null)[] = new Array(10).fill(null); + const randomTiles: (TileRef | null)[] = new Array(10).fill( + null, + ); for (let i = 0; i < randomTiles.length; i++) { randomTiles[i] = this.randTerritoryTile(other); } @@ -413,7 +414,9 @@ export class FakeHumanExecution implements Execution { if (this.player.isOnSameTeam(other)) return; const closest = closestTwoTiles( this.mg, - Array.from(this.player.borderTiles()).filter((t) => this.mg?.isOceanShore(t)), + Array.from(this.player.borderTiles()).filter((t) => + this.mg?.isOceanShore(t), + ), Array.from(other.borderTiles()).filter((t) => this.mg?.isOceanShore(t)), ); if (closest === null) { @@ -467,8 +470,8 @@ export class FakeHumanExecution implements Execution { const tiles = type === UnitType.Port ? Array.from(this.player.borderTiles()).filter((t) => - this.mg?.isOceanShore(t), - ) + this.mg?.isOceanShore(t), + ) : Array.from(this.player.tiles()); if (tiles.length === 0) return null; const valueFunction = this.structureSpawnTileValue(type); @@ -486,7 +489,7 @@ export class FakeHumanExecution implements Execution { return bestTile; } - private * arraySampler(a: T[], sampleSize = 50): Generator { + private *arraySampler(a: T[], sampleSize = 50): Generator { if (a.length <= sampleSize) { // Return all elements yield* a; @@ -508,7 +511,9 @@ export class FakeHumanExecution implements Execution { const { mg } = this; const otherUnits = this.player.units(type); // Prefer spacing structures out of atom bomb range - const borderSpacing = this.mg.config().nukeMagnitudes(UnitType.AtomBomb).outer; + const borderSpacing = this.mg + .config() + .nukeMagnitudes(UnitType.AtomBomb).outer; const structureSpacing = borderSpacing * 2; switch (type) { case UnitType.Port: @@ -516,7 +521,9 @@ export class FakeHumanExecution implements Execution { let w = 0; // Prefer to be far away from other structures of the same type - const otherTiles: Set = new Set(otherUnits.map((u) => u.tile())); + const otherTiles: Set = new Set( + otherUnits.map((u) => u.tile()), + ); otherTiles.delete(tile); const closestOther = closestTwoTiles(mg, otherTiles, [tile]); if (closestOther !== null) { @@ -543,7 +550,9 @@ export class FakeHumanExecution implements Execution { } // Prefer to be away from other structures of the same type - const otherTiles: Set = new Set(otherUnits.map((u) => u.tile())); + const otherTiles: Set = new Set( + otherUnits.map((u) => u.tile()), + ); otherTiles.delete(tile); const closestOther = closestTwoTiles(mg, otherTiles, [tile]); if (closestOther !== null) { diff --git a/src/core/execution/MIRVExecution.ts b/src/core/execution/MIRVExecution.ts index 6360ba826e..5067d66d31 100644 --- a/src/core/execution/MIRVExecution.ts +++ b/src/core/execution/MIRVExecution.ts @@ -7,11 +7,11 @@ import { Unit, UnitType, } from "../game/Game"; -import { NukeExecution } from "./NukeExecution"; -import { ParabolaPathFinder } from "../pathfinding/PathFinding"; -import { PseudoRandom } from "../PseudoRandom"; import { TileRef } from "../game/GameMap"; +import { PseudoRandom } from "../PseudoRandom"; +import { ParabolaPathFinder } from "../pathfinding/PathFinding"; import { simpleHash } from "../Util"; +import { NukeExecution } from "./NukeExecution"; export class MirvExecution implements Execution { private active = true; diff --git a/src/core/execution/NukeExecution.ts b/src/core/execution/NukeExecution.ts index 575cbaf866..6020e78125 100644 --- a/src/core/execution/NukeExecution.ts +++ b/src/core/execution/NukeExecution.ts @@ -1,18 +1,18 @@ import { Execution, Game, + isStructureType, MessageType, Player, TerraNullius, TrajectoryTile, Unit, UnitType, - isStructureType, } from "../game/Game"; -import { NukeType } from "../StatsSchemas"; -import { ParabolaPathFinder } from "../pathfinding/PathFinding"; -import { PseudoRandom } from "../PseudoRandom"; import { TileRef } from "../game/GameMap"; +import { PseudoRandom } from "../PseudoRandom"; +import { ParabolaPathFinder } from "../pathfinding/PathFinding"; +import { NukeType } from "../StatsSchemas"; const SPRITE_RADIUS = 16; diff --git a/src/core/execution/PlayerExecution.ts b/src/core/execution/PlayerExecution.ts index 457a79222f..f8945adea9 100644 --- a/src/core/execution/PlayerExecution.ts +++ b/src/core/execution/PlayerExecution.ts @@ -1,8 +1,8 @@ +import { Config } from "../configuration/Config"; import { Execution, Game, Player, UnitType } from "../game/Game"; +import { GameImpl } from "../game/GameImpl"; import { GameMap, TileRef } from "../game/GameMap"; import { calculateBoundingBox, getMode, inscribed, simpleHash } from "../Util"; -import { Config } from "../configuration/Config"; -import { GameImpl } from "../game/GameImpl"; export class PlayerExecution implements Execution { private readonly ticksPerClusterCalc = 20; diff --git a/src/core/execution/PortExecution.ts b/src/core/execution/PortExecution.ts index 5c7dfc566c..7dcff805dc 100644 --- a/src/core/execution/PortExecution.ts +++ b/src/core/execution/PortExecution.ts @@ -1,6 +1,6 @@ import { Execution, Game, Player, Unit, UnitType } from "../game/Game"; -import { PseudoRandom } from "../PseudoRandom"; import { TileRef } from "../game/GameMap"; +import { PseudoRandom } from "../PseudoRandom"; import { TradeShipExecution } from "./TradeShipExecution"; import { TrainStationExecution } from "./TrainStationExecution"; diff --git a/src/core/execution/RailroadExecution.ts b/src/core/execution/RailroadExecution.ts index 33ddaa8049..1f3bc8d29e 100644 --- a/src/core/execution/RailroadExecution.ts +++ b/src/core/execution/RailroadExecution.ts @@ -1,7 +1,7 @@ import { Execution, Game } from "../game/Game"; +import { TileRef } from "../game/GameMap"; import { GameUpdateType, RailTile, RailType } from "../game/GameUpdates"; import { Railroad } from "../game/Railroad"; -import { TileRef } from "../game/GameMap"; export class RailroadExecution implements Execution { private mg: Game | undefined; @@ -18,17 +18,16 @@ export class RailroadExecution implements Execution { return this.active; } - /* eslint-disable sort-keys */ init(mg: Game, ticks: number): void { this.mg = mg; const { tiles } = this.railRoad; // Inverse direction computation for the first tile this.railTiles.push({ - tile: tiles[0], railType: tiles.length > 0 ? this.computeExtremityDirection(tiles[0], tiles[1]) : RailType.VERTICAL, + tile: tiles[0], }); for (let i = 1; i < tiles.length - 1; i++) { const direction = this.computeDirection( @@ -36,20 +35,19 @@ export class RailroadExecution implements Execution { tiles[i], tiles[i + 1], ); - this.railTiles.push({ tile: tiles[i], railType: direction }); + this.railTiles.push({ railType: direction, tile: tiles[i] }); } this.railTiles.push({ - tile: tiles[tiles.length - 1], railType: tiles.length > 0 ? this.computeExtremityDirection( - tiles[tiles.length - 1], - tiles[tiles.length - 2], - ) + tiles[tiles.length - 1], + tiles[tiles.length - 2], + ) : RailType.VERTICAL, + tile: tiles[tiles.length - 1], }); } - /* eslint-enable sort-keys */ private computeExtremityDirection(tile: TileRef, next: TileRef): RailType { if (this.mg === undefined) throw new Error("Not initialized"); diff --git a/src/core/execution/SAMLauncherExecution.ts b/src/core/execution/SAMLauncherExecution.ts index 744021ce3e..ef1b5fdfa1 100644 --- a/src/core/execution/SAMLauncherExecution.ts +++ b/src/core/execution/SAMLauncherExecution.ts @@ -1,15 +1,15 @@ import { Execution, Game, + isUnit, MessageType, Player, Unit, UnitType, - isUnit, } from "../game/Game"; +import { TileRef } from "../game/GameMap"; import { PseudoRandom } from "../PseudoRandom"; import { SAMMissileExecution } from "./SAMMissileExecution"; -import { TileRef } from "../game/GameMap"; type Target = { unit: Unit; @@ -97,8 +97,7 @@ class SAMTargetingSystem { } const interceptionTile = this.computeInterceptionTile(nuke.unit); if (interceptionTile !== undefined) { - // eslint-disable-next-line sort-keys - targets.push({ unit: nuke.unit, tile: interceptionTile }); + targets.push({ tile: interceptionTile, unit: nuke.unit }); } else { // Store unreachable nukes in order to prevent useless interception computation this.storeUnreachableNukes(nuke.unit.id()); diff --git a/src/core/execution/SAMMissileExecution.ts b/src/core/execution/SAMMissileExecution.ts index 0b598f072b..8ca029f79e 100644 --- a/src/core/execution/SAMMissileExecution.ts +++ b/src/core/execution/SAMMissileExecution.ts @@ -6,10 +6,10 @@ import { Unit, UnitType, } from "../game/Game"; +import { TileRef } from "../game/GameMap"; +import { PseudoRandom } from "../PseudoRandom"; import { AirPathFinder } from "../pathfinding/PathFinding"; import { NukeType } from "../StatsSchemas"; -import { PseudoRandom } from "../PseudoRandom"; -import { TileRef } from "../game/GameMap"; export class SAMMissileExecution implements Execution { private active = true; diff --git a/src/core/execution/ShellExecution.ts b/src/core/execution/ShellExecution.ts index 3573a0690a..3abc1e63e4 100644 --- a/src/core/execution/ShellExecution.ts +++ b/src/core/execution/ShellExecution.ts @@ -1,7 +1,7 @@ import { Execution, Game, Player, Unit, UnitType } from "../game/Game"; -import { AirPathFinder } from "../pathfinding/PathFinding"; -import { PseudoRandom } from "../PseudoRandom"; import { TileRef } from "../game/GameMap"; +import { PseudoRandom } from "../PseudoRandom"; +import { AirPathFinder } from "../pathfinding/PathFinding"; export class ShellExecution implements Execution { private active = true; diff --git a/src/core/execution/SpawnExecution.ts b/src/core/execution/SpawnExecution.ts index 3ed33dd38d..3eac33ca14 100644 --- a/src/core/execution/SpawnExecution.ts +++ b/src/core/execution/SpawnExecution.ts @@ -1,7 +1,7 @@ import { Execution, Game, Player, PlayerInfo, PlayerType } from "../game/Game"; +import { TileRef } from "../game/GameMap"; import { BotExecution } from "./BotExecution"; import { PlayerExecution } from "./PlayerExecution"; -import { TileRef } from "../game/GameMap"; import { getSpawnTiles } from "./Util"; export class SpawnExecution implements Execution { diff --git a/src/core/execution/TradeShipExecution.ts b/src/core/execution/TradeShipExecution.ts index 04e9fbbc7e..3815b35c3a 100644 --- a/src/core/execution/TradeShipExecution.ts +++ b/src/core/execution/TradeShipExecution.ts @@ -1,3 +1,4 @@ +import { renderNumber } from "../../client/Utils"; import { Execution, Game, @@ -6,11 +7,10 @@ import { Unit, UnitType, } from "../game/Game"; +import { TileRef } from "../game/GameMap"; import { PathFindResultType } from "../pathfinding/AStar"; import { PathFinder } from "../pathfinding/PathFinding"; -import { TileRef } from "../game/GameMap"; import { distSortUnit } from "../Util"; -import { renderNumber } from "../../client/Utils"; export class TradeShipExecution implements Execution { private active = true; diff --git a/src/core/execution/TrainExecution.ts b/src/core/execution/TrainExecution.ts index 2fafd2af31..707c6a3bbf 100644 --- a/src/core/execution/TrainExecution.ts +++ b/src/core/execution/TrainExecution.ts @@ -6,9 +6,9 @@ import { Unit, UnitType, } from "../game/Game"; -import { OrientedRailroad, getOrientedRailroad } from "../game/Railroad"; -import { RailNetwork } from "../game/RailNetwork"; import { TileRef } from "../game/GameMap"; +import { RailNetwork } from "../game/RailNetwork"; +import { getOrientedRailroad, OrientedRailroad } from "../game/Railroad"; import { TrainStation } from "../game/TrainStation"; export class TrainExecution implements Execution { diff --git a/src/core/execution/TrainStationExecution.ts b/src/core/execution/TrainStationExecution.ts index 163a73bad4..b77ee962e4 100644 --- a/src/core/execution/TrainStationExecution.ts +++ b/src/core/execution/TrainStationExecution.ts @@ -1,7 +1,7 @@ import { Execution, Game, Unit } from "../game/Game"; +import { TrainStation } from "../game/TrainStation"; import { PseudoRandom } from "../PseudoRandom"; import { TrainExecution } from "./TrainExecution"; -import { TrainStation } from "../game/TrainStation"; export class TrainStationExecution implements Execution { private mg: Game | undefined; diff --git a/src/core/execution/TransportShipExecution.ts b/src/core/execution/TransportShipExecution.ts index 4862d53af4..5ed3938aef 100644 --- a/src/core/execution/TransportShipExecution.ts +++ b/src/core/execution/TransportShipExecution.ts @@ -8,11 +8,11 @@ import { Unit, UnitType, } from "../game/Game"; -import { AttackExecution } from "./AttackExecution"; -import { PathFindResultType } from "../pathfinding/AStar"; -import { PathFinder } from "../pathfinding/PathFinding"; import { TileRef } from "../game/GameMap"; import { targetTransportTile } from "../game/TransportShipUtils"; +import { PathFindResultType } from "../pathfinding/AStar"; +import { PathFinder } from "../pathfinding/PathFinding"; +import { AttackExecution } from "./AttackExecution"; export class TransportShipExecution implements Execution { private lastMove: number | undefined; diff --git a/src/core/execution/Util.ts b/src/core/execution/Util.ts index 302ea00006..f1b6e87eb5 100644 --- a/src/core/execution/Util.ts +++ b/src/core/execution/Util.ts @@ -1,4 +1,4 @@ -import { GameMap, TileRef, euclDistFN } from "../game/GameMap"; +import { euclDistFN, GameMap, TileRef } from "../game/GameMap"; export function getSpawnTiles(gm: GameMap, tile: TileRef): TileRef[] { return Array.from(gm.bfs(tile, euclDistFN(tile, 4, true))).filter( diff --git a/src/core/execution/WarshipExecution.ts b/src/core/execution/WarshipExecution.ts index 2ba6dacfc4..6ba5ba0564 100644 --- a/src/core/execution/WarshipExecution.ts +++ b/src/core/execution/WarshipExecution.ts @@ -1,17 +1,17 @@ import { Execution, Game, + isUnit, OwnerComp, Unit, UnitParams, UnitType, - isUnit, } from "../game/Game"; +import { TileRef } from "../game/GameMap"; +import { PseudoRandom } from "../PseudoRandom"; import { PathFindResultType } from "../pathfinding/AStar"; import { PathFinder } from "../pathfinding/PathFinding"; -import { PseudoRandom } from "../PseudoRandom"; import { ShellExecution } from "./ShellExecution"; -import { TileRef } from "../game/GameMap"; export class WarshipExecution implements Execution { private random: PseudoRandom | undefined; @@ -108,10 +108,8 @@ export class WarshipExecution implements Execution { const patrolTile = this.warship.patrolTile(); if ( patrolTile !== undefined && - this.mg.euclideanDistSquared( - patrolTile, - unit.tile(), - ) > patrolRangeSquared + this.mg.euclideanDistSquared(patrolTile, unit.tile()) > + patrolRangeSquared ) { // Prevent warship from chasing trade ship that is too far away from // the patrol tile to prevent warships from wandering around the map. @@ -224,10 +222,7 @@ export class WarshipExecution implements Execution { return; } } - const result = this.pathfinder.nextTile( - this.warship.tile(), - targetTile, - ); + const result = this.pathfinder.nextTile(this.warship.tile(), targetTile); switch (result.type) { case PathFindResultType.Completed: this.warship.setTargetTile(undefined); diff --git a/src/core/execution/WinCheckExecution.ts b/src/core/execution/WinCheckExecution.ts index 996cc45273..8c2159a71c 100644 --- a/src/core/execution/WinCheckExecution.ts +++ b/src/core/execution/WinCheckExecution.ts @@ -1,3 +1,4 @@ +import { GameEvent } from "../EventBus"; import { ColoredTeams, Execution, @@ -6,7 +7,6 @@ import { Player, Team, } from "../game/Game"; -import { GameEvent } from "../EventBus"; export class WinEvent implements GameEvent { constructor(public readonly winner: Player) {} diff --git a/src/core/execution/utils/BotBehavior.ts b/src/core/execution/utils/BotBehavior.ts index 76497968c7..2c7f6057cb 100644 --- a/src/core/execution/utils/BotBehavior.ts +++ b/src/core/execution/utils/BotBehavior.ts @@ -7,11 +7,11 @@ import { TerraNullius, Tick, } from "../../game/Game"; -import { AllianceExtensionExecution } from "../alliance/AllianceExtensionExecution"; -import { AttackExecution } from "../AttackExecution"; -import { EmojiExecution } from "../EmojiExecution"; import { PseudoRandom } from "../../PseudoRandom"; import { flattenedEmojiTable } from "../../Util"; +import { AttackExecution } from "../AttackExecution"; +import { AllianceExtensionExecution } from "../alliance/AllianceExtensionExecution"; +import { EmojiExecution } from "../EmojiExecution"; export class BotBehavior { private enemy: Player | null = null; diff --git a/src/core/game/AllianceRequestImpl.ts b/src/core/game/AllianceRequestImpl.ts index 591c383c9c..23d717addf 100644 --- a/src/core/game/AllianceRequestImpl.ts +++ b/src/core/game/AllianceRequestImpl.ts @@ -1,6 +1,6 @@ import { AllianceRequest, Player, Tick } from "./Game"; -import { AllianceRequestUpdate, GameUpdateType } from "./GameUpdates"; import { GameImpl } from "./GameImpl"; +import { AllianceRequestUpdate, GameUpdateType } from "./GameUpdates"; export class AllianceRequestImpl implements AllianceRequest { private status_: "pending" | "accepted" | "rejected" = "pending"; diff --git a/src/core/game/AttackImpl.ts b/src/core/game/AttackImpl.ts index 6ab6dd56d3..7bb4cc1e9b 100644 --- a/src/core/game/AttackImpl.ts +++ b/src/core/game/AttackImpl.ts @@ -1,7 +1,7 @@ import { Attack, Cell, Player, TerraNullius } from "./Game"; import { GameImpl } from "./GameImpl"; -import { PlayerImpl } from "./PlayerImpl"; import { TileRef } from "./GameMap"; +import { PlayerImpl } from "./PlayerImpl"; export class AttackImpl implements Attack { private _isActive = true; diff --git a/src/core/game/BinaryLoaderGameMapLoader.ts b/src/core/game/BinaryLoaderGameMapLoader.ts index 233adbd984..8089860328 100644 --- a/src/core/game/BinaryLoaderGameMapLoader.ts +++ b/src/core/game/BinaryLoaderGameMapLoader.ts @@ -1,5 +1,5 @@ -import { GameMapLoader, MapData } from "./GameMapLoader"; import { GameMapType } from "./Game"; +import { GameMapLoader, MapData } from "./GameMapLoader"; import { MapManifest } from "./TerrainMapLoader"; export type BinModule = { diff --git a/src/core/game/FetchGameMapLoader.ts b/src/core/game/FetchGameMapLoader.ts index 5bdeed62dc..096f974475 100644 --- a/src/core/game/FetchGameMapLoader.ts +++ b/src/core/game/FetchGameMapLoader.ts @@ -1,5 +1,5 @@ -import { GameMapLoader, MapData } from "./GameMapLoader"; import { GameMapType } from "./Game"; +import { GameMapLoader, MapData } from "./GameMapLoader"; import { MapManifestSchema } from "./TerrainMapLoader"; export class FetchGameMapLoader implements GameMapLoader { diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 2416371daf..70434288df 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -1,3 +1,4 @@ +import { Config } from "../configuration/Config"; import { AllPlayersStats, ClientID } from "../Schemas"; import { GameMap, TileRef } from "./GameMap"; import { @@ -6,7 +7,6 @@ import { PlayerUpdate, UnitUpdate, } from "./GameUpdates"; -import { Config } from "../configuration/Config"; import { RailNetwork } from "./RailNetwork"; import { Stats } from "./Stats"; import { UnitPredicate } from "./UnitGrid"; @@ -44,18 +44,16 @@ export const Duos = "Duos" as const; export const Trios = "Trios" as const; export const Quads = "Quads" as const; -/* eslint-disable sort-keys */ export const ColoredTeams: Record = { - Red: "Red", Blue: "Blue", - Teal: "Teal", + Bot: "Bot", + Green: "Green", + Orange: "Orange", Purple: "Purple", + Red: "Red", + Teal: "Teal", Yellow: "Yellow", - Orange: "Orange", - Green: "Green", - Bot: "Bot", } as const; -/* eslint-enable sort-keys */ export enum GameMapType { World = "World", @@ -103,6 +101,14 @@ export const mapCategories: Record = { GameMapType.Africa, GameMapType.Oceania, ], + + fantasy: [ + GameMapType.Pangaea, + GameMapType.Pluto, + GameMapType.MarsRevised, + GameMapType.Mars, + GameMapType.DeglaciatedAntarctica, + ], regional: [ GameMapType.BlackSea, GameMapType.Britannia, @@ -120,14 +126,6 @@ export const mapCategories: Record = { GameMapType.Italia, GameMapType.Yenisei, ], - // eslint-disable-next-line sort-keys - fantasy: [ - GameMapType.Pangaea, - GameMapType.Pluto, - GameMapType.MarsRevised, - GameMapType.Mars, - GameMapType.DeglaciatedAntarctica, - ], }; export enum GameType { diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index 9d18989105..dca0dcee75 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -1,5 +1,11 @@ /* eslint-disable max-lines */ + +import { renderNumber } from "../../client/Utils"; +import { Config } from "../configuration/Config"; import { AllPlayersStats, ClientID, Winner } from "../Schemas"; +import { simpleHash } from "../Util"; +import { AllianceImpl } from "./AllianceImpl"; +import { AllianceRequestImpl } from "./AllianceRequestImpl"; import { Alliance, AllianceRequest, @@ -20,8 +26,8 @@ import { PlayerType, Quads, Team, - TerraNullius, TerrainType, + TerraNullius, Trios, Unit, UnitInfo, @@ -29,19 +35,14 @@ import { } from "./Game"; import { GameMap, TileRef, TileUpdate } from "./GameMap"; import { GameUpdate, GameUpdateType } from "./GameUpdates"; -import { UnitGrid, UnitPredicate } from "./UnitGrid"; -import { AllianceImpl } from "./AllianceImpl"; -import { AllianceRequestImpl } from "./AllianceRequestImpl"; -import { Config } from "../configuration/Config"; import { PlayerImpl } from "./PlayerImpl"; import { RailNetwork } from "./RailNetwork"; +import { createRailNetwork } from "./RailNetworkImpl"; import { Stats } from "./Stats"; import { StatsImpl } from "./StatsImpl"; -import { TerraNulliusImpl } from "./TerraNulliusImpl"; import { assignTeams } from "./TeamAssignment"; -import { createRailNetwork } from "./RailNetworkImpl"; -import { renderNumber } from "../../client/Utils"; -import { simpleHash } from "../Util"; +import { TerraNulliusImpl } from "./TerraNulliusImpl"; +import { UnitGrid, UnitPredicate } from "./UnitGrid"; export function createGame( humans: PlayerInfo[], diff --git a/src/core/game/GameView.ts b/src/core/game/GameView.ts index 01a08f59e1..dedec770b7 100644 --- a/src/core/game/GameView.ts +++ b/src/core/game/GameView.ts @@ -1,11 +1,9 @@ -import { - AllianceView, - AttackUpdate, - GameUpdateType, - GameUpdateViewData, - PlayerUpdate, - UnitUpdate, -} from "./GameUpdates"; +import { base64url } from "jose"; +import { Config } from "../configuration/Config"; +import { PatternDecoder } from "../PatternDecoder"; +import { ClientID, GameID, Player } from "../Schemas"; +import { createRandomName } from "../Util"; +import { WorkerClient } from "../worker/WorkerClient"; import { Cell, EmojiMessage, @@ -18,24 +16,26 @@ import { PlayerProfile, PlayerType, Team, - TerraNullius, TerrainType, + TerraNullius, Tick, TrainType, UnitInfo, UnitType, } from "./Game"; -import { ClientID, GameID, Player } from "../Schemas"; import { GameMap, TileRef, TileUpdate } from "./GameMap"; -import { UnitGrid, UnitPredicate } from "./UnitGrid"; -import { Config } from "../configuration/Config"; -import { PatternDecoder } from "../PatternDecoder"; -import { TerraNulliusImpl } from "./TerraNulliusImpl"; +import { + AllianceView, + AttackUpdate, + GameUpdateType, + GameUpdateViewData, + PlayerUpdate, + UnitUpdate, +} from "./GameUpdates"; import { TerrainMapData } from "./TerrainMapLoader"; +import { TerraNulliusImpl } from "./TerraNulliusImpl"; +import { UnitGrid, UnitPredicate } from "./UnitGrid"; import { UserSettings } from "./UserSettings"; -import { WorkerClient } from "../worker/WorkerClient"; -import { base64url } from "jose"; -import { createRandomName } from "../Util"; const userSettings: UserSettings = new UserSettings(); diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index bb906acb3d..db931ebcff 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -1,8 +1,22 @@ /* eslint-disable max-lines */ + +import { renderNumber, renderTroops } from "../../client/Utils"; +import { PseudoRandom } from "../PseudoRandom"; +import { ClientID } from "../Schemas"; +import { + assertNever, + distSortUnit, + minInt, + simpleHash, + toInt, + within, +} from "../Util"; +import { sanitizeUsername } from "../validations/username"; +import { AttackImpl } from "./AttackImpl"; import { - AllPlayers, Alliance, AllianceRequest, + AllPlayers, Attack, BuildableUnit, Cell, @@ -27,32 +41,19 @@ import { UnitParams, UnitType, } from "./Game"; +import { GameImpl } from "./GameImpl"; +import { andFN, manhattanDistFN, TileRef } from "./GameMap"; import { AllianceView, AttackUpdate, GameUpdateType, PlayerUpdate, } from "./GameUpdates"; -import { TileRef, andFN, manhattanDistFN } from "./GameMap"; -import { - assertNever, - distSortUnit, - minInt, - simpleHash, - toInt, - within, -} from "../Util"; import { bestShoreDeploymentSource, canBuildTransportShip, } from "./TransportShipUtils"; -import { renderNumber, renderTroops } from "../../client/Utils"; -import { AttackImpl } from "./AttackImpl"; -import { ClientID } from "../Schemas"; -import { GameImpl } from "./GameImpl"; -import { PseudoRandom } from "../PseudoRandom"; import { UnitImpl } from "./UnitImpl"; -import { sanitizeUsername } from "../validations/username"; type Target = { tick: Tick; @@ -127,58 +128,56 @@ export class PlayerImpl implements Player { ); const stats = this.mg.stats().getPlayerStats(this); - /* eslint-disable sort-keys */ return { - type: GameUpdateType.Player, + alliances: this.alliances().map( + (a) => + ({ + createdAt: a.createdAt(), + expiresAt: a.expiresAt(), + id: a.id(), + other: a.other(this).id(), + }) satisfies AllianceView, + ), + allies: this.alliances().map((a) => a.other(this).smallID()), + betrayals: stats?.betrayals, clientID: this.clientID(), - name: this.name(), displayName: this.displayName(), - id: this.id(), - team: this.team() ?? undefined, - smallID: this.smallID(), - playerType: this.type(), - isAlive: this.isAlive(), - isDisconnected: this.isDisconnected(), - tilesOwned: this.numTilesOwned(), - gold: this._gold, - troops: this.troops(), - allies: this.alliances().map((a) => a.other(this).smallID()), embargoes: new Set([...this.embargoes.keys()].map((p) => p.toString())), - isTraitor: this.isTraitor(), - targets: this.targets().map((p) => p.smallID()), - outgoingEmojis: this.outgoingEmojis(), - outgoingAttacks: this._outgoingAttacks.map((a) => { + gold: this._gold, + hasSpawned: this.hasSpawned(), + id: this.id(), + incomingAttacks: this._incomingAttacks.map((a) => { return { attackerID: a.attacker().smallID(), - targetID: a.target().smallID(), - troops: a.troops(), id: a.id(), retreating: a.retreating(), + targetID: a.target().smallID(), + troops: a.troops(), } satisfies AttackUpdate; }), - incomingAttacks: this._incomingAttacks.map((a) => { + isAlive: this.isAlive(), + isDisconnected: this.isDisconnected(), + isTraitor: this.isTraitor(), + name: this.name(), + outgoingAllianceRequests, + outgoingAttacks: this._outgoingAttacks.map((a) => { return { attackerID: a.attacker().smallID(), - targetID: a.target().smallID(), - troops: a.troops(), id: a.id(), retreating: a.retreating(), + targetID: a.target().smallID(), + troops: a.troops(), } satisfies AttackUpdate; }), - outgoingAllianceRequests, - alliances: this.alliances().map( - (a) => - ({ - id: a.id(), - other: a.other(this).id(), - createdAt: a.createdAt(), - expiresAt: a.expiresAt(), - }) satisfies AllianceView, - ), - hasSpawned: this.hasSpawned(), - betrayals: stats?.betrayals, + outgoingEmojis: this.outgoingEmojis(), + playerType: this.type(), + smallID: this.smallID(), + targets: this.targets().map((p) => p.smallID()), + team: this.team() ?? undefined, + tilesOwned: this.numTilesOwned(), + troops: this.troops(), + type: GameUpdateType.Player, }; - /* eslint-enable sort-keys */ } smallID(): number { @@ -511,8 +510,7 @@ export class PlayerImpl implements Player { } target(other: Player): void { - // eslint-disable-next-line sort-keys - this.targets_.push({ tick: this.mg.ticks(), target: other }); + this.targets_.push({ target: other, tick: this.mg.ticks() }); this.mg.target(this, other); } diff --git a/src/core/game/RailNetwork.ts b/src/core/game/RailNetwork.ts index 1606180e75..00c2e6f5e9 100644 --- a/src/core/game/RailNetwork.ts +++ b/src/core/game/RailNetwork.ts @@ -1,5 +1,5 @@ -import { TrainStation } from "./TrainStation"; import { Unit } from "./Game"; +import { TrainStation } from "./TrainStation"; export type RailNetwork = { connectStation(station: TrainStation): void; diff --git a/src/core/game/RailNetworkImpl.ts b/src/core/game/RailNetworkImpl.ts index a77d218a0f..5cb2a300c2 100644 --- a/src/core/game/RailNetworkImpl.ts +++ b/src/core/game/RailNetworkImpl.ts @@ -1,12 +1,12 @@ -import { Cluster, TrainStation, TrainStationMapAdapter } from "./TrainStation"; -import { Game, Unit, UnitType } from "./Game"; -import { MiniAStar } from "../pathfinding/MiniAStar"; -import { PathFindResultType } from "../pathfinding/AStar"; -import { RailNetwork } from "./RailNetwork"; -import { Railroad } from "./Railroad"; import { RailroadExecution } from "../execution/RailroadExecution"; +import { PathFindResultType } from "../pathfinding/AStar"; +import { MiniAStar } from "../pathfinding/MiniAStar"; import { SerialAStar } from "../pathfinding/SerialAStar"; +import { Game, Unit, UnitType } from "./Game"; import { TileRef } from "./GameMap"; +import { RailNetwork } from "./RailNetwork"; +import { Railroad } from "./Railroad"; +import { Cluster, TrainStation, TrainStationMapAdapter } from "./TrainStation"; /** * The Stations handle their own neighbors so the graph is naturally traversable, @@ -216,8 +216,7 @@ export class RailNetworkImpl implements RailNetwork { const visited = new Set(); const queue: Array<{ station: TrainStation; distance: number }> = [ - // eslint-disable-next-line sort-keys - { station: start, distance: 0 }, + { distance: 0, station: start }, ]; let head = 0; @@ -231,8 +230,7 @@ export class RailNetworkImpl implements RailNetwork { for (const neighbor of station.neighbors()) { if (neighbor === dest) return distance + 1; if (!visited.has(neighbor)) { - // eslint-disable-next-line sort-keys - queue.push({ station: neighbor, distance: distance + 1 }); + queue.push({ distance: distance + 1, station: neighbor }); } } } diff --git a/src/core/game/Railroad.ts b/src/core/game/Railroad.ts index f415eede38..f862c01471 100644 --- a/src/core/game/Railroad.ts +++ b/src/core/game/Railroad.ts @@ -1,6 +1,6 @@ -import { GameUpdateType, RailTile, RailType } from "./GameUpdates"; import { Game } from "./Game"; import { TileRef } from "./GameMap"; +import { GameUpdateType, RailTile, RailType } from "./GameUpdates"; import { TrainStation } from "./TrainStation"; export class Railroad { diff --git a/src/core/game/Stats.ts b/src/core/game/Stats.ts index fbde0d9728..63a8aac793 100644 --- a/src/core/game/Stats.ts +++ b/src/core/game/Stats.ts @@ -1,6 +1,6 @@ +import { AllPlayersStats } from "../Schemas"; import { NukeType, OtherUnitType, PlayerStats } from "../StatsSchemas"; import { Player, TerraNullius } from "./Game"; -import { AllPlayersStats } from "../Schemas"; export type Stats = { getPlayerStats(player: Player): PlayerStats | null; diff --git a/src/core/game/StatsImpl.ts b/src/core/game/StatsImpl.ts index bae57a81a1..5b33dd6245 100644 --- a/src/core/game/StatsImpl.ts +++ b/src/core/game/StatsImpl.ts @@ -1,3 +1,4 @@ +import { AllPlayersStats } from "../Schemas"; import { ATTACK_INDEX_CANCEL, ATTACK_INDEX_RECV, @@ -26,7 +27,6 @@ import { unitTypeToOtherUnit, } from "../StatsSchemas"; import { Player, TerraNullius } from "./Game"; -import { AllPlayersStats } from "../Schemas"; import { Stats } from "./Stats"; type BigIntLike = bigint | number; diff --git a/src/core/game/TeamAssignment.ts b/src/core/game/TeamAssignment.ts index fcef06c552..f5a12f221c 100644 --- a/src/core/game/TeamAssignment.ts +++ b/src/core/game/TeamAssignment.ts @@ -1,6 +1,6 @@ -import { PlayerInfo, PlayerType, Team } from "./Game"; import { PseudoRandom } from "../PseudoRandom"; import { simpleHash } from "../Util"; +import { PlayerInfo, PlayerType, Team } from "./Game"; export function assignTeams( players: PlayerInfo[], diff --git a/src/core/game/TerrainMapLoader.ts b/src/core/game/TerrainMapLoader.ts index dc4bc4156c..0fd01b6dcf 100644 --- a/src/core/game/TerrainMapLoader.ts +++ b/src/core/game/TerrainMapLoader.ts @@ -1,7 +1,7 @@ +import { z } from "zod"; +import { GameMapType } from "./Game"; import { GameMap, GameMapImpl } from "./GameMap"; import { GameMapLoader } from "./GameMapLoader"; -import { GameMapType } from "./Game"; -import { z } from "zod"; export type TerrainMapData = { manifest: MapManifest; @@ -66,7 +66,8 @@ export async function genTerrainFromBin( ): Promise { if (data.length !== mapData.width * mapData.height) { throw new Error( - `Invalid data: buffer size ${data.length} incorrect for ${mapData.width}x${mapData.height + `Invalid data: buffer size ${data.length} incorrect for ${mapData.width}x${ + mapData.height } terrain plus 4 bytes for dimensions.`, ); } diff --git a/src/core/game/TrainStation.ts b/src/core/game/TrainStation.ts index 7e844439ca..f0b28eced6 100644 --- a/src/core/game/TrainStation.ts +++ b/src/core/game/TrainStation.ts @@ -1,10 +1,10 @@ +import { TrainExecution } from "../execution/TrainExecution"; +import { PseudoRandom } from "../PseudoRandom"; +import { GraphAdapter } from "../pathfinding/SerialAStar"; import { Game, Player, Unit, UnitType } from "./Game"; +import { TileRef } from "./GameMap"; import { GameUpdateType, RailTile, RailType } from "./GameUpdates"; -import { GraphAdapter } from "../pathfinding/SerialAStar"; -import { PseudoRandom } from "../PseudoRandom"; import { Railroad } from "./Railroad"; -import { TileRef } from "./GameMap"; -import { TrainExecution } from "../execution/TrainExecution"; /** * Handle train stops at various station types diff --git a/src/core/game/TransportShipUtils.ts b/src/core/game/TransportShipUtils.ts index 861cfadc80..df335b714a 100644 --- a/src/core/game/TransportShipUtils.ts +++ b/src/core/game/TransportShipUtils.ts @@ -1,7 +1,7 @@ -import { Game, Player, UnitType } from "./Game"; -import { GameMap, TileRef, andFN, manhattanDistFN } from "./GameMap"; -import { MiniAStar } from "../pathfinding/MiniAStar"; import { PathFindResultType } from "../pathfinding/AStar"; +import { MiniAStar } from "../pathfinding/MiniAStar"; +import { Game, Player, UnitType } from "./Game"; +import { andFN, GameMap, manhattanDistFN, TileRef } from "./GameMap"; export function canBuildTransportShip( game: Game, diff --git a/src/core/game/UnitGrid.ts b/src/core/game/UnitGrid.ts index bb8b52d0d3..b790c2a825 100644 --- a/src/core/game/UnitGrid.ts +++ b/src/core/game/UnitGrid.ts @@ -1,5 +1,5 @@ -import { GameMap, TileRef } from "./GameMap"; import { PlayerID, Unit, UnitType } from "./Game"; +import { GameMap, TileRef } from "./GameMap"; import { UnitView } from "./GameView"; export type UnitPredicate = (value: { @@ -159,8 +159,8 @@ export class UnitGrid { if (!unit.isActive()) continue; const distSquared = this.squaredDistanceFromTile(unit, tile); if (distSquared > rangeSquared) continue; - // eslint-disable-next-line sort-keys - const value = { unit, distSquared }; + + const value = { distSquared, unit }; if (predicate !== undefined && !predicate(value)) continue; nearby.push(value); } diff --git a/src/core/game/UnitImpl.ts b/src/core/game/UnitImpl.ts index 037f0e23bc..df5819d2e8 100644 --- a/src/core/game/UnitImpl.ts +++ b/src/core/game/UnitImpl.ts @@ -1,3 +1,4 @@ +import { simpleHash, toInt, withinInt } from "../Util"; import { AllUnitParams, MessageType, @@ -9,11 +10,10 @@ import { UnitInfo, UnitType, } from "./Game"; -import { GameUpdateType, UnitUpdate } from "./GameUpdates"; -import { simpleHash, toInt, withinInt } from "../Util"; import { GameImpl } from "./GameImpl"; -import { PlayerImpl } from "./PlayerImpl"; import { TileRef } from "./GameMap"; +import { GameUpdateType, UnitUpdate } from "./GameUpdates"; +import { PlayerImpl } from "./PlayerImpl"; export class UnitImpl implements Unit { private _active = true; @@ -114,33 +114,31 @@ export class UnitImpl implements Unit { return this._id; } - /* eslint-disable sort-keys */ toUpdate(): UnitUpdate { return { - type: GameUpdateType.Unit, - unitType: this._type, + constructionType: this._constructionType, + hasTrainStation: this._hasTrainStation, + health: this.hasHealth() ? Number(this._health) : undefined, id: this._id, - troops: this._troops, - ownerID: this._owner.smallID(), - lastOwnerID: this._lastOwner?.smallID(), isActive: this._active, + lastOwnerID: this._lastOwner?.smallID(), + lastPos: this._lastTile, + level: this.level(), + loaded: this._loaded, + missileTimerQueue: this._missileTimerQueue, + ownerID: this._owner.smallID(), + pos: this._tile, reachedTarget: this._reachedTarget, retreating: this._retreating, - pos: this._tile, targetable: this._targetable, - lastPos: this._lastTile, - health: this.hasHealth() ? Number(this._health) : undefined, - constructionType: this._constructionType, - targetUnitId: this._targetUnit?.id() ?? undefined, targetTile: this.targetTile() ?? undefined, - missileTimerQueue: this._missileTimerQueue, - level: this.level(), - hasTrainStation: this._hasTrainStation, + targetUnitId: this._targetUnit?.id() ?? undefined, trainType: this._trainType, - loaded: this._loaded, + troops: this._troops, + type: GameUpdateType.Unit, + unitType: this._type, }; } - /* eslint-enable sort-keys */ type(): UnitType { return this._type; diff --git a/src/core/pathfinding/AStar.ts b/src/core/pathfinding/AStar.ts index 63fbf907f5..4056c684fc 100644 --- a/src/core/pathfinding/AStar.ts +++ b/src/core/pathfinding/AStar.ts @@ -11,19 +11,19 @@ export enum PathFindResultType { } export type AStarResult = | { - type: PathFindResultType.NextTile; - node: NodeType; - } + type: PathFindResultType.NextTile; + node: NodeType; + } | { - type: PathFindResultType.Pending; - } + type: PathFindResultType.Pending; + } | { - type: PathFindResultType.Completed; - node: NodeType; - } + type: PathFindResultType.Completed; + node: NodeType; + } | { - type: PathFindResultType.PathNotFound; - }; + type: PathFindResultType.PathNotFound; + }; export type Point = { x: number; diff --git a/src/core/pathfinding/MiniAStar.ts b/src/core/pathfinding/MiniAStar.ts index c4c227b043..826edc6bc8 100644 --- a/src/core/pathfinding/MiniAStar.ts +++ b/src/core/pathfinding/MiniAStar.ts @@ -1,7 +1,7 @@ -import { AStar, PathFindResultType } from "./AStar"; +import { Cell } from "../game/Game"; import { GameMap, TileRef } from "../game/GameMap"; +import { AStar, PathFindResultType } from "./AStar"; import { GraphAdapter, SerialAStar } from "./SerialAStar"; -import { Cell } from "../game/Game"; export class GameMapAdapter implements GraphAdapter { constructor( diff --git a/src/core/pathfinding/PathFinding.ts b/src/core/pathfinding/PathFinding.ts index 2711b8b39d..e788a5985d 100644 --- a/src/core/pathfinding/PathFinding.ts +++ b/src/core/pathfinding/PathFinding.ts @@ -1,9 +1,9 @@ -import { AStar, AStarResult, PathFindResultType } from "./AStar"; +import { Game } from "../game/Game"; import { GameMap, TileRef } from "../game/GameMap"; +import { PseudoRandom } from "../PseudoRandom"; import { DistanceBasedBezierCurve } from "../utilities/Line"; -import { Game } from "../game/Game"; +import { AStar, AStarResult, PathFindResultType } from "./AStar"; import { MiniAStar } from "./MiniAStar"; -import { PseudoRandom } from "../PseudoRandom"; const parabolaMinHeight = 50; @@ -148,8 +148,7 @@ export class PathFinder { } if (this.game.manhattanDist(curr, dst) < dist) { - // eslint-disable-next-line sort-keys - return { type: PathFindResultType.Completed, node: curr }; + return { node: curr, type: PathFindResultType.Completed }; } if (this.computeFinished) { @@ -165,8 +164,8 @@ export class PathFinder { if (tile === undefined) { throw new Error("missing tile"); } - // eslint-disable-next-line sort-keys - return { type: PathFindResultType.NextTile, node: tile }; + + return { node: tile, type: PathFindResultType.NextTile }; } } diff --git a/src/core/pathfinding/SerialAStar.ts b/src/core/pathfinding/SerialAStar.ts index 13439c818a..703cca73a4 100644 --- a/src/core/pathfinding/SerialAStar.ts +++ b/src/core/pathfinding/SerialAStar.ts @@ -1,5 +1,5 @@ -import { AStar, PathFindResultType } from "./AStar"; import FastPriorityQueue from "fastpriorityqueue"; +import { AStar, PathFindResultType } from "./AStar"; /** * Implement this interface with your graph to find paths with A* @@ -48,18 +48,16 @@ export class SerialAStar implements AStar { this.sources.forEach((startPoint) => { this.fwdGScore.set(startPoint, 0); this.fwdOpenSet.add({ - tile: startPoint, - // eslint-disable-next-line sort-keys fScore: this.heuristic(startPoint, dst), + tile: startPoint, }); }); // Initialize backward search from destination this.bwdGScore.set(dst, 0); this.bwdOpenSet.add({ - tile: dst, - // eslint-disable-next-line sort-keys fScore: this.heuristic(dst, this.findClosestSource(dst)), + tile: dst, }); } @@ -128,7 +126,8 @@ export class SerialAStar implements AStar { const openSet = isForward ? this.fwdOpenSet : this.bwdOpenSet; const cameFrom = isForward ? this.fwdCameFrom : this.bwdCameFrom; - const tentativeGScore = (gScore.get(current) ?? 0) + this.graph.cost(neighbor); + const tentativeGScore = + (gScore.get(current) ?? 0) + this.graph.cost(neighbor); let penalty = 0; // With a direction change penalty, the path will get as straight as possible if (this.directionChangePenalty > 0) { @@ -150,8 +149,8 @@ export class SerialAStar implements AStar { const fScore = totalG + this.heuristic(neighbor, isForward ? this.dst : this.closestSource); - // eslint-disable-next-line sort-keys - openSet.add({ tile: neighbor, fScore }); + + openSet.add({ fScore, tile: neighbor }); } } } diff --git a/src/core/validations/username.ts b/src/core/validations/username.ts index c587267a85..6e73dd296b 100644 --- a/src/core/validations/username.ts +++ b/src/core/validations/username.ts @@ -1,14 +1,14 @@ import { - RegExpMatcher, collapseDuplicatesTransformer, englishDataset, englishRecommendedTransformers, + RegExpMatcher, resolveConfusablesTransformer, resolveLeetSpeakTransformer, skipNonAlphabeticTransformer, } from "obscenity"; -import { simpleHash } from "../Util"; import { translateText } from "../../client/Utils"; +import { simpleHash } from "../Util"; const matcher = new RegExpMatcher({ ...englishDataset.build(), @@ -50,8 +50,7 @@ export function validateUsername(username: string): { error?: string; } { if (typeof username !== "string") { - // eslint-disable-next-line sort-keys - return { isValid: false, error: translateText("username.not_string") }; + return { error: translateText("username.not_string"), isValid: false }; } if (username.length < MIN_USERNAME_LENGTH) { diff --git a/src/core/worker/Worker.worker.ts b/src/core/worker/Worker.worker.ts index 28e979c7ec..ad1f7ff725 100644 --- a/src/core/worker/Worker.worker.ts +++ b/src/core/worker/Worker.worker.ts @@ -1,3 +1,7 @@ +import version from "../../../resources/version.txt"; +import { createGameRunner, GameRunner } from "../GameRunner"; +import { FetchGameMapLoader } from "../game/FetchGameMapLoader"; +import { ErrorUpdate, GameUpdateViewData } from "../game/GameUpdates"; import { AttackAveragePositionResultMessage, InitializedMessage, @@ -8,10 +12,6 @@ import { TransportShipSpawnResultMessage, WorkerMessage, } from "./WorkerMessages"; -import { ErrorUpdate, GameUpdateViewData } from "../game/GameUpdates"; -import { GameRunner, createGameRunner } from "../GameRunner"; -import { FetchGameMapLoader } from "../game/FetchGameMapLoader"; -import version from "../../../resources/version.txt"; const ctx: Worker = self as unknown as Worker; let gameRunner: Promise | null = null; diff --git a/src/core/worker/WorkerClient.ts b/src/core/worker/WorkerClient.ts index 1017cc7f18..f4cc16ab7c 100644 --- a/src/core/worker/WorkerClient.ts +++ b/src/core/worker/WorkerClient.ts @@ -5,16 +5,19 @@ import { PlayerID, PlayerProfile, } from "../game/Game"; -import { ClientID, GameStartInfo, Turn } from "../Schemas"; -import { ErrorUpdate, GameUpdateViewData } from "../game/GameUpdates"; import { TileRef } from "../game/GameMap"; -import { WorkerMessage } from "./WorkerMessages"; +import { ErrorUpdate, GameUpdateViewData } from "../game/GameUpdates"; +import { ClientID, GameStartInfo, Turn } from "../Schemas"; import { generateID } from "../Util"; +import { WorkerMessage } from "./WorkerMessages"; export class WorkerClient { private readonly worker: Worker; private isInitialized = false; - private readonly messageHandlers: Map void>; + private readonly messageHandlers: Map< + string, + (message: WorkerMessage) => void + >; private gameUpdateCallback?: ( update: GameUpdateViewData | ErrorUpdate, ) => void; diff --git a/src/core/worker/WorkerMessages.ts b/src/core/worker/WorkerMessages.ts index 3f24796c02..059fbe3f75 100644 --- a/src/core/worker/WorkerMessages.ts +++ b/src/core/worker/WorkerMessages.ts @@ -1,12 +1,12 @@ -import { ClientID, GameStartInfo, Turn } from "../Schemas"; import { PlayerActions, PlayerBorderTiles, PlayerID, PlayerProfile, } from "../game/Game"; -import { GameUpdateViewData } from "../game/GameUpdates"; import { TileRef } from "../game/GameMap"; +import { GameUpdateViewData } from "../game/GameUpdates"; +import { ClientID, GameStartInfo, Turn } from "../Schemas"; export type WorkerMessageType = | "heartbeat" diff --git a/src/server/Archive.ts b/src/server/Archive.ts index bda347292a..ecc4f958b8 100644 --- a/src/server/Archive.ts +++ b/src/server/Archive.ts @@ -1,8 +1,8 @@ -import { AnalyticsRecord, GameID, GameRecord } from "../core/Schemas"; import { S3 } from "@aws-sdk/client-s3"; import { getServerConfigFromServer } from "../core/configuration/ConfigLoader"; -import { logger } from "./Logger"; +import { AnalyticsRecord, GameID, GameRecord } from "../core/Schemas"; import { replacer } from "../core/Util"; +import { logger } from "./Logger"; const config = getServerConfigFromServer(); @@ -10,14 +10,13 @@ const log = logger.child({ component: "Archive" }); // R2 client configuration const r2 = new S3({ - region: "auto", // R2 ignores region, but it's required by the SDK - /* eslint-disable sort-keys */ - endpoint: config.r2Endpoint(), credentials: { accessKeyId: config.r2AccessKey(), secretAccessKey: config.r2SecretKey(), }, - /* eslint-disable sort-keys */ + + endpoint: config.r2Endpoint(), + region: "auto", // R2 ignores region, but it's required by the SDK }); const bucket = config.r2Bucket(); @@ -49,8 +48,8 @@ export async function archive(gameRecord: GameRecord) { const { message, stack, name } = error; log.error(`${gameRecord.info.gameID}: Final archive error: ${error}`, { message, - stack, name, + stack, ...(error && typeof error === "object" ? error : {}), }); } @@ -60,11 +59,11 @@ async function archiveAnalyticsToR2(gameRecord: GameRecord) { // Create analytics data object const { info, version, gitCommit, subdomain, domain } = gameRecord; const analyticsData: AnalyticsRecord = { - info, - version, + domain, gitCommit, + info, subdomain, - domain, + version, }; try { @@ -72,10 +71,10 @@ async function archiveAnalyticsToR2(gameRecord: GameRecord) { const analyticsKey = `${info.gameID}.json`; await r2.putObject({ - Bucket: bucket, - Key: `${analyticsFolder}/${analyticsKey}`, Body: JSON.stringify(analyticsData, replacer), + Bucket: bucket, ContentType: "application/json", + Key: `${analyticsFolder}/${analyticsKey}`, }); log.info(`${info.gameID}: successfully wrote game analytics to R2`); @@ -91,8 +90,8 @@ async function archiveAnalyticsToR2(gameRecord: GameRecord) { const { message, stack, name } = error; log.error(`${info.gameID}: Error writing game analytics to R2: ${error}`, { message, - stack, name, + stack, ...(error && typeof error === "object" ? error : {}), }); throw error; @@ -110,10 +109,10 @@ async function archiveFullGameToR2(gameRecord: GameRecord) { try { await r2.putObject({ - Bucket: bucket, - Key: `${gameFolder}/${recordCopy.info.gameID}`, Body: JSON.stringify(recordCopy, replacer), + Bucket: bucket, ContentType: "application/json", + Key: `${gameFolder}/${recordCopy.info.gameID}`, }); } catch (error) { log.error(`error saving game ${gameRecord.info.gameID}`); @@ -148,8 +147,8 @@ export async function readGameRecord( // Log the error for monitoring purposes log.error(`${gameId}: Error reading game record from R2: ${error}`, { message, - stack, name, + stack, ...(error && typeof error === "object" ? error : {}), }); @@ -179,8 +178,8 @@ export async function gameRecordExists(gameId: GameID): Promise { } log.error(`${gameId}: Error checking archive existence: ${error}`, { message, - stack, name, + stack, ...(error && typeof error === "object" ? error : {}), }); return false; diff --git a/src/server/Client.ts b/src/server/Client.ts index af38b3a82e..ecac7f885e 100644 --- a/src/server/Client.ts +++ b/src/server/Client.ts @@ -1,7 +1,7 @@ -import { ClientID, Winner } from "../core/Schemas"; -import { Tick } from "../core/game/Game"; -import { TokenPayload } from "../core/ApiSchemas"; import WebSocket from "ws"; +import { TokenPayload } from "../core/ApiSchemas"; +import { Tick } from "../core/game/Game"; +import { ClientID, Winner } from "../core/Schemas"; export class Client { public lastPing: number = Date.now(); diff --git a/src/server/Cloudflare.ts b/src/server/Cloudflare.ts index 2de807f05e..6ca1f1fd0c 100644 --- a/src/server/Cloudflare.ts +++ b/src/server/Cloudflare.ts @@ -1,8 +1,8 @@ -import { promises as fs } from "fs"; -import { logger } from "./Logger"; import { spawn } from "child_process"; +import { promises as fs } from "fs"; import yaml from "js-yaml"; import { z } from "zod"; +import { logger } from "./Logger"; const log = logger.child({ module: "cloudflare", @@ -70,7 +70,7 @@ export class Cloudflare { const response = await fetch(url, { body: data ? JSON.stringify(data) : undefined, headers: { - "Authorization": `Bearer ${this.apiToken}`, + Authorization: `Bearer ${this.apiToken}`, "Content-Type": "application/json", }, method, @@ -185,7 +185,7 @@ export class Cloudflare { const tunnelConfig: CloudflaredConfig = { "credentials-file": this.credsPath, - "ingress": [ + ingress: [ ...Array.from(subdomainToService.entries()).map( ([subdomain, service]) => ({ hostname: `${subdomain}.${domain}`, @@ -196,7 +196,7 @@ export class Cloudflare { service: "http_status:404", }, ], - "tunnel": tunnelId, + tunnel: tunnelId, }; // Write config file diff --git a/src/server/GameManager.ts b/src/server/GameManager.ts index cf9865af19..f9f941ef1d 100644 --- a/src/server/GameManager.ts +++ b/src/server/GameManager.ts @@ -1,9 +1,9 @@ +import { Logger } from "winston"; +import { ServerConfig } from "../core/configuration/Config"; import { Difficulty, GameMapType, GameMode, GameType } from "../core/game/Game"; import { GameConfig, GameID } from "../core/Schemas"; -import { GamePhase, GameServer } from "./GameServer"; import { Client } from "./Client"; -import { Logger } from "winston"; -import { ServerConfig } from "../core/configuration/Config"; +import { GamePhase, GameServer } from "./GameServer"; export class GameManager { private games: Map = new Map(); @@ -41,8 +41,8 @@ export class GameManager { { bots: 400, difficulty: Difficulty.Medium, - disableNPCs: false, disabledUnits: [], + disableNPCs: false, donateGold: false, donateTroops: false, gameMap: GameMapType.World, diff --git a/src/server/GameServer.ts b/src/server/GameServer.ts index dd8d0d81ba..033a554c8f 100644 --- a/src/server/GameServer.ts +++ b/src/server/GameServer.ts @@ -1,3 +1,9 @@ +import ipAnonymize from "ip-anonymize"; +import { Logger } from "winston"; +import WebSocket from "ws"; +import { z } from "zod"; +import { GameEnv, ServerConfig } from "../core/configuration/Config"; +import { GameType } from "../core/game/Game"; import { ClientID, ClientSendWinnerMessage, @@ -14,17 +20,11 @@ import { ServerTurnMessage, Turn, } from "../core/Schemas"; -import { GameEnv, ServerConfig } from "../core/configuration/Config"; -import { Client } from "./Client"; -import { GameType } from "../core/game/Game"; -import { Logger } from "winston"; -import WebSocket from "ws"; -import { archive } from "./Archive"; import { createGameRecord } from "../core/Util"; +import { archive } from "./Archive"; +import { Client } from "./Client"; import { gatekeeper } from "./Gatekeeper"; -import ipAnonymize from "ip-anonymize"; import { postJoinMessageHandler } from "./worker/websocket/handler/message/PostJoinHandler"; -import { z } from "zod"; export enum GamePhase { Lobby = "LOBBY", @@ -44,7 +44,8 @@ export class GameServer { public activeClients: Client[] = []; lobbyCreatorID: string | undefined; private readonly allClients: Map = new Map(); - private readonly clientsDisconnectedStatus: Map = new Map(); + private readonly clientsDisconnectedStatus: Map = + new Map(); private _hasStarted = false; private _startTime: number | null = null; diff --git a/src/server/Gatekeeper.ts b/src/server/Gatekeeper.ts index 047baa94b1..dccf45cb9f 100644 --- a/src/server/Gatekeeper.ts +++ b/src/server/Gatekeeper.ts @@ -1,9 +1,9 @@ // src/server/Security.ts import { NextFunction, Request, Response } from "express"; -import { fileURLToPath } from "url"; import fs from "fs"; import http from "http"; import path from "path"; +import { fileURLToPath } from "url"; export enum LimiterType { Get = "get", diff --git a/src/server/Logger.ts b/src/server/Logger.ts index 193b54c49f..5a4de59a76 100644 --- a/src/server/Logger.ts +++ b/src/server/Logger.ts @@ -1,14 +1,15 @@ -import * as dotenv from "dotenv"; import * as logsAPI from "@opentelemetry/api-logs"; +import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http"; import { LoggerProvider, SimpleLogRecordProcessor, } from "@opentelemetry/sdk-logs"; -import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http"; import { OpenTelemetryTransportV3 } from "@opentelemetry/winston-transport"; -import { getOtelResource } from "./OtelResource"; -import { getServerConfigFromServer } from "../core/configuration/ConfigLoader"; +import * as dotenv from "dotenv"; import winston from "winston"; +import { getServerConfigFromServer } from "../core/configuration/ConfigLoader"; +import { getOtelResource } from "./OtelResource"; + dotenv.config(); const config = getServerConfigFromServer(); @@ -28,9 +29,8 @@ if (config.otelEnabled()) { // Add OTLP exporter for logs const logExporter = new OTLPLogExporter({ - url: `${config.otelEndpoint()}/v1/logs`, - // eslint-disable-next-line sort-keys headers, + url: `${config.otelEndpoint()}/v1/logs`, }); // Add a log processor with the exporter @@ -56,18 +56,18 @@ const addSeverityFormat = winston.format((info) => { // Define your base/parent logger const logger = winston.createLogger({ - level: "info", - /* eslint-disable sort-keys */ + defaultMeta: { + environment: process.env.GAME_ENV ?? "prod", + service: "openfront", + }, + format: winston.format.combine( winston.format.timestamp(), addSeverityFormat(), winston.format.json(), ), - defaultMeta: { - service: "openfront", - environment: process.env.GAME_ENV ?? "prod", - }, - /* eslint-enable sort-keys */ + level: "info", + transports: [ new winston.transports.Console(), new OpenTelemetryTransportV3(), diff --git a/src/server/MapPlaylist.ts b/src/server/MapPlaylist.ts index b87dd4c99b..ef2e574563 100644 --- a/src/server/MapPlaylist.ts +++ b/src/server/MapPlaylist.ts @@ -1,3 +1,4 @@ +import { getServerConfigFromServer } from "../core/configuration/ConfigLoader"; import { Difficulty, Duos, @@ -8,9 +9,8 @@ import { Quads, Trios, } from "../core/game/Game"; -import { GameConfig, TeamCountConfig } from "../core/Schemas"; import { PseudoRandom } from "../core/PseudoRandom"; -import { getServerConfigFromServer } from "../core/configuration/ConfigLoader"; +import { GameConfig, TeamCountConfig } from "../core/Schemas"; import { logger } from "./Logger"; const log = logger.child({}); @@ -78,8 +78,8 @@ export class MapPlaylist { return { bots: 400, difficulty: Difficulty.Medium, - disableNPCs: mode === GameMode.Team, disabledUnits: [], + disableNPCs: mode === GameMode.Team, donateGold: true, donateTroops: true, gameMap: map, @@ -105,7 +105,9 @@ export class MapPlaylist { log.info(`Generated map playlist in ${i + 1} attempts`); const next = this.mapsPlaylist.shift(); if (next !== undefined) return next; - log.error("Playlist unexpectedly empty after successful shuffle; using fallback."); + log.error( + "Playlist unexpectedly empty after successful shuffle; using fallback.", + ); return { map: GameMapType.World, mode: GameMode.FFA }; } } diff --git a/src/server/Master.ts b/src/server/Master.ts index b33ac52413..4d76404b92 100644 --- a/src/server/Master.ts +++ b/src/server/Master.ts @@ -1,18 +1,22 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -import { ApiEnvResponse, ApiPublicLobbiesResponse } from "../core/ExpressSchemas"; -import { LimiterType, gatekeeper } from "./Gatekeeper"; -import { GameInfo } from "../core/Schemas"; -import { ID } from "../core/BaseSchemas"; -import { MapPlaylist } from "./MapPlaylist"; + import cluster from "cluster"; import express from "express"; +import rateLimit from "express-rate-limit"; +import http from "http"; +import path from "path"; import { fileURLToPath } from "url"; -import { generateID } from "../core/Util"; +import { ID } from "../core/BaseSchemas"; import { getServerConfigFromServer } from "../core/configuration/ConfigLoader"; -import http from "http"; +import { + ApiEnvResponse, + ApiPublicLobbiesResponse, +} from "../core/ExpressSchemas"; +import { GameInfo } from "../core/Schemas"; +import { generateID } from "../core/Util"; +import { gatekeeper, LimiterType } from "./Gatekeeper"; import { logger } from "./Logger"; -import path from "path"; -import rateLimit from "express-rate-limit"; +import { MapPlaylist } from "./MapPlaylist"; const config = getServerConfigFromServer(); const playlist = new MapPlaylist(); diff --git a/src/server/OtelResource.ts b/src/server/OtelResource.ts index 8bd49d4d46..6d48fb4424 100644 --- a/src/server/OtelResource.ts +++ b/src/server/OtelResource.ts @@ -1,9 +1,9 @@ +import { resourceFromAttributes } from "@opentelemetry/resources"; import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION, } from "@opentelemetry/semantic-conventions"; import { getServerConfigFromServer } from "../core/configuration/ConfigLoader"; -import { resourceFromAttributes } from "@opentelemetry/resources"; const config = getServerConfigFromServer(); @@ -17,15 +17,14 @@ export function getOtelResource() { export function getPromLabels() { return { - "service.instance.id": process.env.HOSTNAME, - /* eslint-disable sort-keys */ - "openfront.environment": config.env(), - "openfront.host": process.env.HOST, - "openfront.domain": process.env.DOMAIN, - "openfront.subdomain": process.env.SUBDOMAIN, "openfront.component": process.env.WORKER_ID ? "Worker " + process.env.WORKER_ID : "Master", - /* eslint-enable sort-keys */ + "openfront.domain": process.env.DOMAIN, + + "openfront.environment": config.env(), + "openfront.host": process.env.HOST, + "openfront.subdomain": process.env.SUBDOMAIN, + "service.instance.id": process.env.HOSTNAME, }; } diff --git a/src/server/PrivilegeRefresher.ts b/src/server/PrivilegeRefresher.ts index a8043fd3d4..51c270a565 100644 --- a/src/server/PrivilegeRefresher.ts +++ b/src/server/PrivilegeRefresher.ts @@ -1,11 +1,11 @@ +import { base64url } from "jose"; +import { Logger } from "winston"; +import { CosmeticsSchema } from "../core/CosmeticSchemas"; import { FailOpenPrivilegeChecker, PrivilegeChecker, PrivilegeCheckerImpl, } from "./Privilege"; -import { CosmeticsSchema } from "../core/CosmeticSchemas"; -import { Logger } from "winston"; -import { base64url } from "jose"; // Refreshes the privilege checker every 5 minutes. // WARNING: This fails open if cosmetics.json is not available. diff --git a/src/server/Server.ts b/src/server/Server.ts index 5a9da84493..8a00653ead 100644 --- a/src/server/Server.ts +++ b/src/server/Server.ts @@ -1,8 +1,8 @@ +import cluster from "cluster"; import * as dotenv from "dotenv"; -import { Cloudflare, TunnelConfig } from "./Cloudflare"; import { GameEnv } from "../core/configuration/Config"; -import cluster from "cluster"; import { getServerConfigFromServer } from "../core/configuration/ConfigLoader"; +import { Cloudflare, TunnelConfig } from "./Cloudflare"; import { startMaster } from "./Master"; import { startWorker } from "./Worker"; diff --git a/src/server/Worker.ts b/src/server/Worker.ts index 202dd184a8..858e4f0763 100644 --- a/src/server/Worker.ts +++ b/src/server/Worker.ts @@ -1,28 +1,28 @@ +import express, { NextFunction, Request, Response } from "express"; +import rateLimit from "express-rate-limit"; +import http from "http"; +import ipAnonymize from "ip-anonymize"; +import path from "path"; +import { fileURLToPath } from "url"; +import { WebSocket, WebSocketServer } from "ws"; +import { z } from "zod"; +import { ID } from "../core/BaseSchemas"; +import { GameEnv } from "../core/configuration/Config"; +import { getServerConfigFromServer } from "../core/configuration/ConfigLoader"; +import { GameType } from "../core/game/Game"; +import { GameRecord, GameRecordSchema } from "../core/Schemas"; import { CreateGameInputSchema, GameInputSchema, WorkerApiGameIdExists, } from "../core/WorkerSchemas"; -import { GameRecord, GameRecordSchema } from "../core/Schemas"; -import { LimiterType, gatekeeper } from "./Gatekeeper"; -import { WebSocket, WebSocketServer } from "ws"; import { archive, readGameRecord } from "./Archive"; -import express, { NextFunction, Request, Response } from "express"; -import { GameEnv } from "../core/configuration/Config"; import { GameManager } from "./GameManager"; -import { GameType } from "../core/game/Game"; -import { ID } from "../core/BaseSchemas"; +import { gatekeeper, LimiterType } from "./Gatekeeper"; +import { logger } from "./Logger"; import { PrivilegeRefresher } from "./PrivilegeRefresher"; -import { fileURLToPath } from "url"; -import { getServerConfigFromServer } from "../core/configuration/ConfigLoader"; -import http from "http"; import { initWorkerMetrics } from "./WorkerMetrics"; -import ipAnonymize from "ip-anonymize"; -import { logger } from "./Logger"; -import path from "path"; import { preJoinMessageHandler } from "./worker/websocket/handler/message/PreJoinHandler"; -import rateLimit from "express-rate-limit"; -import { z } from "zod"; const config = getServerConfigFromServer(); @@ -134,17 +134,9 @@ export async function startWorker() { const game = gm.createGame(id, gc, creatorClientID); log.info( - `Worker ${ - workerId - }: IP ${ - ipAnonymize(clientIP) - } creating ${ + `Worker ${workerId}: IP ${ipAnonymize(clientIP)} creating ${ game.isPublic() ? "Public" : "Private" - }${ - gc?.gameMode ? ` ${gc.gameMode}` : "" - } game with id ${ - id - }${ + }${gc?.gameMode ? ` ${gc.gameMode}` : ""} game with id ${id}${ creatorClientID ? `, creator: ${creatorClientID}` : "" }`, ); diff --git a/src/server/WorkerMetrics.ts b/src/server/WorkerMetrics.ts index e233f8ac98..4af92f551e 100644 --- a/src/server/WorkerMetrics.ts +++ b/src/server/WorkerMetrics.ts @@ -1,9 +1,12 @@ -import * as dotenv from "dotenv"; -import { MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics"; -import { getOtelResource, getPromLabels } from "./OtelResource"; -import { GameManager } from "./GameManager"; import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http"; +import { + MeterProvider, + PeriodicExportingMetricReader, +} from "@opentelemetry/sdk-metrics"; +import * as dotenv from "dotenv"; import { getServerConfigFromServer } from "../core/configuration/ConfigLoader"; +import { GameManager } from "./GameManager"; +import { getOtelResource, getPromLabels } from "./OtelResource"; dotenv.config(); @@ -28,8 +31,8 @@ export function initWorkerMetrics(gameManager: GameManager): void { // Configure the metric reader const metricReader = new PeriodicExportingMetricReader({ - exportIntervalMillis: 15000, // Export metrics every 15 seconds exporter: metricExporter, + exportIntervalMillis: 15000, // Export metrics every 15 seconds }); // Create a meter provider diff --git a/src/server/jwt.ts b/src/server/jwt.ts index e259accbef..a1ea941d56 100644 --- a/src/server/jwt.ts +++ b/src/server/jwt.ts @@ -1,19 +1,19 @@ +import { jwtVerify } from "jose"; +import { z } from "zod"; import { TokenPayload, TokenPayloadSchema, UserMeResponse, UserMeResponseSchema, } from "../core/ApiSchemas"; -import { PersistentIdSchema } from "../core/Schemas"; import { ServerConfig } from "../core/configuration/Config"; -import { jwtVerify } from "jose"; -import { z } from "zod"; +import { PersistentIdSchema } from "../core/Schemas"; type TokenVerificationResult = | { - persistentId: string; - claims: TokenPayload | null; - } + persistentId: string; + claims: TokenPayload | null; + } | false; export async function verifyClientToken( @@ -21,8 +21,7 @@ export async function verifyClientToken( config: ServerConfig, ): Promise { if (PersistentIdSchema.safeParse(token).success) { - // eslint-disable-next-line sort-keys - return { persistentId: token, claims: null }; + return { claims: null, persistentId: token }; } try { const issuer = config.jwtIssuer(); diff --git a/src/server/worker/websocket/handler/message/PostJoinHandler.ts b/src/server/worker/websocket/handler/message/PostJoinHandler.ts index 00b5b783a8..cea44173ad 100644 --- a/src/server/worker/websocket/handler/message/PostJoinHandler.ts +++ b/src/server/worker/websocket/handler/message/PostJoinHandler.ts @@ -1,3 +1,5 @@ +import { Logger } from "winston"; +import { z } from "zod"; import { ClientMessageSchema, ClientSendWinnerMessage, @@ -5,8 +7,6 @@ import { } from "../../../../../core/Schemas"; import { Client } from "../../../../Client"; import { GameServer } from "../../../../GameServer"; -import { Logger } from "winston"; -import { z } from "zod"; export async function postJoinMessageHandler( gs: GameServer, @@ -118,7 +118,9 @@ export async function postJoinMessageHandler( function handleWinner( gs: GameServer, log: Logger, - client: Client, clientMsg: ClientSendWinnerMessage) { + client: Client, + clientMsg: ClientSendWinnerMessage, +) { if ( gs.outOfSyncClients.has(client.clientID) || gs.kickedClients.has(client.clientID) || diff --git a/src/server/worker/websocket/handler/message/PreJoinHandler.ts b/src/server/worker/websocket/handler/message/PreJoinHandler.ts index c4896942df..7c869b54f5 100644 --- a/src/server/worker/websocket/handler/message/PreJoinHandler.ts +++ b/src/server/worker/websocket/handler/message/PreJoinHandler.ts @@ -1,17 +1,17 @@ +import http from "http"; +import ipAnonymize from "ip-anonymize"; +import { WebSocket } from "ws"; +import { z } from "zod"; +import { getServerConfigFromServer } from "../../../../../core/configuration/ConfigLoader"; import { ClientMessageSchema, ServerErrorMessage, } from "../../../../../core/Schemas"; -import { getUserMe, verifyClientToken } from "../../../../jwt"; import { Client } from "../../../../Client"; import { GameManager } from "../../../../GameManager"; -import { PrivilegeRefresher } from "../../../../PrivilegeRefresher"; -import { WebSocket } from "ws"; -import { getServerConfigFromServer } from "../../../../../core/configuration/ConfigLoader"; -import http from "http"; -import ipAnonymize from "ip-anonymize"; +import { getUserMe, verifyClientToken } from "../../../../jwt"; import { logger } from "../../../../Logger"; -import { z } from "zod"; +import { PrivilegeRefresher } from "../../../../PrivilegeRefresher"; const config = getServerConfigFromServer(); @@ -62,13 +62,13 @@ async function handleJoinMessage( ): Promise< | undefined | { - success: true; - } + success: true; + } | { - success: false; - code: 1002; - error: string; - reason: + success: false; + code: 1002; + error: string; + reason: | "ClientJoinMessageSchema" | "Flag invalid" | "Flag restricted" @@ -78,19 +78,19 @@ async function handleJoinMessage( | "Pattern restricted" | "Pattern unlisted" | "Unauthorized"; - } + } | { - success: false; - code: 1011; - reason: "Internal server error"; - error: string; - } + success: false; + code: 1011; + reason: "Internal server error"; + error: string; + } > { const forwarded = req.headers["x-forwarded-for"]; const ip = Array.isArray(forwarded) ? forwarded[0] : // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - forwarded || req.socket.remoteAddress || "unknown"; + forwarded || req.socket.remoteAddress || "unknown"; try { // Parse and handle client messages @@ -121,8 +121,8 @@ async function handleJoinMessage( const expectedWorkerId = config.workerIndex(clientMsg.gameID); if (expectedWorkerId !== workerId) { log.warn( - `Worker mismatch: Game ${clientMsg.gameID - } should be on worker ${expectedWorkerId + `Worker mismatch: Game ${clientMsg.gameID} should be on worker ${ + expectedWorkerId }, but this is worker ${workerId}`, ); return; diff --git a/tests/AllianceExtensionExecution.test.ts b/tests/AllianceExtensionExecution.test.ts index 7b60df7efa..35fc3fe82b 100644 --- a/tests/AllianceExtensionExecution.test.ts +++ b/tests/AllianceExtensionExecution.test.ts @@ -1,8 +1,8 @@ -import { Game, Player, PlayerType } from "../src/core/game/Game"; -import { playerInfo, setup } from "./util/Setup"; import { AllianceExtensionExecution } from "../src/core/execution/alliance/AllianceExtensionExecution"; import { AllianceRequestExecution } from "../src/core/execution/alliance/AllianceRequestExecution"; import { AllianceRequestReplyExecution } from "../src/core/execution/alliance/AllianceRequestReplyExecution"; +import { Game, Player, PlayerType } from "../src/core/game/Game"; +import { playerInfo, setup } from "./util/Setup"; let game: Game; let player1: Player; @@ -15,8 +15,8 @@ describe("AllianceExtensionExecution", () => { "ocean_and_land", { infiniteGold: true, - instantBuild: true, infiniteTroops: true, + instantBuild: true, }, [ playerInfo("player1", PlayerType.Human), diff --git a/tests/AllianceRequestExecution.test.ts b/tests/AllianceRequestExecution.test.ts index b6df1e657a..21bae39031 100644 --- a/tests/AllianceRequestExecution.test.ts +++ b/tests/AllianceRequestExecution.test.ts @@ -1,7 +1,7 @@ -import { Game, Player, PlayerType } from "../src/core/game/Game"; -import { playerInfo, setup } from "./util/Setup"; import { AllianceRequestExecution } from "../src/core/execution/alliance/AllianceRequestExecution"; import { AllianceRequestReplyExecution } from "../src/core/execution/alliance/AllianceRequestReplyExecution"; +import { Game, Player, PlayerType } from "../src/core/game/Game"; +import { playerInfo, setup } from "./util/Setup"; let game: Game; let player1: Player; @@ -13,8 +13,8 @@ describe("AllianceRequestExecution", () => { "plains", { infiniteGold: true, - instantBuild: true, infiniteTroops: true, + instantBuild: true, }, [ playerInfo("player1", PlayerType.Human), diff --git a/tests/Attack.test.ts b/tests/Attack.test.ts index 727fc955ac..4c83f57176 100644 --- a/tests/Attack.test.ts +++ b/tests/Attack.test.ts @@ -1,3 +1,6 @@ +import { AttackExecution } from "../src/core/execution/AttackExecution"; +import { SpawnExecution } from "../src/core/execution/SpawnExecution"; +import { TransportShipExecution } from "../src/core/execution/TransportShipExecution"; import { Game, Player, @@ -5,13 +8,10 @@ import { PlayerType, UnitType, } from "../src/core/game/Game"; -import { AttackExecution } from "../src/core/execution/AttackExecution"; -import { SpawnExecution } from "../src/core/execution/SpawnExecution"; -import { TestConfig } from "./util/TestConfig"; import { TileRef } from "../src/core/game/GameMap"; -import { TransportShipExecution } from "../src/core/execution/TransportShipExecution"; -import { constructionExecution } from "./util/utils"; import { setup } from "./util/Setup"; +import { TestConfig } from "./util/TestConfig"; +import { constructionExecution } from "./util/utils"; let game: Game; let attacker: Player; @@ -29,8 +29,8 @@ describe("Attack", () => { beforeEach(async () => { game = await setup("ocean_and_land", { infiniteGold: true, - instantBuild: true, infiniteTroops: true, + instantBuild: true, }); const attackerInfo = new PlayerInfo( "attacker dude", @@ -130,8 +130,8 @@ describe("Attack race condition with alliance requests", () => { beforeEach(async () => { game = await setup("ocean_and_land", { infiniteGold: true, - instantBuild: true, infiniteTroops: true, + instantBuild: true, }); const playerAInfo = new PlayerInfo( diff --git a/tests/AttackStats.test.ts b/tests/AttackStats.test.ts index 785851e9a6..1955aec111 100644 --- a/tests/AttackStats.test.ts +++ b/tests/AttackStats.test.ts @@ -1,7 +1,7 @@ -import { GOLD_INDEX_WAR, GOLD_INDEX_WORK } from "../src/core/StatsSchemas"; -import { Game, Player, PlayerInfo, PlayerType } from "../src/core/game/Game"; import { AttackExecution } from "../src/core/execution/AttackExecution"; import { SpawnExecution } from "../src/core/execution/SpawnExecution"; +import { Game, Player, PlayerInfo, PlayerType } from "../src/core/game/Game"; +import { GOLD_INDEX_WAR, GOLD_INDEX_WORK } from "../src/core/StatsSchemas"; import { setup } from "./util/Setup"; let game: Game; diff --git a/tests/BotBehavior.test.ts b/tests/BotBehavior.test.ts index eb1ea8dd15..701b66958d 100644 --- a/tests/BotBehavior.test.ts +++ b/tests/BotBehavior.test.ts @@ -1,3 +1,5 @@ +import { AllianceExtensionExecution } from "../src/core/execution/alliance/AllianceExtensionExecution"; +import { BotBehavior } from "../src/core/execution/utils/BotBehavior"; import { AllianceRequest, Game, @@ -7,8 +9,6 @@ import { Relation, Tick, } from "../src/core/game/Game"; -import { AllianceExtensionExecution } from "../src/core/execution/alliance/AllianceExtensionExecution"; -import { BotBehavior } from "../src/core/execution/utils/BotBehavior"; import { PseudoRandom } from "../src/core/PseudoRandom"; import { setup } from "./util/Setup"; @@ -77,11 +77,11 @@ describe("BotBehavior.handleAllianceRequests", () => { .mockReturnValue(new Array(alliancesCount)); const mockRequest = { - requestor: () => requestor, - recipient: () => player, - createdAt: () => 0 as unknown as Tick, accept: jest.fn(), + createdAt: () => 0 as unknown as Tick, + recipient: () => player, reject: jest.fn(), + requestor: () => requestor, } as unknown as AllianceRequest; jest @@ -120,8 +120,8 @@ describe("BotBehavior.handleAllianceRequests", () => { test("should accept alliance if requestor is much larger (> 3 times size of recipient) and has too many alliances (>= 3)", () => { const request = setupAllianceRequest({ - numTilesRequestor: 40, alliancesCount: 4, + numTilesRequestor: 40, }); botBehavior.handleAllianceRequests(); @@ -132,8 +132,8 @@ describe("BotBehavior.handleAllianceRequests", () => { test("should accept alliance if requestor is much larger (> 3 times size of recipient) and does not have too many alliances (< 3)", () => { const request = setupAllianceRequest({ - numTilesRequestor: 40, alliancesCount: 2, + numTilesRequestor: 40, }); botBehavior.handleAllianceRequests(); @@ -171,8 +171,8 @@ describe("BotBehavior.handleAllianceExtensionRequests", () => { mockPlayer = { alliances: jest.fn(() => [mockAlliance]), - relation: jest.fn(), id: jest.fn(() => "bot_id"), + relation: jest.fn(), type: jest.fn(() => PlayerType.FakeHuman), }; diff --git a/tests/Censor.test.ts b/tests/Censor.test.ts index fc23b7172f..165c74765a 100644 --- a/tests/Censor.test.ts +++ b/tests/Censor.test.ts @@ -1,6 +1,9 @@ // Mocking the obscenity library to control its behavior in tests. jest.mock("obscenity", () => { return { + collapseDuplicatesTransformer: () => ({}), + englishDataset: { build: () => ({}) }, + englishRecommendedTransformers: {}, RegExpMatcher: class { private readonly dummy: string[] = ["foo", "bar", "leet", "code"]; constructor(_opts: any) {} @@ -16,9 +19,6 @@ jest.mock("obscenity", () => { return this.dummy.some((token) => decoded.includes(token)); } }, - collapseDuplicatesTransformer: () => ({}), - englishRecommendedTransformers: {}, - englishDataset: { build: () => ({}) }, resolveConfusablesTransformer: () => ({}), resolveLeetSpeakTransformer: () => ({}), skipNonAlphabeticTransformer: () => ({}), @@ -32,10 +32,10 @@ jest.mock("../src/client/Utils", () => ({ })); import { - MAX_USERNAME_LENGTH, - MIN_USERNAME_LENGTH, fixProfaneUsername, isProfaneUsername, + MAX_USERNAME_LENGTH, + MIN_USERNAME_LENGTH, sanitizeUsername, validateUsername, } from "../src/core/validations/username"; @@ -53,14 +53,14 @@ describe("username.ts functions", () => { describe("isProfaneUsername & fixProfaneUsername with leet decoding (mocked)", () => { test.each([ - { username: "l33t", profane: true }, // decodes to "leet" - { username: "L33T", profane: true }, - { username: "l33tc0de", profane: true }, // decodes to "leetcode", contains "leet" and "code" - { username: "L33TC0DE", profane: true }, - { username: "foo123", profane: true }, // contains "foo" - { username: "b4r", profane: true }, // decodes to "bar" - { username: "safeName", profane: false }, - { username: "s4f3", profane: false }, // decodes to "safe" but "safe" not in dummy list + { profane: true, username: "l33t" }, // decodes to "leet" + { profane: true, username: "L33T" }, + { profane: true, username: "l33tc0de" }, // decodes to "leetcode", contains "leet" and "code" + { profane: true, username: "L33TC0DE" }, + { profane: true, username: "foo123" }, // contains "foo" + { profane: true, username: "b4r" }, // decodes to "bar" + { profane: false, username: "safeName" }, + { profane: false, username: "s4f3" }, // decodes to "safe" but "safe" not in dummy list ])('isProfaneUsername("%s") → %s', ({ username, profane }) => { expect(isProfaneUsername(username)).toBe(profane); }); @@ -113,17 +113,17 @@ describe("username.ts functions", () => { describe("sanitizeUsername", () => { test.each([ - { input: "GoodName", expected: "GoodName" }, - { input: "a!", expected: "axx" }, - { input: "a$%b", expected: "abx" }, + { expected: "GoodName", input: "GoodName" }, + { expected: "axx", input: "a!" }, + { expected: "abx", input: "a$%b" }, { - input: "abc".repeat(10), expected: "abc" .repeat(Math.floor(MAX_USERNAME_LENGTH / 3)) .slice(0, MAX_USERNAME_LENGTH), + input: "abc".repeat(10), }, - { input: "", expected: "xxx" }, - { input: "Ünicode🐈Test!", expected: "Ünicode🐈Test" }, + { expected: "xxx", input: "" }, + { expected: "Ünicode🐈Test", input: "Ünicode🐈Test!" }, ])('sanitizeUsername("%s") → "%s"', ({ input, expected }) => { const out = sanitizeUsername(input); expect(out).toBe(expected); diff --git a/tests/Colors.test.ts b/tests/Colors.test.ts index 515d193370..762eea9c9d 100644 --- a/tests/Colors.test.ts +++ b/tests/Colors.test.ts @@ -1,8 +1,8 @@ +import { Colord, colord } from "colord"; import { ColorAllocator, selectDistinctColorIndex, } from "../src/core/configuration/ColorAllocator"; -import { Colord, colord } from "colord"; import { blue, botColor, @@ -16,14 +16,14 @@ import { import { ColoredTeams } from "../src/core/game/Game"; const mockColors: Colord[] = [ - colord({ r: 255, g: 0, b: 0 }), - colord({ r: 0, g: 255, b: 0 }), - colord({ r: 0, g: 0, b: 255 }), + colord({ b: 0, g: 0, r: 255 }), + colord({ b: 0, g: 255, r: 0 }), + colord({ b: 255, g: 0, r: 0 }), ]; const fallbackMockColors: Colord[] = [ - colord({ r: 0, g: 0, b: 0 }), - colord({ r: 255, g: 255, b: 255 }), + colord({ b: 0, g: 0, r: 0 }), + colord({ b: 255, g: 255, r: 255 }), ]; const fallbackColors = [...fallbackMockColors, ...mockColors]; @@ -148,19 +148,19 @@ describe("ColorAllocator", () => { describe("selectDistinctColor", () => { test("returns the most distant color", () => { - const assignedColors = [colord({ r: 255, g: 0, b: 0 })]; // bright red + const assignedColors = [colord({ b: 0, g: 0, r: 255 })]; // bright red const availableColors = [ - colord({ r: 254, g: 1, b: 1 }), // too close - colord({ r: 0, g: 255, b: 0 }), // distinct green - colord({ r: 0, g: 0, b: 255 }), // distinct blue + colord({ b: 1, g: 1, r: 254 }), // too close + colord({ b: 0, g: 255, r: 0 }), // distinct green + colord({ b: 255, g: 0, r: 0 }), // distinct blue ]; const result = selectDistinctColorIndex(availableColors, assignedColors); expect(result).not.toBeNull(); const rgb = availableColors[result!].toRgb(); expect([ - { r: 0, g: 255, b: 0, a: 1 }, - { r: 0, g: 0, b: 255, a: 1 }, + { a: 1, b: 0, g: 255, r: 0 }, + { a: 1, b: 255, g: 0, r: 0 }, ]).toContainEqual(rgb); }); }); diff --git a/tests/DeleteUnitExecution.test.ts b/tests/DeleteUnitExecution.test.ts index f529e6112e..f2ad5cc550 100644 --- a/tests/DeleteUnitExecution.test.ts +++ b/tests/DeleteUnitExecution.test.ts @@ -1,3 +1,5 @@ +import { DeleteUnitExecution } from "../src/core/execution/DeleteUnitExecution"; +import { SpawnExecution } from "../src/core/execution/SpawnExecution"; import { Game, Player, @@ -6,8 +8,6 @@ import { Unit, UnitType, } from "../src/core/game/Game"; -import { DeleteUnitExecution } from "../src/core/execution/DeleteUnitExecution"; -import { SpawnExecution } from "../src/core/execution/SpawnExecution"; import { TileRef } from "../src/core/game/GameMap"; import { setup } from "./util/Setup"; @@ -20,8 +20,8 @@ describe("DeleteUnitExecution Security Tests", () => { beforeEach(async () => { game = await setup("plains", { infiniteGold: true, - instantBuild: true, infiniteTroops: true, + instantBuild: true, }); const player1Info = new PlayerInfo( diff --git a/tests/Disconnected.test.ts b/tests/Disconnected.test.ts index e2a6012408..e03138efa8 100644 --- a/tests/Disconnected.test.ts +++ b/tests/Disconnected.test.ts @@ -1,8 +1,8 @@ -import { Game, Player, PlayerInfo, PlayerType } from "../src/core/game/Game"; import { MarkDisconnectedExecution } from "../src/core/execution/MarkDisconnectedExecution"; import { SpawnExecution } from "../src/core/execution/SpawnExecution"; -import { executeTicks } from "./util/utils"; +import { Game, Player, PlayerInfo, PlayerType } from "../src/core/game/Game"; import { setup } from "./util/Setup"; +import { executeTicks } from "./util/utils"; let game: Game; let player1: Player; diff --git a/tests/Donate.test.ts b/tests/Donate.test.ts index 2523e7c82c..6634b564db 100644 --- a/tests/Donate.test.ts +++ b/tests/Donate.test.ts @@ -1,14 +1,14 @@ -import { PlayerInfo, PlayerType } from "../src/core/game/Game"; import { DonateGoldExecution } from "../src/core/execution/DonateGoldExecution"; import { DonateTroopsExecution } from "../src/core/execution/DonateTroopExecution"; import { SpawnExecution } from "../src/core/execution/SpawnExecution"; +import { PlayerInfo, PlayerType } from "../src/core/game/Game"; import { setup } from "./util/Setup"; describe("Donate troops to an ally", () => { it("Troops should be successfully donated", async () => { const game = await setup("ocean_and_land", { - infiniteTroops: false, donateTroops: true, + infiniteTroops: false, }); const donorInfo = new PlayerInfo( @@ -70,8 +70,8 @@ describe("Donate troops to an ally", () => { describe("Donate gold to an ally", () => { it("Gold should be successfully donated", async () => { const game = await setup("ocean_and_land", { - infiniteGold: false, donateGold: true, + infiniteGold: false, }); const donorInfo = new PlayerInfo( @@ -134,8 +134,8 @@ describe("Donate gold to an ally", () => { describe("Donate troops to a non ally", () => { it("Troops should not be donated", async () => { const game = await setup("ocean_and_land", { - infiniteTroops: false, donateTroops: true, + infiniteTroops: false, }); const donorInfo = new PlayerInfo( @@ -194,8 +194,8 @@ describe("Donate troops to a non ally", () => { describe("Donate Gold to a non ally", () => { it("Gold should not be donated", async () => { const game = await setup("ocean_and_land", { - infiniteGold: false, donateGold: true, + infiniteGold: false, }); const donorInfo = new PlayerInfo( diff --git a/tests/MissileSilo.test.ts b/tests/MissileSilo.test.ts index 7731b2549a..1459a25430 100644 --- a/tests/MissileSilo.test.ts +++ b/tests/MissileSilo.test.ts @@ -1,3 +1,6 @@ +import { NukeExecution } from "../src/core/execution/NukeExecution"; +import { SpawnExecution } from "../src/core/execution/SpawnExecution"; +import { UpgradeStructureExecution } from "../src/core/execution/UpgradeStructureExecution"; import { Game, Player, @@ -5,12 +8,9 @@ import { PlayerType, UnitType, } from "../src/core/game/Game"; -import { constructionExecution, executeTicks } from "./util/utils"; -import { NukeExecution } from "../src/core/execution/NukeExecution"; -import { SpawnExecution } from "../src/core/execution/SpawnExecution"; import { TileRef } from "../src/core/game/GameMap"; -import { UpgradeStructureExecution } from "../src/core/execution/UpgradeStructureExecution"; import { setup } from "./util/Setup"; +import { constructionExecution, executeTicks } from "./util/utils"; let game: Game; let attacker: Player; diff --git a/tests/ShellRandom.test.ts b/tests/ShellRandom.test.ts index a8546c1744..5c55903833 100644 --- a/tests/ShellRandom.test.ts +++ b/tests/ShellRandom.test.ts @@ -1,3 +1,6 @@ +import { DefensePostExecution } from "../src/core/execution/DefensePostExecution"; +import { ShellExecution } from "../src/core/execution/ShellExecution"; +import { WarshipExecution } from "../src/core/execution/WarshipExecution"; import { Game, Player, @@ -5,9 +8,6 @@ import { PlayerType, UnitType, } from "../src/core/game/Game"; -import { DefensePostExecution } from "../src/core/execution/DefensePostExecution"; -import { ShellExecution } from "../src/core/execution/ShellExecution"; -import { WarshipExecution } from "../src/core/execution/WarshipExecution"; import { setup } from "./util/Setup"; const coastX = 7; diff --git a/tests/TerritoryCapture.test.ts b/tests/TerritoryCapture.test.ts index 5724dea323..e46678c08a 100644 --- a/tests/TerritoryCapture.test.ts +++ b/tests/TerritoryCapture.test.ts @@ -1,5 +1,5 @@ -import { Player, PlayerInfo, PlayerType } from "../src/core/game/Game"; import { SpawnExecution } from "../src/core/execution/SpawnExecution"; +import { Player, PlayerInfo, PlayerType } from "../src/core/game/Game"; import { setup } from "./util/Setup"; describe("Territory management", () => { diff --git a/tests/Warship.test.ts b/tests/Warship.test.ts index fbfc26e80d..b883b65871 100644 --- a/tests/Warship.test.ts +++ b/tests/Warship.test.ts @@ -1,3 +1,5 @@ +import { MoveWarshipExecution } from "../src/core/execution/MoveWarshipExecution"; +import { WarshipExecution } from "../src/core/execution/WarshipExecution"; import { Game, Player, @@ -5,10 +7,8 @@ import { PlayerType, UnitType, } from "../src/core/game/Game"; -import { MoveWarshipExecution } from "../src/core/execution/MoveWarshipExecution"; -import { WarshipExecution } from "../src/core/execution/WarshipExecution"; -import { executeTicks } from "./util/utils"; import { setup } from "./util/Setup"; +import { executeTicks } from "./util/utils"; const coastX = 7; let game: Game; diff --git a/tests/client/graphics/RadialMenuElements.test.ts b/tests/client/graphics/RadialMenuElements.test.ts index 29d6b893dc..2fc297d33f 100644 --- a/tests/client/graphics/RadialMenuElements.test.ts +++ b/tests/client/graphics/RadialMenuElements.test.ts @@ -2,20 +2,20 @@ * @jest-environment jsdom */ import { - COLORS, - MenuElementParams, - Slot, attackMenuElement, buildMenuElement, + COLORS, + MenuElementParams, rootMenuElement, + Slot, } from "../../../src/client/graphics/layers/RadialMenuElements"; -import { GameView, PlayerView } from "../../../src/core/game/GameView"; -import { TileRef } from "../../../src/core/game/GameMap"; import { UnitType } from "../../../src/core/game/Game"; +import { TileRef } from "../../../src/core/game/GameMap"; +import { GameView, PlayerView } from "../../../src/core/game/GameView"; jest.mock("../../../src/client/Utils", () => ({ - translateText: jest.fn((key: string) => key), renderNumber: jest.fn((num: number) => num.toString()), + translateText: jest.fn((key: string) => key), })); jest.mock("../../../src/client/graphics/layers/BuildMenu", () => { @@ -23,46 +23,46 @@ jest.mock("../../../src/client/graphics/layers/BuildMenu", () => { return { flattenedBuildTable: [ { - unitType: UnitType.City, - key: "unit_type.city", + countable: true, description: "unit_type.city_desc", icon: "city-icon", - countable: true, + key: "unit_type.city", + unitType: UnitType.City, }, { - unitType: UnitType.Factory, - key: "unit_type.factory", + countable: true, description: "unit_type.factory_desc", icon: "factory-icon", - countable: true, + key: "unit_type.factory", + unitType: UnitType.Factory, }, { - unitType: UnitType.AtomBomb, - key: "unit_type.atom_bomb", + countable: false, description: "unit_type.atom_bomb_desc", icon: "atom-bomb-icon", - countable: false, + key: "unit_type.atom_bomb", + unitType: UnitType.AtomBomb, }, { - unitType: UnitType.Warship, - key: "unit_type.warship", + countable: true, description: "unit_type.warship_desc", icon: "warship-icon", - countable: true, + key: "unit_type.warship", + unitType: UnitType.Warship, }, { - unitType: UnitType.HydrogenBomb, - key: "unit_type.hydrogen_bomb", + countable: false, description: "unit_type.hydrogen_bomb_desc", icon: "hydrogen-bomb-icon", - countable: false, + key: "unit_type.hydrogen_bomb", + unitType: UnitType.HydrogenBomb, }, { - unitType: UnitType.MIRV, - key: "unit_type.mirv", + countable: false, description: "unit_type.mirv_desc", icon: "mirv-icon", - countable: false, + key: "unit_type.mirv", + unitType: UnitType.MIRV, }, ], }; @@ -95,17 +95,17 @@ describe("RadialMenuElements", () => { } as unknown as PlayerView; mockGame = { - inSpawnPhase: jest.fn(() => false), - owner: jest.fn(() => mockPlayer), - isLand: jest.fn(() => true), config: jest.fn(() => ({ + isUnitDisabled: jest.fn(() => false), theme: () => ({ territoryColor: () => ({ lighten: () => ({ alpha: () => ({ toRgbString: () => "#fff" }) }), }), }), - isUnitDisabled: jest.fn(() => false), })), + inSpawnPhase: jest.fn(() => false), + isLand: jest.fn(() => true), + owner: jest.fn(() => mockPlayer), } as unknown as GameView; mockBuildMenu = { @@ -117,38 +117,38 @@ describe("RadialMenuElements", () => { mockPlayerActions = { buildableUnits: [ - { type: UnitType.City, canBuild: true }, - { type: UnitType.Factory, canBuild: true }, - { type: UnitType.AtomBomb, canBuild: true }, - { type: UnitType.Warship, canBuild: true }, - { type: UnitType.HydrogenBomb, canBuild: true }, - { type: UnitType.MIRV, canBuild: true }, - { type: UnitType.TransportShip, canBuild: true }, + { canBuild: true, type: UnitType.City }, + { canBuild: true, type: UnitType.Factory }, + { canBuild: true, type: UnitType.AtomBomb }, + { canBuild: true, type: UnitType.Warship }, + { canBuild: true, type: UnitType.HydrogenBomb }, + { canBuild: true, type: UnitType.MIRV }, + { canBuild: true, type: UnitType.TransportShip }, ], canAttack: true, interaction: { - canSendAllianceRequest: true, canBreakAlliance: false, - canDonateTroops: true, canDonateGold: true, + canDonateTroops: true, + canSendAllianceRequest: true, }, }; mockTile = {} as TileRef; mockParams = { - myPlayer: mockPlayer, - selected: mockPlayer, - tile: mockTile, - playerActions: mockPlayerActions, - game: mockGame, buildMenu: mockBuildMenu, + chatIntegration: {} as any, + closeMenu: jest.fn(), emojiTable: {} as any, + eventBus: {} as any, + game: mockGame, + myPlayer: mockPlayer, playerActionHandler: {} as any, + playerActions: mockPlayerActions, playerPanel: {} as any, - chatIntegration: {} as any, - eventBus: {} as any, - closeMenu: jest.fn(), + selected: mockPlayer, + tile: mockTile, }; }); diff --git a/tests/client/graphics/UILayer.test.ts b/tests/client/graphics/UILayer.test.ts index 6a25048a52..81b5ec98ce 100644 --- a/tests/client/graphics/UILayer.test.ts +++ b/tests/client/graphics/UILayer.test.ts @@ -11,8 +11,6 @@ describe("UILayer", () => { beforeEach(() => { game = { - width: () => 100, - height: () => 100, config: () => ({ theme: () => ({ territoryColor: () => ({ @@ -20,12 +18,14 @@ describe("UILayer", () => { }), }), }), - x: () => 10, - y: () => 10, - unitInfo: () => ({ maxHealth: 10, constructionDuration: 5 }), + height: () => 100, myPlayer: () => ({ id: () => 1 }), ticks: () => 1, + unitInfo: () => ({ constructionDuration: 5, maxHealth: 10 }), updatesSinceLastTick: () => undefined, + width: () => 100, + x: () => 10, + y: () => 10, }; eventBus = { on: jest.fn() }; }); @@ -46,10 +46,10 @@ describe("UILayer", () => { const ui = new UILayer(game, eventBus); ui.redraw(); const unit = { - type: () => "Warship", isActive: () => true, - tile: () => ({}), owner: () => ({}), + tile: () => ({}), + type: () => "Warship", }; const event = { isSelected: true, unit }; ui.drawSelectionBox = jest.fn(); @@ -61,13 +61,13 @@ describe("UILayer", () => { const ui = new UILayer(game, eventBus); ui.redraw(); const unit = { - id: () => 1, - type: () => "Warship", + createdAt: () => 1, health: () => 5, - tile: () => ({}), - owner: () => ({}), + id: () => 1, isActive: () => true, - createdAt: () => 1, + owner: () => ({}), + tile: () => ({}), + type: () => "Warship", } as unknown as UnitView; ui.drawHealthBar(unit); expect(ui["allHealthBars"].has(1)).toBe(true); @@ -90,12 +90,12 @@ describe("UILayer", () => { const ui = new UILayer(game, eventBus); ui.redraw(); const unit = { - id: () => 1, - type: () => "Warship", health: () => 5, - tile: () => ({}), - owner: () => ({}), + id: () => 1, isActive: () => true, + owner: () => ({}), + tile: () => ({}), + type: () => "Warship", } as unknown as UnitView; ui.drawHealthBar(unit); expect(ui["allHealthBars"].has(1)).toBe(true); @@ -111,8 +111,8 @@ describe("UILayer", () => { ui.redraw(); const unit = { id: () => 2, - tile: () => ({}), isActive: () => true, + tile: () => ({}), } as unknown as UnitView; ui.createLoadingBar(unit); expect(ui["allProgressBars"].has(2)).toBe(true); @@ -122,12 +122,12 @@ describe("UILayer", () => { const ui = new UILayer(game, eventBus); ui.redraw(); const unit = { - id: () => 2, - type: () => "Construction", constructionType: () => "City", + id: () => 2, + isActive: () => true, owner: () => ({ id: () => 1 }), tile: () => ({}), - isActive: () => true, + type: () => "Construction", } as unknown as UnitView; ui.onUnitEvent(unit); expect(ui["allProgressBars"].has(2)).toBe(true); @@ -142,13 +142,13 @@ describe("UILayer", () => { const ui = new UILayer(game, eventBus); ui.redraw(); const unit = { - id: () => 2, - type: () => "Construction", constructionType: () => "City", + createdAt: () => 1, + id: () => 2, + isActive: () => true, owner: () => ({ id: () => 1 }), tile: () => ({}), - isActive: () => true, - createdAt: () => 1, + type: () => "Construction", } as unknown as UnitView; ui.onUnitEvent(unit); expect(ui["allProgressBars"].has(2)).toBe(true); diff --git a/tests/core/executions/NukeExecution.test.ts b/tests/core/executions/NukeExecution.test.ts index 789fb043c0..5ed577f447 100644 --- a/tests/core/executions/NukeExecution.test.ts +++ b/tests/core/executions/NukeExecution.test.ts @@ -1,3 +1,4 @@ +import { NukeExecution } from "../../../src/core/execution/NukeExecution"; import { Game, Player, @@ -5,10 +6,9 @@ import { PlayerType, UnitType, } from "../../../src/core/game/Game"; -import { NukeExecution } from "../../../src/core/execution/NukeExecution"; +import { setup } from "../../util/Setup"; import { TestConfig } from "../../util/TestConfig"; import { executeTicks } from "../../util/utils"; -import { setup } from "../../util/Setup"; let game: Game; let player: Player; diff --git a/tests/core/executions/SAMLauncherExecution.test.ts b/tests/core/executions/SAMLauncherExecution.test.ts index a98eddf1e4..41822bc985 100644 --- a/tests/core/executions/SAMLauncherExecution.test.ts +++ b/tests/core/executions/SAMLauncherExecution.test.ts @@ -1,3 +1,7 @@ +import { NukeExecution } from "../../../src/core/execution/NukeExecution"; +import { SAMLauncherExecution } from "../../../src/core/execution/SAMLauncherExecution"; +import { SpawnExecution } from "../../../src/core/execution/SpawnExecution"; +import { UpgradeStructureExecution } from "../../../src/core/execution/UpgradeStructureExecution"; import { Game, Player, @@ -5,12 +9,8 @@ import { PlayerType, UnitType, } from "../../../src/core/game/Game"; -import { constructionExecution, executeTicks } from "../../util/utils"; -import { NukeExecution } from "../../../src/core/execution/NukeExecution"; -import { SAMLauncherExecution } from "../../../src/core/execution/SAMLauncherExecution"; -import { SpawnExecution } from "../../../src/core/execution/SpawnExecution"; -import { UpgradeStructureExecution } from "../../../src/core/execution/UpgradeStructureExecution"; import { setup } from "../../util/Setup"; +import { constructionExecution, executeTicks } from "../../util/utils"; let game: Game; let attacker: Player; @@ -86,9 +86,9 @@ describe("SAM", () => { const nuke = attacker.buildUnit(UnitType.AtomBomb, game.ref(1, 1), { targetTile: game.ref(3, 1), trajectory: [ - { tile: game.ref(1, 1), targetable: true }, - { tile: game.ref(2, 1), targetable: true }, - { tile: game.ref(3, 1), targetable: true }, + { targetable: true, tile: game.ref(1, 1) }, + { targetable: true, tile: game.ref(2, 1) }, + { targetable: true, tile: game.ref(3, 1) }, ], }); executeTicks(game, 3); @@ -102,17 +102,17 @@ describe("SAM", () => { attacker.buildUnit(UnitType.AtomBomb, game.ref(2, 1), { targetTile: game.ref(3, 1), trajectory: [ - { tile: game.ref(1, 1), targetable: true }, - { tile: game.ref(2, 1), targetable: true }, - { tile: game.ref(3, 1), targetable: true }, + { targetable: true, tile: game.ref(1, 1) }, + { targetable: true, tile: game.ref(2, 1) }, + { targetable: true, tile: game.ref(3, 1) }, ], }); attacker.buildUnit(UnitType.AtomBomb, game.ref(1, 2), { targetTile: game.ref(1, 3), trajectory: [ - { tile: game.ref(1, 1), targetable: true }, - { tile: game.ref(1, 2), targetable: true }, - { tile: game.ref(1, 3), targetable: true }, + { targetable: true, tile: game.ref(1, 1) }, + { targetable: true, tile: game.ref(1, 2) }, + { targetable: true, tile: game.ref(1, 3) }, ], }); expect(attacker.units(UnitType.AtomBomb)).toHaveLength(2); @@ -130,9 +130,9 @@ describe("SAM", () => { const nuke = attacker.buildUnit(UnitType.AtomBomb, game.ref(1, 1), { targetTile: game.ref(1, 3), trajectory: [ - { tile: game.ref(1, 1), targetable: true }, - { tile: game.ref(2, 1), targetable: true }, - { tile: game.ref(3, 1), targetable: true }, + { targetable: true, tile: game.ref(1, 1) }, + { targetable: true, tile: game.ref(2, 1) }, + { targetable: true, tile: game.ref(3, 1) }, ], }); @@ -158,9 +158,9 @@ describe("SAM", () => { const nuke = attacker.buildUnit(UnitType.AtomBomb, game.ref(1, 1), { targetTile: game.ref(1, 3), trajectory: [ - { tile: game.ref(1, 1), targetable: true }, - { tile: game.ref(1, 2), targetable: true }, - { tile: game.ref(1, 3), targetable: true }, + { targetable: true, tile: game.ref(1, 1) }, + { targetable: true, tile: game.ref(1, 2) }, + { targetable: true, tile: game.ref(1, 3) }, ], }); diff --git a/tests/core/executions/TradeShipExecution.test.ts b/tests/core/executions/TradeShipExecution.test.ts index 43879162dd..c23443dc3e 100644 --- a/tests/core/executions/TradeShipExecution.test.ts +++ b/tests/core/executions/TradeShipExecution.test.ts @@ -1,5 +1,5 @@ -import { Game, Player, Unit } from "../../../src/core/game/Game"; import { TradeShipExecution } from "../../../src/core/execution/TradeShipExecution"; +import { Game, Player, Unit } from "../../../src/core/game/Game"; import { setup } from "../../util/Setup"; describe("TradeShipExecution", () => { @@ -22,66 +22,66 @@ describe("TradeShipExecution", () => { }); game.displayMessage = jest.fn(); origOwner = { - canBuild: jest.fn(() => true), + addGold: jest.fn(), buildUnit: jest.fn((type, spawn, opts) => tradeShip), + canBuild: jest.fn(() => true), + canTrade: jest.fn(() => true), displayName: jest.fn(() => "Origin"), - addGold: jest.fn(), - units: jest.fn(() => [dstPort]), - unitCount: jest.fn(() => 1), id: jest.fn(() => 1), - canTrade: jest.fn(() => true), + unitCount: jest.fn(() => 1), + units: jest.fn(() => [dstPort]), } as any; dstOwner = { - id: jest.fn(() => 2), addGold: jest.fn(), + canTrade: jest.fn(() => true), displayName: jest.fn(() => "Destination"), - units: jest.fn(() => [dstPort]), + id: jest.fn(() => 2), unitCount: jest.fn(() => 1), - canTrade: jest.fn(() => true), + units: jest.fn(() => [dstPort]), } as any; pirate = { - id: jest.fn(() => 3), addGold: jest.fn(), + canTrade: jest.fn(() => true), displayName: jest.fn(() => "Destination"), - units: jest.fn(() => [piratePort]), + id: jest.fn(() => 3), unitCount: jest.fn(() => 1), - canTrade: jest.fn(() => true), + units: jest.fn(() => [piratePort]), } as any; piratePort = { - tile: jest.fn(() => 40011), - owner: jest.fn(() => pirate), isActive: jest.fn(() => true), + owner: jest.fn(() => pirate), + tile: jest.fn(() => 40011), } as any; srcPort = { - tile: jest.fn(() => 20011), - owner: jest.fn(() => origOwner), isActive: jest.fn(() => true), + owner: jest.fn(() => origOwner), + tile: jest.fn(() => 20011), } as any; dstPort = { - tile: jest.fn(() => 30015), // 15x15 - owner: jest.fn(() => dstOwner), isActive: jest.fn(() => true), + owner: jest.fn(() => dstOwner), + tile: jest.fn(() => 30015), // 15x15 } as any; tradeShip = { + delete: jest.fn(), isActive: jest.fn(() => true), - owner: jest.fn(() => origOwner), move: jest.fn(), - setTargetUnit: jest.fn(), + owner: jest.fn(() => origOwner), setSafeFromPirates: jest.fn(), - delete: jest.fn(), + setTargetUnit: jest.fn(), tile: jest.fn(() => 2001), } as any; tradeShipExecution = new TradeShipExecution(origOwner, srcPort, dstPort); tradeShipExecution.init(game, 0); tradeShipExecution["pathFinder"] = { - nextTile: jest.fn(() => ({ type: 0, node: 2001 })), + nextTile: jest.fn(() => ({ node: 2001, type: 0 })), } as any; tradeShipExecution["tradeShip"] = tradeShip; }); @@ -112,7 +112,7 @@ describe("TradeShipExecution", () => { it("should complete trade and award gold", () => { tradeShipExecution["pathFinder"] = { - nextTile: jest.fn(() => ({ type: 2, node: 2001 })), + nextTile: jest.fn(() => ({ node: 2001, type: 2 })), } as any; tradeShipExecution.tick(1); expect(tradeShip.delete).toHaveBeenCalledWith(false); diff --git a/tests/core/game/Cluster.test.ts b/tests/core/game/Cluster.test.ts index 95758f1fc1..1cb36dd19a 100644 --- a/tests/core/game/Cluster.test.ts +++ b/tests/core/game/Cluster.test.ts @@ -2,9 +2,9 @@ import { Cluster, TrainStation } from "../../../src/core/game/TrainStation"; const createMockStation = (id: string): jest.Mocked => { return { + getCluster: jest.fn(() => null), id, setCluster: jest.fn(), - getCluster: jest.fn(() => null), } as any; }; diff --git a/tests/core/game/GameImpl.test.ts b/tests/core/game/GameImpl.test.ts index ae30392326..20f9c253c2 100644 --- a/tests/core/game/GameImpl.test.ts +++ b/tests/core/game/GameImpl.test.ts @@ -1,13 +1,13 @@ +import { AttackExecution } from "../../../src/core/execution/AttackExecution"; +import { AllianceRequestExecution } from "../../../src/core/execution/alliance/AllianceRequestExecution"; +import { AllianceRequestReplyExecution } from "../../../src/core/execution/alliance/AllianceRequestReplyExecution"; +import { SpawnExecution } from "../../../src/core/execution/SpawnExecution"; import { Game, Player, PlayerInfo, PlayerType, } from "../../../src/core/game/Game"; -import { AllianceRequestExecution } from "../../../src/core/execution/alliance/AllianceRequestExecution"; -import { AllianceRequestReplyExecution } from "../../../src/core/execution/alliance/AllianceRequestReplyExecution"; -import { AttackExecution } from "../../../src/core/execution/AttackExecution"; -import { SpawnExecution } from "../../../src/core/execution/SpawnExecution"; import { TileRef } from "../../../src/core/game/GameMap"; import { setup } from "../../util/Setup"; @@ -21,8 +21,8 @@ describe("GameImpl", () => { beforeEach(async () => { game = await setup("ocean_and_land", { infiniteGold: true, - instantBuild: true, infiniteTroops: true, + instantBuild: true, }); const attackerInfo = new PlayerInfo( "attacker dude", diff --git a/tests/core/game/RailNetwork.test.ts b/tests/core/game/RailNetwork.test.ts index 9cd724f697..a253559652 100644 --- a/tests/core/game/RailNetwork.test.ts +++ b/tests/core/game/RailNetwork.test.ts @@ -1,24 +1,27 @@ -import { RailNetworkImpl, StationManagerImpl } from "../../../src/core/game/RailNetworkImpl"; -import { Cluster } from "../../../src/core/game/TrainStation"; -import { Railroad } from "../../../src/core/game/Railroad"; import { Unit } from "../../../src/core/game/Game"; +import { + RailNetworkImpl, + StationManagerImpl, +} from "../../../src/core/game/RailNetworkImpl"; +import { Railroad } from "../../../src/core/game/Railroad"; +import { Cluster } from "../../../src/core/game/TrainStation"; // Mock types const createMockStation = (unitId: number): any => { const cluster = new Cluster(); const railroads = new Set(); return { + addRailroad: jest.fn(), + clearRailroads: jest.fn(), + getCluster: jest.fn(() => cluster), + getRailroads: jest.fn(() => railroads), + neighbors: jest.fn(() => []), + setCluster: jest.fn(), + tile: jest.fn(), unit: { id: unitId, setTrainStation: jest.fn(), }, - tile: jest.fn(), - neighbors: jest.fn(() => []), - getCluster: jest.fn(() => cluster), - setCluster: jest.fn(), - addRailroad: jest.fn(), - getRailroads: jest.fn(() => railroads), - clearRailroads: jest.fn(), }; }; @@ -52,22 +55,22 @@ describe("RailNetworkImpl", () => { beforeEach(() => { stationManager = { addStation: jest.fn(), - removeStation: jest.fn(), findStation: jest.fn(), getAll: jest.fn(() => new Set()), + removeStation: jest.fn(), }; pathService = { - findTilePath: jest.fn(() => [0]), findStationsPath: jest.fn(() => [0]), + findTilePath: jest.fn(() => [0]), }; game = { - nearbyUnits: jest.fn(() => []), addExecution: jest.fn(), config: () => ({ + railroadMaxSize: () => 100, trainStationMaxRange: () => 80, trainStationMinRange: () => 10, - railroadMaxSize: () => 100, }), + nearbyUnits: jest.fn(() => []), }; network = new RailNetworkImpl(game, stationManager, pathService); @@ -150,7 +153,7 @@ describe("RailNetworkImpl", () => { neighborStation.getCluster = jest.fn(() => cluster); cluster.has = jest.fn(() => false); - const neighborUnit = { unit: neighborStation.unit, distSquared: 20 }; + const neighborUnit = { distSquared: 20, unit: neighborStation.unit }; game.nearbyUnits.mockReturnValue([neighborUnit]); stationManager.findStation.mockReturnValue(neighborStation); diff --git a/tests/core/game/TrainStation.test.ts b/tests/core/game/TrainStation.test.ts index 738abf3f07..549d7e2f2b 100644 --- a/tests/core/game/TrainStation.test.ts +++ b/tests/core/game/TrainStation.test.ts @@ -1,6 +1,6 @@ -import { Cluster, TrainStation } from "../../../src/core/game/TrainStation"; -import { Game, Player, Unit, UnitType } from "../../../src/core/game/Game"; import { TrainExecution } from "../../../src/core/execution/TrainExecution"; +import { Game, Player, Unit, UnitType } from "../../../src/core/game/Game"; +import { Cluster, TrainStation } from "../../../src/core/game/TrainStation"; jest.mock("../../../src/core/game/Game"); jest.mock("../../../src/core/execution/TrainExecution"); @@ -14,34 +14,34 @@ describe("TrainStation", () => { beforeEach(() => { game = { - ticks: jest.fn().mockReturnValue(123), + addExecution: jest.fn(), + addUpdate: jest.fn(), config: jest.fn().mockReturnValue({ trainGold: (isFriendly: boolean) => isFriendly ? BigInt(1000) : BigInt(500), }), - addUpdate: jest.fn(), - addExecution: jest.fn(), + ticks: jest.fn().mockReturnValue(123), } as any; player = { addGold: jest.fn(), - id: 1, canTrade: jest.fn().mockReturnValue(true), + id: 1, isFriendly: jest.fn().mockReturnValue(false), } as any; unit = { - owner: jest.fn().mockReturnValue(player), + isActive: jest.fn().mockReturnValue(true), level: jest.fn().mockReturnValue(1), + owner: jest.fn().mockReturnValue(player), tile: jest.fn().mockReturnValue({ x: 0, y: 0 }), type: jest.fn(), - isActive: jest.fn().mockReturnValue(true), } as any; trainExecution = { + level: jest.fn(), loadCargo: jest.fn(), owner: jest.fn().mockReturnValue(player), - level: jest.fn(), } as any; }); @@ -82,7 +82,7 @@ describe("TrainStation", () => { it("adds and retrieves neighbors", () => { const stationA = new TrainStation(game, unit); const stationB = new TrainStation(game, unit); - const railRoad = { from: stationA, to: stationB, tiles: [] } as any; + const railRoad = { from: stationA, tiles: [], to: stationB } as any; stationA.addRailroad(railRoad); @@ -96,8 +96,8 @@ describe("TrainStation", () => { const railRoad = { from: stationA, - to: stationB, tiles: [{ x: 1, y: 1 }], + to: stationB, } as any; stationA.addRailroad(railRoad); diff --git a/tests/perf/AstarPerf.ts b/tests/perf/AstarPerf.ts index 457399ac1f..b6a8a92dc5 100644 --- a/tests/perf/AstarPerf.ts +++ b/tests/perf/AstarPerf.ts @@ -1,7 +1,7 @@ import Benchmark from "benchmark"; -import { PathFinder } from "../../src/core/pathfinding/PathFinding"; import { dirname } from "path"; import { fileURLToPath } from "url"; +import { PathFinder } from "../../src/core/pathfinding/PathFinding"; import { setup } from "../util/Setup"; const game = await setup( diff --git a/tests/server/Privilege.customFlag.test.ts b/tests/server/Privilege.customFlag.test.ts index b314b499fa..ce5908fa2b 100644 --- a/tests/server/Privilege.customFlag.test.ts +++ b/tests/server/Privilege.customFlag.test.ts @@ -7,22 +7,22 @@ describe("PrivilegeChecker.isCustomFlagAllowed (with mock cosmetics)", () => { }; const mockCosmetics: Cosmetics = { - patterns: {}, flag: { + color: { + a: { color: "#ff0000", flares: ["cosmetic:red"], name: "red" }, + b: { color: "#00ff00", name: "green" }, + c: { color: "#0000ff", flares: ["cosmetic:blue"], name: "blue" }, + }, layers: { a: { - name: "chocolate", flares: ["cosmetic:flags"], + name: "chocolate", }, b: { name: "center_hline" }, c: { name: "admin_layer" }, }, - color: { - a: { color: "#ff0000", name: "red", flares: ["cosmetic:red"] }, - b: { color: "#00ff00", name: "green" }, - c: { color: "#0000ff", name: "blue", flares: ["cosmetic:blue"] }, - }, }, + patterns: {}, }; const checker = new PrivilegeCheckerImpl(mockCosmetics, dummyPatternDecoder); diff --git a/tests/util/Setup.ts b/tests/util/Setup.ts index e509048646..c170a2dd31 100644 --- a/tests/util/Setup.ts +++ b/tests/util/Setup.ts @@ -1,3 +1,6 @@ +import fs from "fs"; +import path from "path"; +import { z } from "zod"; import { Difficulty, Game, @@ -7,18 +10,15 @@ import { PlayerInfo, PlayerType, } from "../../src/core/game/Game"; +import { createGame } from "../../src/core/game/GameImpl"; import { - MapManifestSchema, genTerrainFromBin, + MapManifestSchema, } from "../../src/core/game/TerrainMapLoader"; +import { UserSettings } from "../../src/core/game/UserSettings"; import { GameConfig } from "../../src/core/Schemas"; import { TestConfig } from "./TestConfig"; import { TestServerConfig } from "./TestServerConfig"; -import { UserSettings } from "../../src/core/game/UserSettings"; -import { createGame } from "../../src/core/game/GameImpl"; -import fs from "fs"; -import path from "path"; -import { z } from "zod"; export async function setup( mapName: string, diff --git a/tests/util/TestConfig.ts b/tests/util/TestConfig.ts index 3b6bd9373a..69b23f3cdd 100644 --- a/tests/util/TestConfig.ts +++ b/tests/util/TestConfig.ts @@ -1,3 +1,5 @@ +import { NukeMagnitude } from "../../src/core/configuration/Config"; +import { DefaultConfig } from "../../src/core/configuration/DefaultConfig"; import { Game, Player, @@ -5,8 +7,6 @@ import { Tick, UnitType, } from "../../src/core/game/Game"; -import { DefaultConfig } from "../../src/core/configuration/DefaultConfig"; -import { NukeMagnitude } from "../../src/core/configuration/Config"; import { TileRef } from "../../src/core/game/GameMap"; export class TestConfig extends DefaultConfig { diff --git a/tests/util/TestServerConfig.ts b/tests/util/TestServerConfig.ts index 2f0a23ee1e..86a277d65a 100644 --- a/tests/util/TestServerConfig.ts +++ b/tests/util/TestServerConfig.ts @@ -1,7 +1,7 @@ +import { JWK } from "jose"; import { GameEnv, ServerConfig } from "../../src/core/configuration/Config"; -import { GameID } from "../../src/core/Schemas"; import { GameMapType } from "../../src/core/game/Game"; -import { JWK } from "jose"; +import { GameID } from "../../src/core/Schemas"; export class TestServerConfig implements ServerConfig { allowedFlares(): string[] | undefined { diff --git a/tests/util/utils.ts b/tests/util/utils.ts index 3a8f3a2e39..dbdfb3d1dc 100644 --- a/tests/util/utils.ts +++ b/tests/util/utils.ts @@ -3,8 +3,8 @@ // However buildUnit do not create executions (e.g.: WarshipExecution) // If you also need execution use function below. Does not work with things not -import { Game, Player, UnitType } from "../../src/core/game/Game"; import { ConstructionExecution } from "../../src/core/execution/ConstructionExecution"; +import { Game, Player, UnitType } from "../../src/core/game/Game"; // built via UI (e.g.: trade ships) export function constructionExecution( diff --git a/webpack.config.js b/webpack.config.js index a60ae5367b..0820e8cd00 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,9 +1,9 @@ +import { execSync } from "child_process"; import CopyPlugin from "copy-webpack-plugin"; import ESLintPlugin from "eslint-webpack-plugin"; import HtmlWebpackPlugin from "html-webpack-plugin"; -import { execSync } from "child_process"; -import { fileURLToPath } from "url"; import path from "path"; +import { fileURLToPath } from "url"; import webpack from "webpack"; const __filename = fileURLToPath(import.meta.url); @@ -111,13 +111,13 @@ export default async (env, argv) => { // Add optimization for HTML minify: isProduction ? { - collapseWhitespace: true, - removeComments: true, - removeRedundantAttributes: true, - removeScriptTypeAttributes: true, - removeStyleLinkTypeAttributes: true, - useShortDoctype: true, - } + collapseWhitespace: true, + removeComments: true, + removeRedundantAttributes: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true, + useShortDoctype: true, + } : false, }), new webpack.DefinePlugin({ @@ -160,91 +160,91 @@ export default async (env, argv) => { devServer: isProduction ? {} : { - devMiddleware: { writeToDisk: true }, - static: { - directory: path.join(__dirname, "static"), - }, - historyApiFallback: true, - compress: true, - port: 9000, - proxy: [ - // WebSocket proxies - { - context: ["/socket"], - target: "ws://localhost:3000", - ws: true, - changeOrigin: true, - logLevel: "debug", - }, - // Worker WebSocket proxies - using direct paths without /socket suffix - { - context: ["/w0"], - target: "ws://localhost:3001", - ws: true, - secure: false, - changeOrigin: true, - logLevel: "debug", - }, - { - context: ["/w1"], - target: "ws://localhost:3002", - ws: true, - secure: false, - changeOrigin: true, - logLevel: "debug", - }, - { - context: ["/w2"], - target: "ws://localhost:3003", - ws: true, - secure: false, - changeOrigin: true, - logLevel: "debug", - }, - // Worker proxies for HTTP requests - { - context: ["/w0"], - target: "http://localhost:3001", - pathRewrite: { "^/w0": "" }, - secure: false, - changeOrigin: true, - logLevel: "debug", - }, - { - context: ["/w1"], - target: "http://localhost:3002", - pathRewrite: { "^/w1": "" }, - secure: false, - changeOrigin: true, - logLevel: "debug", - }, - { - context: ["/w2"], - target: "http://localhost:3003", - pathRewrite: { "^/w2": "" }, - secure: false, - changeOrigin: true, - logLevel: "debug", - }, - // Original API endpoints - { - context: [ - "/api/env", - "/api/game", - "/api/public_lobbies", - "/api/join_game", - "/api/start_game", - "/api/create_game", - "/api/archive_singleplayer_game", - "/api/auth/callback", - "/api/auth/discord", - "/api/kick_player", - ], - target: "http://localhost:3000", - secure: false, - changeOrigin: true, + devMiddleware: { writeToDisk: true }, + static: { + directory: path.join(__dirname, "static"), }, - ], - }, + historyApiFallback: true, + compress: true, + port: 9000, + proxy: [ + // WebSocket proxies + { + context: ["/socket"], + target: "ws://localhost:3000", + ws: true, + changeOrigin: true, + logLevel: "debug", + }, + // Worker WebSocket proxies - using direct paths without /socket suffix + { + context: ["/w0"], + target: "ws://localhost:3001", + ws: true, + secure: false, + changeOrigin: true, + logLevel: "debug", + }, + { + context: ["/w1"], + target: "ws://localhost:3002", + ws: true, + secure: false, + changeOrigin: true, + logLevel: "debug", + }, + { + context: ["/w2"], + target: "ws://localhost:3003", + ws: true, + secure: false, + changeOrigin: true, + logLevel: "debug", + }, + // Worker proxies for HTTP requests + { + context: ["/w0"], + target: "http://localhost:3001", + pathRewrite: { "^/w0": "" }, + secure: false, + changeOrigin: true, + logLevel: "debug", + }, + { + context: ["/w1"], + target: "http://localhost:3002", + pathRewrite: { "^/w1": "" }, + secure: false, + changeOrigin: true, + logLevel: "debug", + }, + { + context: ["/w2"], + target: "http://localhost:3003", + pathRewrite: { "^/w2": "" }, + secure: false, + changeOrigin: true, + logLevel: "debug", + }, + // Original API endpoints + { + context: [ + "/api/env", + "/api/game", + "/api/public_lobbies", + "/api/join_game", + "/api/start_game", + "/api/create_game", + "/api/archive_singleplayer_game", + "/api/auth/callback", + "/api/auth/discord", + "/api/kick_player", + ], + target: "http://localhost:3000", + secure: false, + changeOrigin: true, + }, + ], + }, }; };