mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 06:20:44 +00:00
build: migrate build system to Vite and test runner to Vitest & Remove depracated husky usage (#2703)
- Replace Webpack with Vite for faster client bundling and HMR. - Migrate tests from Jest to Vitest and update configuration. - Update Web Worker instantiation to standard ESM syntax. - Implement Env utility in `src/core` for safe, hybrid environment variable access (Vite vs Node). - Refactor configuration loaders to remove direct `process.env` dependencies in shared code. - Update TypeScript environment definitions and project scripts for the new toolchain. - Remove the [depracated usage of the husky](https://github.com/typicode/husky/releases/tag/v9.0.1). ## Description: migrate build system to Vite and test runner to Vitest & Remove depracated husky usage ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [ ] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: wraith4081 --------- Co-authored-by: evanpelle <evanpelle@gmail.com>
This commit is contained in:
@@ -11,3 +11,5 @@ resources/.DS_Store
|
|||||||
.clinic/
|
.clinic/
|
||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
.idea/
|
.idea/
|
||||||
|
# this is autogenerated by script
|
||||||
|
src/assets/
|
||||||
|
|||||||
Executable → Regular
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
. "$(dirname -- "$0")/_/husky.sh"
|
# Deprecated with husky v9
|
||||||
|
#. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
# Add PATH setup to ensure npx is found
|
# 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"
|
export PATH="/usr/local/bin:$HOME/.npm-global/bin:$HOME/.nvm/versions/node/$(node -v)/bin:$PATH"
|
||||||
|
|||||||
+2
-3
@@ -12,8 +12,7 @@ RUN --mount=type=cache,target=/root/.npm \
|
|||||||
|
|
||||||
# Copy only what's needed for build
|
# Copy only what's needed for build
|
||||||
COPY tsconfig.json ./
|
COPY tsconfig.json ./
|
||||||
COPY tsconfig.jest.json ./
|
COPY vite.config.ts ./
|
||||||
COPY webpack.config.js ./
|
|
||||||
COPY tailwind.config.js ./
|
COPY tailwind.config.js ./
|
||||||
COPY postcss.config.js ./
|
COPY postcss.config.js ./
|
||||||
COPY eslint.config.js ./
|
COPY eslint.config.js ./
|
||||||
@@ -93,4 +92,4 @@ ENV CF_CONFIG_PATH=/etc/cloudflared/config.yml
|
|||||||
ENV CF_CREDS_PATH=/etc/cloudflared/creds.json
|
ENV CF_CREDS_PATH=/etc/cloudflared/creds.json
|
||||||
|
|
||||||
# Use the startup script as the entrypoint
|
# Use the startup script as the entrypoint
|
||||||
ENTRYPOINT ["/usr/local/bin/startup.sh"]
|
ENTRYPOINT ["/usr/local/bin/startup.sh"]
|
||||||
|
|||||||
+1
-2
@@ -26,10 +26,9 @@ export default [
|
|||||||
allowDefaultProject: [
|
allowDefaultProject: [
|
||||||
"__mocks__/fileMock.js",
|
"__mocks__/fileMock.js",
|
||||||
"eslint.config.js",
|
"eslint.config.js",
|
||||||
"jest.config.ts",
|
|
||||||
"postcss.config.js",
|
"postcss.config.js",
|
||||||
"tailwind.config.js",
|
"tailwind.config.js",
|
||||||
"webpack.config.js",
|
"scripts/sync-assets.mjs",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
tsconfigRootDir: import.meta.dirname,
|
tsconfigRootDir: import.meta.dirname,
|
||||||
|
|||||||
@@ -7,12 +7,8 @@
|
|||||||
content="width=device-width, initial-scale=1.0, user-scalable=no"
|
content="width=device-width, initial-scale=1.0, user-scalable=no"
|
||||||
/>
|
/>
|
||||||
<title>OpenFront (ALPHA)</title>
|
<title>OpenFront (ALPHA)</title>
|
||||||
<link rel="manifest" href="../../resources/manifest.json" />
|
<link rel="manifest" href="/manifest.json" />
|
||||||
<link
|
<link rel="icon" type="image/x-icon" href="/images/Favicon.svg" />
|
||||||
rel="icon"
|
|
||||||
type="image/x-icon"
|
|
||||||
href="../../resources/images/Favicon.svg"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- SEO -->
|
<!-- SEO -->
|
||||||
<link rel="canonical" href="https://openfront.io/" />
|
<link rel="canonical" href="https://openfront.io/" />
|
||||||
@@ -119,6 +115,7 @@
|
|||||||
from {
|
from {
|
||||||
transform: translateY(-100%) rotate(0deg); /* Start off-screen */
|
transform: translateY(-100%) rotate(0deg); /* Start off-screen */
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
transform: translateY(105vh) rotate(360deg); /* Fall completely out of view */
|
transform: translateY(105vh) rotate(360deg); /* Fall completely out of view */
|
||||||
}
|
}
|
||||||
@@ -338,7 +335,7 @@
|
|||||||
style="width: 80px; height: 80px; background-color: var(--primaryColor)"
|
style="width: 80px; height: 80px; background-color: var(--primaryColor)"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="../../resources/images/SettingIconWhite.svg"
|
src="/images/SettingIconWhite.svg"
|
||||||
alt="Settings"
|
alt="Settings"
|
||||||
style="width: 72px; height: 72px"
|
style="width: 72px; height: 72px"
|
||||||
/>
|
/>
|
||||||
@@ -416,7 +413,7 @@
|
|||||||
>
|
>
|
||||||
© OpenFront™ and Contributors
|
© OpenFront™ and Contributors
|
||||||
<img
|
<img
|
||||||
src="../../resources/icons/github-mark-white.svg"
|
src="/icons/github-mark-white.svg"
|
||||||
alt="GitHub"
|
alt="GitHub"
|
||||||
width="20"
|
width="20"
|
||||||
height="20"
|
height="20"
|
||||||
@@ -511,5 +508,6 @@
|
|||||||
src="https://static.cloudflareinsights.com/beacon.min.js"
|
src="https://static.cloudflareinsights.com/beacon.min.js"
|
||||||
data-cf-beacon='{"token": "03d93e6fefb349c28ee69b408fa25a13"}'
|
data-cf-beacon='{"token": "03d93e6fefb349c28ee69b408fa25a13"}'
|
||||||
></script>
|
></script>
|
||||||
|
<script type="module" src="/src/client/Main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
export default {
|
|
||||||
testEnvironment: "node",
|
|
||||||
testRegex: "/tests/.*\\.(test|spec)?\\.(ts|tsx)$",
|
|
||||||
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
|
|
||||||
extensionsToTreatAsEsm: [".ts"],
|
|
||||||
moduleNameMapper: {
|
|
||||||
"^(\\.{1,2}/.*)\\.js$": "$1",
|
|
||||||
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$":
|
|
||||||
"<rootDir>/__mocks__/fileMock.js",
|
|
||||||
"\\.(css|less)$": "<rootDir>/__mocks__/fileMock.js",
|
|
||||||
},
|
|
||||||
transform: {
|
|
||||||
"^.+\\.tsx?$": ["@swc/jest"],
|
|
||||||
"^.+\\.mjs$": ["@swc/jest"],
|
|
||||||
"^.+\\.js$": ["@swc/jest"],
|
|
||||||
},
|
|
||||||
transformIgnorePatterns: [
|
|
||||||
"node_modules/(?!(nanoid|@jsep|fastpriorityqueue|@datastructures-js|jose|lit.*|@lit)/)",
|
|
||||||
],
|
|
||||||
collectCoverageFrom: ["src/**/*.ts", "!src/**/*.d.ts"],
|
|
||||||
coverageThreshold: {
|
|
||||||
global: {
|
|
||||||
statements: 21,
|
|
||||||
branches: 16,
|
|
||||||
lines: 21.0,
|
|
||||||
functions: 20.5,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
coverageReporters: ["text", "lcov", "html"],
|
|
||||||
};
|
|
||||||
Generated
+1897
-9527
File diff suppressed because it is too large
Load Diff
+23
-32
@@ -1,26 +1,33 @@
|
|||||||
{
|
{
|
||||||
"name": "openfront-client",
|
"name": "openfront-client",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build-dev": "webpack --config webpack.config.js --mode development",
|
"build-dev": "concurrently \"tsc --noEmit\" \"vite build --mode development\"",
|
||||||
"build-prod": "webpack --config webpack.config.js --mode production",
|
"build-prod": "concurrently --kill-others-on-fail \"tsc --noEmit\" \"vite build\"",
|
||||||
"start:client": "webpack serve --open --node-env development",
|
"start:client": "vite",
|
||||||
"start:server": "node --loader ts-node/esm --experimental-specifier-resolution=node src/server/Server.ts",
|
"start:server": "tsx src/server/Server.ts",
|
||||||
"start:server-dev": "cross-env GAME_ENV=dev node --loader ts-node/esm --experimental-specifier-resolution=node src/server/Server.ts",
|
"start:server-dev": "cross-env GAME_ENV=dev tsx src/server/Server.ts",
|
||||||
"dev": "cross-env GAME_ENV=dev concurrently \"npm run start:client\" \"npm run start:server-dev\"",
|
"dev": "cross-env GAME_ENV=dev concurrently \"npm run start:client\" \"npm run start:server-dev\"",
|
||||||
"dev:staging": "cross-env GAME_ENV=dev API_DOMAIN=api.openfront.dev concurrently \"npm run start:client\" \"npm run start:server-dev\"",
|
"dev:staging": "cross-env GAME_ENV=dev API_DOMAIN=api.openfront.dev concurrently \"npm run start:client\" \"npm run start:server-dev\"",
|
||||||
"dev:prod": "cross-env GAME_ENV=dev API_DOMAIN=api.openfront.io concurrently \"npm run start:client\" \"npm run start:server-dev\"",
|
"dev:prod": "cross-env GAME_ENV=dev API_DOMAIN=api.openfront.io concurrently \"npm run start:client\" \"npm run start:server-dev\"",
|
||||||
"docs:map-generator": "cd map-generator && go doc -cmd -u -all",
|
"docs:map-generator": "cd map-generator && go doc -cmd -u -all",
|
||||||
"tunnel": "npm run build-prod && npm run start:server",
|
"tunnel": "npm run build-prod && npm run start:server",
|
||||||
"test": "jest",
|
"test": "vitest run",
|
||||||
"perf": "npx tsx tests/perf/*.ts",
|
"perf": "npx tsx tests/perf/*.ts",
|
||||||
"test:coverage": "jest --coverage",
|
"test:coverage": "vitest run --coverage",
|
||||||
"format": "prettier --ignore-unknown --write .",
|
"format": "prettier --ignore-unknown --write .",
|
||||||
"format:map-generator": "cd map-generator && go fmt .",
|
"format:map-generator": "cd map-generator && go fmt .",
|
||||||
"lint": "eslint",
|
"lint": "eslint",
|
||||||
"lint:fix": "eslint --fix",
|
"lint:fix": "eslint --fix",
|
||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
"gen-maps": "cd map-generator && go run . && npm run format",
|
"gen-maps": "cd map-generator && go run . && npm run format",
|
||||||
"inst": "npm ci --ignore-scripts"
|
"inst": "npm ci --ignore-scripts",
|
||||||
|
"sync-assets": "node scripts/sync-assets.mjs",
|
||||||
|
"predev": "npm run sync-assets",
|
||||||
|
"prebuild-dev": "npm run sync-assets",
|
||||||
|
"prebuild-prod": "npm run sync-assets",
|
||||||
|
"prestart:client": "npm run sync-assets",
|
||||||
|
"pretest": "npm run sync-assets",
|
||||||
|
"pretest:coverage": "npm run sync-assets"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"**/*": [
|
"**/*": [
|
||||||
@@ -29,13 +36,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.25.2",
|
|
||||||
"@babel/preset-env": "^7.25.3",
|
|
||||||
"@babel/preset-typescript": "^7.24.7",
|
|
||||||
"@datastructures-js/priority-queue": "^6.3.3",
|
"@datastructures-js/priority-queue": "^6.3.3",
|
||||||
"@eslint/compat": "^1.2.7",
|
"@eslint/compat": "^1.2.7",
|
||||||
"@eslint/js": "^9.21.0",
|
"@eslint/js": "^9.21.0",
|
||||||
"@swc/jest": "^0.2.39",
|
|
||||||
"@types/benchmark": "^2.1.5",
|
"@types/benchmark": "^2.1.5",
|
||||||
"@types/chai": "^4.3.17",
|
"@types/chai": "^4.3.17",
|
||||||
"@types/d3": "^7.4.3",
|
"@types/d3": "^7.4.3",
|
||||||
@@ -43,7 +46,6 @@
|
|||||||
"@types/google-protobuf": "^3.15.12",
|
"@types/google-protobuf": "^3.15.12",
|
||||||
"@types/hammerjs": "^2.0.46",
|
"@types/hammerjs": "^2.0.46",
|
||||||
"@types/howler": "^2.2.12",
|
"@types/howler": "^2.2.12",
|
||||||
"@types/jest": "^30.0.0",
|
|
||||||
"@types/jquery": "^3.5.31",
|
"@types/jquery": "^3.5.31",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/msgpack5": "^3.4.6",
|
"@types/msgpack5": "^3.4.6",
|
||||||
@@ -53,29 +55,21 @@
|
|||||||
"@types/sinon": "^17.0.3",
|
"@types/sinon": "^17.0.3",
|
||||||
"@types/systeminformation": "^3.23.1",
|
"@types/systeminformation": "^3.23.1",
|
||||||
"@types/ws": "^8.5.11",
|
"@types/ws": "^8.5.11",
|
||||||
|
"@vitest/coverage-v8": "^4.0.16",
|
||||||
|
"@vitest/ui": "^4.0.16",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"benchmark": "^2.1.4",
|
"benchmark": "^2.1.4",
|
||||||
"binary-base64-loader": "^1.0.0",
|
|
||||||
"binary-loader": "^0.0.1",
|
|
||||||
"canvas": "^3.1.0",
|
"canvas": "^3.1.0",
|
||||||
"chai": "^5.1.1",
|
"chai": "^5.1.1",
|
||||||
"concurrently": "^8.2.2",
|
"concurrently": "^8.2.2",
|
||||||
"copy-webpack-plugin": "^13.0.0",
|
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"css-loader": "^7.1.2",
|
|
||||||
"d3": "^7.9.0",
|
"d3": "^7.9.0",
|
||||||
"eslint": "^9.21.0",
|
"eslint": "^9.21.0",
|
||||||
"eslint-config-prettier": "^10.1.1",
|
"eslint-config-prettier": "^10.1.1",
|
||||||
"eslint-formatter-gha": "^1.5.2",
|
"eslint-formatter-gha": "^1.5.2",
|
||||||
"eslint-webpack-plugin": "^5.0.0",
|
|
||||||
"file-loader": "^6.2.0",
|
|
||||||
"globals": "^16.0.0",
|
"globals": "^16.0.0",
|
||||||
"html-inline-script-webpack-plugin": "^3.2.1",
|
|
||||||
"html-loader": "^5.1.0",
|
|
||||||
"html-webpack-plugin": "^5.6.3",
|
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"jest": "^30.0.0",
|
"jsdom": "^27.4.0",
|
||||||
"jest-environment-jsdom": "^30.0.0",
|
|
||||||
"lint-staged": "^16.1.2",
|
"lint-staged": "^16.1.2",
|
||||||
"lit": "^3.3.1",
|
"lit": "^3.3.1",
|
||||||
"lit-markdown": "^1.3.2",
|
"lit-markdown": "^1.3.2",
|
||||||
@@ -83,25 +77,22 @@
|
|||||||
"pixi-filters": "^6.1.4",
|
"pixi-filters": "^6.1.4",
|
||||||
"pixi.js": "^8.11.0",
|
"pixi.js": "^8.11.0",
|
||||||
"postcss": "^8.5.1",
|
"postcss": "^8.5.1",
|
||||||
"postcss-loader": "^8.1.1",
|
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"prettier-plugin-organize-imports": "^4.1.0",
|
"prettier-plugin-organize-imports": "^4.1.0",
|
||||||
"prettier-plugin-sh": "^0.17.4",
|
"prettier-plugin-sh": "^0.17.4",
|
||||||
"protobufjs": "^7.5.3",
|
"protobufjs": "^7.5.3",
|
||||||
"raw-loader": "^4.0.2",
|
|
||||||
"sinon": "^21.0.0",
|
"sinon": "^21.0.0",
|
||||||
"sinon-chai": "^4.0.0",
|
"sinon-chai": "^4.0.0",
|
||||||
"style-loader": "^4.0.0",
|
|
||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^3.4.17",
|
||||||
"ts-loader": "^9.5.2",
|
|
||||||
"tsconfig-paths": "^4.2.0",
|
"tsconfig-paths": "^4.2.0",
|
||||||
"tsx": "^4.17.0",
|
"tsx": "^4.17.0",
|
||||||
"typescript": "^5.7.2",
|
"typescript": "^5.7.2",
|
||||||
"typescript-eslint": "^8.26.0",
|
"typescript-eslint": "^8.26.0",
|
||||||
"webpack": "^5.100.2",
|
"vite": "^7.3.0",
|
||||||
"webpack-cli": "^6.0.1",
|
"vite-plugin-html": "^3.2.2",
|
||||||
"webpack-dev-server": "^5.2.2",
|
"vite-plugin-static-copy": "^3.1.4",
|
||||||
"worker-loader": "^3.0.8"
|
"vite-tsconfig-paths": "^6.0.3",
|
||||||
|
"vitest": "^4.0.16"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.758.0",
|
"@aws-sdk/client-s3": "^3.758.0",
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import { promises as fs } from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
const root = path.resolve(__dirname, "..");
|
||||||
|
|
||||||
|
const resourcesDir = path.join(root, "resources");
|
||||||
|
const assetsDir = path.join(root, "src", "assets");
|
||||||
|
const dataDir = path.join(assetsDir, "data");
|
||||||
|
const langDir = path.join(assetsDir, "lang");
|
||||||
|
|
||||||
|
const dataFiles = ["version.txt", "countries.json", "QuickChat.json"];
|
||||||
|
|
||||||
|
async function ensureDir(dir) {
|
||||||
|
await fs.mkdir(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyFile(src, dest) {
|
||||||
|
await ensureDir(path.dirname(dest));
|
||||||
|
await fs.copyFile(src, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyDataFiles() {
|
||||||
|
await Promise.all(
|
||||||
|
dataFiles.map((name) =>
|
||||||
|
copyFile(path.join(resourcesDir, name), path.join(dataDir, name)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyLangFiles() {
|
||||||
|
const sourceDir = path.join(resourcesDir, "lang");
|
||||||
|
const entries = await fs.readdir(sourceDir, { withFileTypes: true });
|
||||||
|
await Promise.all(
|
||||||
|
entries
|
||||||
|
.filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
|
||||||
|
.map((entry) =>
|
||||||
|
copyFile(
|
||||||
|
path.join(sourceDir, entry.name),
|
||||||
|
path.join(langDir, entry.name),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
await ensureDir(dataDir);
|
||||||
|
await ensureDir(langDir);
|
||||||
|
await copyDataFiles();
|
||||||
|
await copyLangFiles();
|
||||||
|
console.log("Synced resources to src/assets.");
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((error) => {
|
||||||
|
console.error("sync-assets failed:", error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { LitElement, html } from "lit";
|
import { LitElement, html } from "lit";
|
||||||
import { customElement, query, state } from "lit/decorators.js";
|
import { customElement, query, state } from "lit/decorators.js";
|
||||||
import Countries from "./data/countries.json";
|
import Countries from "../assets/data/countries.json" with { type: "json" };
|
||||||
import { translateText } from "./Utils";
|
import { translateText } from "./Utils";
|
||||||
|
|
||||||
@customElement("flag-input-modal")
|
@customElement("flag-input-modal")
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { LitElement, html } from "lit";
|
import { LitElement, html } from "lit";
|
||||||
import { customElement, query, state } from "lit/decorators.js";
|
import { customElement, query, state } from "lit/decorators.js";
|
||||||
import randomMap from "../../resources/images/RandomMap.webp";
|
|
||||||
import { translateText } from "../client/Utils";
|
import { translateText } from "../client/Utils";
|
||||||
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
|
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
|
||||||
import {
|
import {
|
||||||
@@ -32,6 +31,7 @@ import { crazyGamesSDK } from "./CrazyGamesSDK";
|
|||||||
import { JoinLobbyEvent } from "./Main";
|
import { JoinLobbyEvent } from "./Main";
|
||||||
import { terrainMapFileLoader } from "./TerrainMapFileLoader";
|
import { terrainMapFileLoader } from "./TerrainMapFileLoader";
|
||||||
import { renderUnitTypeOptions } from "./utilities/RenderUnitTypeOptions";
|
import { renderUnitTypeOptions } from "./utilities/RenderUnitTypeOptions";
|
||||||
|
import randomMap from "/images/RandomMap.webp?url";
|
||||||
@customElement("host-lobby-modal")
|
@customElement("host-lobby-modal")
|
||||||
export class HostLobbyModal extends LitElement {
|
export class HostLobbyModal extends LitElement {
|
||||||
@query("o-modal") private modalEl!: HTMLElement & {
|
@query("o-modal") private modalEl!: HTMLElement & {
|
||||||
|
|||||||
+34
-34
@@ -2,40 +2,40 @@ import { LitElement, html } from "lit";
|
|||||||
import { customElement, state } from "lit/decorators.js";
|
import { customElement, state } from "lit/decorators.js";
|
||||||
import "./LanguageModal";
|
import "./LanguageModal";
|
||||||
|
|
||||||
import ar from "../../resources/lang/ar.json";
|
import ar from "../assets/lang/ar.json";
|
||||||
import bg from "../../resources/lang/bg.json";
|
import bg from "../assets/lang/bg.json";
|
||||||
import bn from "../../resources/lang/bn.json";
|
import bn from "../assets/lang/bn.json";
|
||||||
import cs from "../../resources/lang/cs.json";
|
import cs from "../assets/lang/cs.json";
|
||||||
import da from "../../resources/lang/da.json";
|
import da from "../assets/lang/da.json";
|
||||||
import de from "../../resources/lang/de.json";
|
import de from "../assets/lang/de.json";
|
||||||
import el from "../../resources/lang/el.json";
|
import el from "../assets/lang/el.json";
|
||||||
import en from "../../resources/lang/en.json";
|
import en from "../assets/lang/en.json";
|
||||||
import eo from "../../resources/lang/eo.json";
|
import eo from "../assets/lang/eo.json";
|
||||||
import es from "../../resources/lang/es.json";
|
import es from "../assets/lang/es.json";
|
||||||
import fa from "../../resources/lang/fa.json";
|
import fa from "../assets/lang/fa.json";
|
||||||
import fi from "../../resources/lang/fi.json";
|
import fi from "../assets/lang/fi.json";
|
||||||
import fr from "../../resources/lang/fr.json";
|
import fr from "../assets/lang/fr.json";
|
||||||
import gl from "../../resources/lang/gl.json";
|
import gl from "../assets/lang/gl.json";
|
||||||
import he from "../../resources/lang/he.json";
|
import he from "../assets/lang/he.json";
|
||||||
import hi from "../../resources/lang/hi.json";
|
import hi from "../assets/lang/hi.json";
|
||||||
import hu from "../../resources/lang/hu.json";
|
import hu from "../assets/lang/hu.json";
|
||||||
import it from "../../resources/lang/it.json";
|
import it from "../assets/lang/it.json";
|
||||||
import ja from "../../resources/lang/ja.json";
|
import ja from "../assets/lang/ja.json";
|
||||||
import ko from "../../resources/lang/ko.json";
|
import ko from "../assets/lang/ko.json";
|
||||||
import mk from "../../resources/lang/mk.json";
|
import mk from "../assets/lang/mk.json";
|
||||||
import nl from "../../resources/lang/nl.json";
|
import nl from "../assets/lang/nl.json";
|
||||||
import pl from "../../resources/lang/pl.json";
|
import pl from "../assets/lang/pl.json";
|
||||||
import pt_BR from "../../resources/lang/pt-BR.json";
|
import pt_BR from "../assets/lang/pt-BR.json";
|
||||||
import pt_PT from "../../resources/lang/pt-PT.json";
|
import pt_PT from "../assets/lang/pt-PT.json";
|
||||||
import ru from "../../resources/lang/ru.json";
|
import ru from "../assets/lang/ru.json";
|
||||||
import sh from "../../resources/lang/sh.json";
|
import sh from "../assets/lang/sh.json";
|
||||||
import sk from "../../resources/lang/sk.json";
|
import sk from "../assets/lang/sk.json";
|
||||||
import sl from "../../resources/lang/sl.json";
|
import sl from "../assets/lang/sl.json";
|
||||||
import sv_SE from "../../resources/lang/sv-SE.json";
|
import sv_SE from "../assets/lang/sv-SE.json";
|
||||||
import tp from "../../resources/lang/tp.json";
|
import tp from "../assets/lang/tp.json";
|
||||||
import tr from "../../resources/lang/tr.json";
|
import tr from "../assets/lang/tr.json";
|
||||||
import uk from "../../resources/lang/uk.json";
|
import uk from "../assets/lang/uk.json";
|
||||||
import zh_CN from "../../resources/lang/zh-CN.json";
|
import zh_CN from "../assets/lang/zh-CN.json";
|
||||||
|
|
||||||
@customElement("lang-selector")
|
@customElement("lang-selector")
|
||||||
export class LangSelector extends LitElement {
|
export class LangSelector extends LitElement {
|
||||||
|
|||||||
+12
-3
@@ -1,5 +1,4 @@
|
|||||||
import Snowflake3Png from "../../resources/images/Snowflake.webp";
|
import version from "../assets/data/version.txt?raw";
|
||||||
import version from "../../resources/version.txt";
|
|
||||||
import { UserMeResponse } from "../core/ApiSchemas";
|
import { UserMeResponse } from "../core/ApiSchemas";
|
||||||
import { EventBus } from "../core/EventBus";
|
import { EventBus } from "../core/EventBus";
|
||||||
import { GameRecord, GameStartInfo, ID } from "../core/Schemas";
|
import { GameRecord, GameStartInfo, ID } from "../core/Schemas";
|
||||||
@@ -46,7 +45,17 @@ import "./components/baseComponents/Button";
|
|||||||
import "./components/baseComponents/Modal";
|
import "./components/baseComponents/Modal";
|
||||||
import "./snow.css";
|
import "./snow.css";
|
||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
|
import "./styles/components/button.css";
|
||||||
|
import "./styles/components/controls.css";
|
||||||
|
import "./styles/components/modal.css";
|
||||||
|
import "./styles/components/setting.css";
|
||||||
|
import "./styles/core/flag-animation.css";
|
||||||
|
import "./styles/core/typography.css";
|
||||||
|
import "./styles/core/variables.css";
|
||||||
|
import "./styles/layout/container.css";
|
||||||
|
import "./styles/layout/header.css";
|
||||||
|
import "./styles/modal/chat.css";
|
||||||
|
import Snowflake3Png from "/images/Snowflake.webp?url";
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
turnstile: any;
|
turnstile: any;
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { LitElement, css, html } from "lit";
|
import { LitElement, css, html } from "lit";
|
||||||
import { resolveMarkdown } from "lit-markdown";
|
import { resolveMarkdown } from "lit-markdown";
|
||||||
import { customElement, property, query } from "lit/decorators.js";
|
import { customElement, property, query } from "lit/decorators.js";
|
||||||
import changelog from "../../resources/changelog.md";
|
import version from "../assets/data/version.txt?raw";
|
||||||
import megaphone from "../../resources/images/Megaphone.svg";
|
|
||||||
import santaHatIcon from "../../resources/images/SantaHat.webp";
|
|
||||||
import version from "../../resources/version.txt";
|
|
||||||
import { translateText } from "../client/Utils";
|
import { translateText } from "../client/Utils";
|
||||||
import "./components/baseComponents/Button";
|
import "./components/baseComponents/Button";
|
||||||
import "./components/baseComponents/Modal";
|
import "./components/baseComponents/Modal";
|
||||||
|
import changelog from "/changelog.md?url";
|
||||||
|
import megaphone from "/images/Megaphone.svg?url";
|
||||||
|
import santaHatIcon from "/images/SantaHat.webp?url";
|
||||||
|
|
||||||
@customElement("news-modal")
|
@customElement("news-modal")
|
||||||
export class NewsModal extends LitElement {
|
export class NewsModal extends LitElement {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { LitElement, html } from "lit";
|
import { LitElement, html } from "lit";
|
||||||
import { customElement, query, state } from "lit/decorators.js";
|
import { customElement, query, state } from "lit/decorators.js";
|
||||||
import randomMap from "../../resources/images/RandomMap.webp";
|
|
||||||
import { translateText } from "../client/Utils";
|
import { translateText } from "../client/Utils";
|
||||||
import {
|
import {
|
||||||
Difficulty,
|
Difficulty,
|
||||||
@@ -28,6 +27,7 @@ import { FlagInput } from "./FlagInput";
|
|||||||
import { JoinLobbyEvent } from "./Main";
|
import { JoinLobbyEvent } from "./Main";
|
||||||
import { UsernameInput } from "./UsernameInput";
|
import { UsernameInput } from "./UsernameInput";
|
||||||
import { renderUnitTypeOptions } from "./utilities/RenderUnitTypeOptions";
|
import { renderUnitTypeOptions } from "./utilities/RenderUnitTypeOptions";
|
||||||
|
import randomMap from "/images/RandomMap.webp?url";
|
||||||
|
|
||||||
@customElement("single-player-modal")
|
@customElement("single-player-modal")
|
||||||
export class SinglePlayerModal extends LitElement {
|
export class SinglePlayerModal extends LitElement {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import version from "../../resources/version.txt";
|
import version from "../assets/data/version.txt?raw";
|
||||||
import { FetchGameMapLoader } from "../core/game/FetchGameMapLoader";
|
import { FetchGameMapLoader } from "../core/game/FetchGameMapLoader";
|
||||||
|
|
||||||
export const terrainMapFileLoader = new FetchGameMapLoader(`/maps`, version);
|
export const terrainMapFileLoader = new FetchGameMapLoader(`/maps`, version);
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import miniBigSmoke from "../../../resources/sprites/bigsmoke.png";
|
|
||||||
import buildingExplosion from "../../../resources/sprites/buildingExplosion.png";
|
|
||||||
import happyElf from "../../../resources/sprites/christmas/happy_elf.png";
|
|
||||||
import sadElf from "../../../resources/sprites/christmas/sad_elf.png";
|
|
||||||
import santa from "../../../resources/sprites/christmas/santa.png";
|
|
||||||
import snowman from "../../../resources/sprites/christmas/snowman.png";
|
|
||||||
import sparks from "../../../resources/sprites/christmas/sparks.png";
|
|
||||||
import conquestSword from "../../../resources/sprites/conquestSword.png";
|
|
||||||
import dust from "../../../resources/sprites/dust.png";
|
|
||||||
import miniExplosion from "../../../resources/sprites/miniExplosion.png";
|
|
||||||
import miniFire from "../../../resources/sprites/minifire.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 { Theme } from "../../core/configuration/Config";
|
||||||
import { PlayerView } from "../../core/game/GameView";
|
import { PlayerView } from "../../core/game/GameView";
|
||||||
import { AnimatedSprite } from "./AnimatedSprite";
|
import { AnimatedSprite } from "./AnimatedSprite";
|
||||||
import { FxType } from "./fx/Fx";
|
import { FxType } from "./fx/Fx";
|
||||||
import { colorizeCanvas } from "./SpriteLoader";
|
import { colorizeCanvas } from "./SpriteLoader";
|
||||||
|
import miniBigSmoke from "/sprites/bigsmoke.png?url";
|
||||||
|
import buildingExplosion from "/sprites/buildingExplosion.png?url";
|
||||||
|
import happyElf from "/sprites/christmas/happy_elf.png?url";
|
||||||
|
import sadElf from "/sprites/christmas/sad_elf.png?url";
|
||||||
|
import santa from "/sprites/christmas/santa.png?url";
|
||||||
|
import snowman from "/sprites/christmas/snowman.png?url";
|
||||||
|
import sparks from "/sprites/christmas/sparks.png?url";
|
||||||
|
import conquestSword from "/sprites/conquestSword.png?url";
|
||||||
|
import dust from "/sprites/dust.png?url";
|
||||||
|
import miniExplosion from "/sprites/miniExplosion.png?url";
|
||||||
|
import miniFire from "/sprites/minifire.png?url";
|
||||||
|
import nuke from "/sprites/nukeExplosion.png?url";
|
||||||
|
import SAMExplosion from "/sprites/samExplosion.png?url";
|
||||||
|
import sinkingShip from "/sprites/sinkingShip.png?url";
|
||||||
|
import miniSmoke from "/sprites/smoke.png?url";
|
||||||
|
import miniSmokeAndFire from "/sprites/smokeAndFire.png?url";
|
||||||
|
import unitExplosion from "/sprites/unitExplosion.png?url";
|
||||||
|
|
||||||
type AnimatedSpriteConfig = {
|
type AnimatedSpriteConfig = {
|
||||||
url: string;
|
url: string;
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import allianceIcon from "../../../resources/images/AllianceIcon.svg";
|
|
||||||
import allianceIconFaded from "../../../resources/images/AllianceIconFaded.svg";
|
|
||||||
import allianceRequestBlackIcon from "../../../resources/images/AllianceRequestBlackIcon.svg";
|
|
||||||
import allianceRequestWhiteIcon from "../../../resources/images/AllianceRequestWhiteIcon.svg";
|
|
||||||
import crownIcon from "../../../resources/images/CrownIcon.svg";
|
|
||||||
import disconnectedIcon from "../../../resources/images/DisconnectedIcon.svg";
|
|
||||||
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 questionMarkIcon from "../../../resources/images/QuestionMarkIcon.svg";
|
|
||||||
import targetIcon from "../../../resources/images/TargetIcon.svg";
|
|
||||||
import traitorIcon from "../../../resources/images/TraitorIcon.svg";
|
|
||||||
import { AllPlayers, nukeTypes } from "../../core/game/Game";
|
import { AllPlayers, nukeTypes } from "../../core/game/Game";
|
||||||
import { GameView, PlayerView } from "../../core/game/GameView";
|
import { GameView, PlayerView } from "../../core/game/GameView";
|
||||||
|
import allianceIcon from "/images/AllianceIcon.svg?url";
|
||||||
|
import allianceIconFaded from "/images/AllianceIconFaded.svg?url";
|
||||||
|
import allianceRequestBlackIcon from "/images/AllianceRequestBlackIcon.svg?url";
|
||||||
|
import allianceRequestWhiteIcon from "/images/AllianceRequestWhiteIcon.svg?url";
|
||||||
|
import crownIcon from "/images/CrownIcon.svg?url";
|
||||||
|
import disconnectedIcon from "/images/DisconnectedIcon.svg?url";
|
||||||
|
import embargoBlackIcon from "/images/EmbargoBlackIcon.svg?url";
|
||||||
|
import embargoWhiteIcon from "/images/EmbargoWhiteIcon.svg?url";
|
||||||
|
import nukeRedIcon from "/images/NukeIconRed.svg?url";
|
||||||
|
import nukeWhiteIcon from "/images/NukeIconWhite.svg?url";
|
||||||
|
import questionMarkIcon from "/images/QuestionMarkIcon.svg?url";
|
||||||
|
import targetIcon from "/images/TargetIcon.svg?url";
|
||||||
|
import traitorIcon from "/images/TraitorIcon.svg?url";
|
||||||
|
|
||||||
export type PlayerIconId =
|
export type PlayerIconId =
|
||||||
| "crown"
|
| "crown"
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { Colord } from "colord";
|
import { Colord } from "colord";
|
||||||
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 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 { Theme } from "../../core/configuration/Config";
|
||||||
import { TrainType, UnitType } from "../../core/game/Game";
|
import { TrainType, UnitType } from "../../core/game/Game";
|
||||||
import { UnitView } from "../../core/game/GameView";
|
import { UnitView } from "../../core/game/GameView";
|
||||||
|
import atomBombSprite from "/sprites/atombomb.png?url";
|
||||||
|
import hydrogenBombSprite from "/sprites/hydrogenbomb.png?url";
|
||||||
|
import mirvSprite from "/sprites/mirv2.png?url";
|
||||||
|
import samMissileSprite from "/sprites/samMissile.png?url";
|
||||||
|
import tradeShipSprite from "/sprites/tradeship.png?url";
|
||||||
|
import trainCarriageSprite from "/sprites/trainCarriage.png?url";
|
||||||
|
import trainLoadedCarriageSprite from "/sprites/trainCarriageLoaded.png?url";
|
||||||
|
import trainEngineSprite from "/sprites/trainEngine.png?url";
|
||||||
|
import transportShipSprite from "/sprites/transportship.png?url";
|
||||||
|
import warshipSprite from "/sprites/warship.png?url";
|
||||||
|
|
||||||
// Can't reuse TrainType because "loaded" is not a type, just an attribute
|
// Can't reuse TrainType because "loaded" is not a type, just an attribute
|
||||||
const TrainTypeSprite = {
|
const TrainTypeSprite = {
|
||||||
|
|||||||
@@ -1,16 +1,5 @@
|
|||||||
import { LitElement, css, html } from "lit";
|
import { LitElement, css, html } from "lit";
|
||||||
import { customElement, state } from "lit/decorators.js";
|
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 missileSiloIcon from "../../../../resources/images/MissileSiloIconWhite.svg";
|
|
||||||
import hydrogenBombIcon from "../../../../resources/images/MushroomCloudIconWhite.svg";
|
|
||||||
import atomBombIcon from "../../../../resources/images/NukeIconWhite.svg";
|
|
||||||
import portIcon from "../../../../resources/images/PortIcon.svg";
|
|
||||||
import samlauncherIcon from "../../../../resources/images/SamLauncherIconWhite.svg";
|
|
||||||
import shieldIcon from "../../../../resources/images/ShieldIconWhite.svg";
|
|
||||||
import { translateText } from "../../../client/Utils";
|
import { translateText } from "../../../client/Utils";
|
||||||
import { EventBus } from "../../../core/EventBus";
|
import { EventBus } from "../../../core/EventBus";
|
||||||
import {
|
import {
|
||||||
@@ -34,6 +23,17 @@ import {
|
|||||||
import { renderNumber } from "../../Utils";
|
import { renderNumber } from "../../Utils";
|
||||||
import { TransformHandler } from "../TransformHandler";
|
import { TransformHandler } from "../TransformHandler";
|
||||||
import { Layer } from "./Layer";
|
import { Layer } from "./Layer";
|
||||||
|
import warshipIcon from "/images/BattleshipIconWhite.svg?url";
|
||||||
|
import cityIcon from "/images/CityIconWhite.svg?url";
|
||||||
|
import factoryIcon from "/images/FactoryIconWhite.svg?url";
|
||||||
|
import goldCoinIcon from "/images/GoldCoinIcon.svg?url";
|
||||||
|
import mirvIcon from "/images/MIRVIcon.svg?url";
|
||||||
|
import missileSiloIcon from "/images/MissileSiloIconWhite.svg?url";
|
||||||
|
import hydrogenBombIcon from "/images/MushroomCloudIconWhite.svg?url";
|
||||||
|
import atomBombIcon from "/images/NukeIconWhite.svg?url";
|
||||||
|
import portIcon from "/images/PortIcon.svg?url";
|
||||||
|
import samlauncherIcon from "/images/SamLauncherIconWhite.svg?url";
|
||||||
|
import shieldIcon from "/images/ShieldIconWhite.svg?url";
|
||||||
|
|
||||||
export interface BuildItemDisplay {
|
export interface BuildItemDisplay {
|
||||||
unitType: UnitType;
|
unitType: UnitType;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { customElement, query } from "lit/decorators.js";
|
|||||||
import { PlayerType } from "../../../core/game/Game";
|
import { PlayerType } from "../../../core/game/Game";
|
||||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||||
|
|
||||||
import quickChatData from "../../../../resources/QuickChat.json";
|
import quickChatData from "../../../assets/data/QuickChat.json" with { type: "json" };
|
||||||
import { EventBus } from "../../../core/EventBus";
|
import { EventBus } from "../../../core/EventBus";
|
||||||
import { CloseViewEvent } from "../../InputHandler";
|
import { CloseViewEvent } from "../../InputHandler";
|
||||||
import { SendQuickChatEvent } from "../../Transport";
|
import { SendQuickChatEvent } from "../../Transport";
|
||||||
|
|||||||
@@ -2,11 +2,6 @@ import { html, LitElement } from "lit";
|
|||||||
import { customElement, query, state } from "lit/decorators.js";
|
import { customElement, query, state } from "lit/decorators.js";
|
||||||
import { DirectiveResult } from "lit/directive.js";
|
import { DirectiveResult } from "lit/directive.js";
|
||||||
import { unsafeHTML, UnsafeHTMLDirective } from "lit/directives/unsafe-html.js";
|
import { unsafeHTML, UnsafeHTMLDirective } 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 nukeIcon from "../../../../resources/images/NukeIconWhite.svg";
|
|
||||||
import swordIcon from "../../../../resources/images/SwordIconWhite.svg";
|
|
||||||
import { EventBus } from "../../../core/EventBus";
|
import { EventBus } from "../../../core/EventBus";
|
||||||
import {
|
import {
|
||||||
AllPlayers,
|
AllPlayers,
|
||||||
@@ -50,6 +45,11 @@ import {
|
|||||||
|
|
||||||
import { getMessageTypeClasses, translateText } from "../../Utils";
|
import { getMessageTypeClasses, translateText } from "../../Utils";
|
||||||
import { UIState } from "../UIState";
|
import { UIState } from "../UIState";
|
||||||
|
import allianceIcon from "/images/AllianceIconWhite.svg?url";
|
||||||
|
import chatIcon from "/images/ChatIconWhite.svg?url";
|
||||||
|
import donateGoldIcon from "/images/DonateGoldIconWhite.svg?url";
|
||||||
|
import nukeIcon from "/images/NukeIconWhite.svg?url";
|
||||||
|
import swordIcon from "/images/SwordIconWhite.svg?url";
|
||||||
|
|
||||||
interface GameEvent {
|
interface GameEvent {
|
||||||
description: string;
|
description: string;
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { Colord } from "colord";
|
import { Colord } from "colord";
|
||||||
import { html, LitElement } from "lit";
|
import { html, LitElement } from "lit";
|
||||||
import { customElement, state } from "lit/decorators.js";
|
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 { GameMode } from "../../../core/game/Game";
|
||||||
import { GameView } from "../../../core/game/GameView";
|
import { GameView } from "../../../core/game/GameView";
|
||||||
import { translateText } from "../../Utils";
|
import { translateText } from "../../Utils";
|
||||||
import { Layer } from "./Layer";
|
import { Layer } from "./Layer";
|
||||||
|
import leaderboardRegularIcon from "/images/LeaderboardIconRegularWhite.svg?url";
|
||||||
|
import leaderboardSolidIcon from "/images/LeaderboardIconSolidWhite.svg?url";
|
||||||
|
import teamRegularIcon from "/images/TeamIconRegularWhite.svg?url";
|
||||||
|
import teamSolidIcon from "/images/TeamIconSolidWhite.svg?url";
|
||||||
|
|
||||||
@customElement("game-left-sidebar")
|
@customElement("game-left-sidebar")
|
||||||
export class GameLeftSidebar extends LitElement implements Layer {
|
export class GameLeftSidebar extends LitElement implements Layer {
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
import { html, LitElement } from "lit";
|
import { html, LitElement } from "lit";
|
||||||
import { customElement, state } from "lit/decorators.js";
|
import { customElement, state } from "lit/decorators.js";
|
||||||
import exitIcon from "../../../../resources/images/ExitIconWhite.svg";
|
|
||||||
import FastForwardIconSolid from "../../../../resources/images/FastForwardIconSolidWhite.svg";
|
|
||||||
import pauseIcon from "../../../../resources/images/PauseIconWhite.svg";
|
|
||||||
import playIcon from "../../../../resources/images/PlayIconWhite.svg";
|
|
||||||
import settingsIcon from "../../../../resources/images/SettingIconWhite.svg";
|
|
||||||
import { EventBus } from "../../../core/EventBus";
|
import { EventBus } from "../../../core/EventBus";
|
||||||
import { GameType } from "../../../core/game/Game";
|
import { GameType } from "../../../core/game/Game";
|
||||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||||
@@ -15,6 +10,11 @@ import { translateText } from "../../Utils";
|
|||||||
import { Layer } from "./Layer";
|
import { Layer } from "./Layer";
|
||||||
import { ShowReplayPanelEvent } from "./ReplayPanel";
|
import { ShowReplayPanelEvent } from "./ReplayPanel";
|
||||||
import { ShowSettingsModalEvent } from "./SettingsModal";
|
import { ShowSettingsModalEvent } from "./SettingsModal";
|
||||||
|
import exitIcon from "/images/ExitIconWhite.svg?url";
|
||||||
|
import FastForwardIconSolid from "/images/FastForwardIconSolidWhite.svg?url";
|
||||||
|
import pauseIcon from "/images/PauseIconWhite.svg?url";
|
||||||
|
import playIcon from "/images/PlayIconWhite.svg?url";
|
||||||
|
import settingsIcon from "/images/SettingIconWhite.svg?url";
|
||||||
|
|
||||||
@customElement("game-right-sidebar")
|
@customElement("game-right-sidebar")
|
||||||
export class GameRightSidebar extends LitElement implements Layer {
|
export class GameRightSidebar extends LitElement implements Layer {
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ import {
|
|||||||
MenuElementParams,
|
MenuElementParams,
|
||||||
rootMenuElement,
|
rootMenuElement,
|
||||||
} from "./RadialMenuElements";
|
} from "./RadialMenuElements";
|
||||||
|
import donateTroopIcon from "/images/DonateTroopIconWhite.svg?url";
|
||||||
|
import swordIcon from "/images/SwordIconWhite.svg?url";
|
||||||
|
|
||||||
import donateTroopIcon from "../../../../resources/images/DonateTroopIconWhite.svg";
|
|
||||||
import swordIcon from "../../../../resources/images/SwordIconWhite.svg";
|
|
||||||
import { ContextMenuEvent } from "../../InputHandler";
|
import { ContextMenuEvent } from "../../InputHandler";
|
||||||
|
|
||||||
@customElement("main-radial-menu")
|
@customElement("main-radial-menu")
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import shieldIcon from "../../../../resources/images/ShieldIconBlack.svg";
|
|
||||||
import { renderPlayerFlag } from "../../../core/CustomFlag";
|
import { renderPlayerFlag } from "../../../core/CustomFlag";
|
||||||
import { EventBus } from "../../../core/EventBus";
|
import { EventBus } from "../../../core/EventBus";
|
||||||
import { PseudoRandom } from "../../../core/PseudoRandom";
|
import { PseudoRandom } from "../../../core/PseudoRandom";
|
||||||
@@ -17,6 +16,7 @@ import {
|
|||||||
} from "../PlayerIcons";
|
} from "../PlayerIcons";
|
||||||
import { TransformHandler } from "../TransformHandler";
|
import { TransformHandler } from "../TransformHandler";
|
||||||
import { Layer } from "./Layer";
|
import { Layer } from "./Layer";
|
||||||
|
import shieldIcon from "/images/ShieldIconBlack.svg?url";
|
||||||
|
|
||||||
class RenderInfo {
|
class RenderInfo {
|
||||||
public icons: Map<PlayerIconId, HTMLElement> = new Map(); // Track icon elements
|
public icons: Map<PlayerIconId, HTMLElement> = new Map(); // Track icon elements
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
import { LitElement, TemplateResult, html } from "lit";
|
import { LitElement, TemplateResult, html } from "lit";
|
||||||
import { ref } from "lit-html/directives/ref.js";
|
import { ref } from "lit-html/directives/ref.js";
|
||||||
import { customElement, property, state } from "lit/decorators.js";
|
import { customElement, property, state } from "lit/decorators.js";
|
||||||
import allianceIcon from "../../../../resources/images/AllianceIcon.svg";
|
|
||||||
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 missileSiloIcon from "../../../../resources/images/MissileSiloIconWhite.svg";
|
|
||||||
import portIcon from "../../../../resources/images/PortIcon.svg";
|
|
||||||
import samLauncherIcon from "../../../../resources/images/SamLauncherIconWhite.svg";
|
|
||||||
import { renderPlayerFlag } from "../../../core/CustomFlag";
|
import { renderPlayerFlag } from "../../../core/CustomFlag";
|
||||||
import { EventBus } from "../../../core/EventBus";
|
import { EventBus } from "../../../core/EventBus";
|
||||||
import {
|
import {
|
||||||
@@ -32,6 +24,14 @@ import { getFirstPlacePlayer, getPlayerIcons } from "../PlayerIcons";
|
|||||||
import { TransformHandler } from "../TransformHandler";
|
import { TransformHandler } from "../TransformHandler";
|
||||||
import { Layer } from "./Layer";
|
import { Layer } from "./Layer";
|
||||||
import { CloseRadialMenuEvent } from "./RadialMenu";
|
import { CloseRadialMenuEvent } from "./RadialMenu";
|
||||||
|
import allianceIcon from "/images/AllianceIcon.svg?url";
|
||||||
|
import warshipIcon from "/images/BattleshipIconWhite.svg?url";
|
||||||
|
import cityIcon from "/images/CityIconWhite.svg?url";
|
||||||
|
import factoryIcon from "/images/FactoryIconWhite.svg?url";
|
||||||
|
import goldCoinIcon from "/images/GoldCoinIcon.svg?url";
|
||||||
|
import missileSiloIcon from "/images/MissileSiloIconWhite.svg?url";
|
||||||
|
import portIcon from "/images/PortIcon.svg?url";
|
||||||
|
import samLauncherIcon from "/images/SamLauncherIconWhite.svg?url";
|
||||||
|
|
||||||
function euclideanDistWorld(
|
function euclideanDistWorld(
|
||||||
coord: { x: number; y: number },
|
coord: { x: number; y: number },
|
||||||
|
|||||||
@@ -1,15 +1,6 @@
|
|||||||
import { html, LitElement } from "lit";
|
import { html, LitElement } from "lit";
|
||||||
import { customElement, state } from "lit/decorators.js";
|
import { customElement, state } from "lit/decorators.js";
|
||||||
import allianceIcon from "../../../../resources/images/AllianceIconWhite.svg";
|
import Countries from "../../../assets/data/countries.json" with { type: "json" };
|
||||||
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 stopTradingIcon from "../../../../resources/images/StopIconWhite.png";
|
|
||||||
import targetIcon from "../../../../resources/images/TargetIconWhite.svg";
|
|
||||||
import startTradingIcon from "../../../../resources/images/TradingIconWhite.png";
|
|
||||||
import traitorIcon from "../../../../resources/images/TraitorIconLightRed.svg";
|
|
||||||
import breakAllianceIcon from "../../../../resources/images/TraitorIconWhite.svg";
|
|
||||||
import { EventBus } from "../../../core/EventBus";
|
import { EventBus } from "../../../core/EventBus";
|
||||||
import {
|
import {
|
||||||
AllPlayers,
|
AllPlayers,
|
||||||
@@ -23,7 +14,6 @@ import { GameView, PlayerView } from "../../../core/game/GameView";
|
|||||||
import { Emoji, flattenedEmojiTable } from "../../../core/Util";
|
import { Emoji, flattenedEmojiTable } from "../../../core/Util";
|
||||||
import { actionButton } from "../../components/ui/ActionButton";
|
import { actionButton } from "../../components/ui/ActionButton";
|
||||||
import "../../components/ui/Divider";
|
import "../../components/ui/Divider";
|
||||||
import Countries from "../../data/countries.json";
|
|
||||||
import { CloseViewEvent, MouseUpEvent } from "../../InputHandler";
|
import { CloseViewEvent, MouseUpEvent } from "../../InputHandler";
|
||||||
import {
|
import {
|
||||||
SendAllianceRequestIntentEvent,
|
SendAllianceRequestIntentEvent,
|
||||||
@@ -44,6 +34,16 @@ import { ChatModal } from "./ChatModal";
|
|||||||
import { EmojiTable } from "./EmojiTable";
|
import { EmojiTable } from "./EmojiTable";
|
||||||
import { Layer } from "./Layer";
|
import { Layer } from "./Layer";
|
||||||
import "./SendResourceModal";
|
import "./SendResourceModal";
|
||||||
|
import allianceIcon from "/images/AllianceIconWhite.svg?url";
|
||||||
|
import chatIcon from "/images/ChatIconWhite.svg?url";
|
||||||
|
import donateGoldIcon from "/images/DonateGoldIconWhite.svg?url";
|
||||||
|
import donateTroopIcon from "/images/DonateTroopIconWhite.svg?url";
|
||||||
|
import emojiIcon from "/images/EmojiIconWhite.svg?url";
|
||||||
|
import stopTradingIcon from "/images/StopIconWhite.png?url";
|
||||||
|
import targetIcon from "/images/TargetIconWhite.svg?url";
|
||||||
|
import startTradingIcon from "/images/TradingIconWhite.png?url";
|
||||||
|
import traitorIcon from "/images/TraitorIconLightRed.svg?url";
|
||||||
|
import breakAllianceIcon from "/images/TraitorIconWhite.svg?url";
|
||||||
|
|
||||||
@customElement("player-panel")
|
@customElement("player-panel")
|
||||||
export class PlayerPanel extends LitElement implements Layer {
|
export class PlayerPanel extends LitElement implements Layer {
|
||||||
@@ -445,7 +445,7 @@ export class PlayerPanel extends LitElement implements Layer {
|
|||||||
${country && typeof flagCode === "string"
|
${country && typeof flagCode === "string"
|
||||||
? html`<img
|
? html`<img
|
||||||
src="/flags/${encodeURIComponent(flagCode)}.svg"
|
src="/flags/${encodeURIComponent(flagCode)}.svg"
|
||||||
alt=${country?.name || "Flag"}
|
alt=${country?.name ?? "Flag"}
|
||||||
class="h-10 w-10 rounded-full object-cover"
|
class="h-10 w-10 rounded-full object-cover"
|
||||||
@error=${(e: Event) => {
|
@error=${(e: Event) => {
|
||||||
(e.target as HTMLImageElement).style.display = "none";
|
(e.target as HTMLImageElement).style.display = "none";
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import * as d3 from "d3";
|
import * as d3 from "d3";
|
||||||
import backIcon from "../../../../resources/images/BackIconWhite.svg";
|
|
||||||
import { EventBus, GameEvent } from "../../../core/EventBus";
|
import { EventBus, GameEvent } from "../../../core/EventBus";
|
||||||
import { CloseViewEvent } from "../../InputHandler";
|
import { CloseViewEvent } from "../../InputHandler";
|
||||||
import { getSvgAspectRatio, translateText } from "../../Utils";
|
import { getSvgAspectRatio, translateText } from "../../Utils";
|
||||||
@@ -10,6 +9,7 @@ import {
|
|||||||
MenuElementParams,
|
MenuElementParams,
|
||||||
TooltipKey,
|
TooltipKey,
|
||||||
} from "./RadialMenuElements";
|
} from "./RadialMenuElements";
|
||||||
|
import backIcon from "/images/BackIconWhite.svg?url";
|
||||||
|
|
||||||
export class CloseRadialMenuEvent implements GameEvent {
|
export class CloseRadialMenuEvent implements GameEvent {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|||||||
@@ -12,19 +12,19 @@ import { PlayerActionHandler } from "./PlayerActionHandler";
|
|||||||
import { PlayerPanel } from "./PlayerPanel";
|
import { PlayerPanel } from "./PlayerPanel";
|
||||||
import { TooltipItem } from "./RadialMenu";
|
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";
|
|
||||||
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 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 { EventBus } from "../../../core/EventBus";
|
import { EventBus } from "../../../core/EventBus";
|
||||||
|
import allianceIcon from "/images/AllianceIconWhite.svg?url";
|
||||||
|
import boatIcon from "/images/BoatIconWhite.svg?url";
|
||||||
|
import buildIcon from "/images/BuildIconWhite.svg?url";
|
||||||
|
import chatIcon from "/images/ChatIconWhite.svg?url";
|
||||||
|
import donateGoldIcon from "/images/DonateGoldIconWhite.svg?url";
|
||||||
|
import donateTroopIcon from "/images/DonateTroopIconWhite.svg?url";
|
||||||
|
import emojiIcon from "/images/EmojiIconWhite.svg?url";
|
||||||
|
import infoIcon from "/images/InfoIcon.svg?url";
|
||||||
|
import swordIcon from "/images/SwordIconWhite.svg?url";
|
||||||
|
import targetIcon from "/images/TargetIconWhite.svg?url";
|
||||||
|
import traitorIcon from "/images/TraitorIconWhite.svg?url";
|
||||||
|
import xIcon from "/images/XIcon.svg?url";
|
||||||
|
|
||||||
export interface MenuElementParams {
|
export interface MenuElementParams {
|
||||||
myPlayer: PlayerView;
|
myPlayer: PlayerView;
|
||||||
|
|||||||
@@ -1,17 +1,5 @@
|
|||||||
import { html, LitElement } from "lit";
|
import { html, LitElement } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators.js";
|
import { customElement, property, query, state } from "lit/decorators.js";
|
||||||
import structureIcon from "../../../../resources/images/CityIconWhite.svg";
|
|
||||||
import cursorPriceIcon from "../../../../resources/images/CursorPriceIconWhite.svg";
|
|
||||||
import darkModeIcon from "../../../../resources/images/DarkModeIconWhite.svg";
|
|
||||||
import emojiIcon from "../../../../resources/images/EmojiIconWhite.svg";
|
|
||||||
import exitIcon from "../../../../resources/images/ExitIconWhite.svg";
|
|
||||||
import explosionIcon from "../../../../resources/images/ExplosionIconWhite.svg";
|
|
||||||
import mouseIcon from "../../../../resources/images/MouseIconWhite.svg";
|
|
||||||
import ninjaIcon from "../../../../resources/images/NinjaIconWhite.svg";
|
|
||||||
import settingsIcon from "../../../../resources/images/SettingIconWhite.svg";
|
|
||||||
import sirenIcon from "../../../../resources/images/SirenIconWhite.svg";
|
|
||||||
import treeIcon from "../../../../resources/images/TreeIconWhite.svg";
|
|
||||||
import musicIcon from "../../../../resources/images/music.svg";
|
|
||||||
import { EventBus } from "../../../core/EventBus";
|
import { EventBus } from "../../../core/EventBus";
|
||||||
import { UserSettings } from "../../../core/game/UserSettings";
|
import { UserSettings } from "../../../core/game/UserSettings";
|
||||||
import { AlternateViewEvent, RefreshGraphicsEvent } from "../../InputHandler";
|
import { AlternateViewEvent, RefreshGraphicsEvent } from "../../InputHandler";
|
||||||
@@ -19,6 +7,18 @@ import { PauseGameIntentEvent } from "../../Transport";
|
|||||||
import { translateText } from "../../Utils";
|
import { translateText } from "../../Utils";
|
||||||
import SoundManager from "../../sound/SoundManager";
|
import SoundManager from "../../sound/SoundManager";
|
||||||
import { Layer } from "./Layer";
|
import { Layer } from "./Layer";
|
||||||
|
import structureIcon from "/images/CityIconWhite.svg?url";
|
||||||
|
import cursorPriceIcon from "/images/CursorPriceIconWhite.svg?url";
|
||||||
|
import darkModeIcon from "/images/DarkModeIconWhite.svg?url";
|
||||||
|
import emojiIcon from "/images/EmojiIconWhite.svg?url";
|
||||||
|
import exitIcon from "/images/ExitIconWhite.svg?url";
|
||||||
|
import explosionIcon from "/images/ExplosionIconWhite.svg?url";
|
||||||
|
import mouseIcon from "/images/MouseIconWhite.svg?url";
|
||||||
|
import ninjaIcon from "/images/NinjaIconWhite.svg?url";
|
||||||
|
import settingsIcon from "/images/SettingIconWhite.svg?url";
|
||||||
|
import sirenIcon from "/images/SirenIconWhite.svg?url";
|
||||||
|
import treeIcon from "/images/TreeIconWhite.svg?url";
|
||||||
|
import musicIcon from "/images/music.svg?url";
|
||||||
|
|
||||||
export class ShowSettingsModalEvent {
|
export class ShowSettingsModalEvent {
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@@ -3,13 +3,12 @@ import { Theme } from "../../../core/configuration/Config";
|
|||||||
import { Cell, UnitType } from "../../../core/game/Game";
|
import { Cell, UnitType } from "../../../core/game/Game";
|
||||||
import { GameView, PlayerView, UnitView } from "../../../core/game/GameView";
|
import { GameView, PlayerView, UnitView } from "../../../core/game/GameView";
|
||||||
import { TransformHandler } from "../TransformHandler";
|
import { TransformHandler } from "../TransformHandler";
|
||||||
|
import anchorIcon from "/images/AnchorIcon.png?url";
|
||||||
import anchorIcon from "../../../../resources/images/AnchorIcon.png";
|
import cityIcon from "/images/CityIcon.png?url";
|
||||||
import cityIcon from "../../../../resources/images/CityIcon.png";
|
import factoryIcon from "/images/FactoryUnit.png?url";
|
||||||
import factoryIcon from "../../../../resources/images/FactoryUnit.png";
|
import missileSiloIcon from "/images/MissileSiloUnit.png?url";
|
||||||
import missileSiloIcon from "../../../../resources/images/MissileSiloUnit.png";
|
import SAMMissileIcon from "/images/SamLauncherUnit.png?url";
|
||||||
import SAMMissileIcon from "../../../../resources/images/SamLauncherUnit.png";
|
import shieldIcon from "/images/ShieldIcon.png?url";
|
||||||
import shieldIcon from "../../../../resources/images/ShieldIcon.png";
|
|
||||||
|
|
||||||
export const STRUCTURE_SHAPES: Partial<Record<UnitType, ShapeType>> = {
|
export const STRUCTURE_SHAPES: Partial<Record<UnitType, ShapeType>> = {
|
||||||
[UnitType.City]: "circle",
|
[UnitType.City]: "circle",
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { extend } from "colord";
|
|||||||
import a11yPlugin from "colord/plugins/a11y";
|
import a11yPlugin from "colord/plugins/a11y";
|
||||||
import { OutlineFilter } from "pixi-filters";
|
import { OutlineFilter } from "pixi-filters";
|
||||||
import * as PIXI from "pixi.js";
|
import * as PIXI from "pixi.js";
|
||||||
import bitmapFont from "../../../../resources/fonts/round_6x6_modified.xml";
|
|
||||||
import { Theme } from "../../../core/configuration/Config";
|
import { Theme } from "../../../core/configuration/Config";
|
||||||
import { EventBus } from "../../../core/EventBus";
|
import { EventBus } from "../../../core/EventBus";
|
||||||
import {
|
import {
|
||||||
@@ -40,6 +39,7 @@ import {
|
|||||||
STRUCTURE_SHAPES,
|
STRUCTURE_SHAPES,
|
||||||
ZOOM_THRESHOLD,
|
ZOOM_THRESHOLD,
|
||||||
} from "./StructureDrawingUtils";
|
} from "./StructureDrawingUtils";
|
||||||
|
import bitmapFont from "/fonts/round_6x6_modified.xml?url";
|
||||||
|
|
||||||
extend([a11yPlugin]);
|
extend([a11yPlugin]);
|
||||||
|
|
||||||
@@ -75,7 +75,8 @@ export class StructureIconsLayer implements Layer {
|
|||||||
public playerActions: PlayerActions | null = null;
|
public playerActions: PlayerActions | null = null;
|
||||||
private dotsStage: PIXI.Container;
|
private dotsStage: PIXI.Container;
|
||||||
private readonly theme: Theme;
|
private readonly theme: Theme;
|
||||||
private renderer: PIXI.Renderer;
|
private renderer: PIXI.Renderer | null = null;
|
||||||
|
private rendererInitialized: boolean = false;
|
||||||
private renders: StructureRenderInfo[] = [];
|
private renders: StructureRenderInfo[] = [];
|
||||||
private readonly seenUnits: Set<UnitView> = new Set();
|
private readonly seenUnits: Set<UnitView> = new Set();
|
||||||
private readonly mousePos = { x: 0, y: 0 };
|
private readonly mousePos = { x: 0, y: 0 };
|
||||||
@@ -113,7 +114,7 @@ export class StructureIconsLayer implements Layer {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to load bitmap font:", error);
|
console.error("Failed to load bitmap font:", error);
|
||||||
}
|
}
|
||||||
this.renderer = new PIXI.WebGLRenderer();
|
const renderer = new PIXI.WebGLRenderer();
|
||||||
this.pixicanvas = document.createElement("canvas");
|
this.pixicanvas = document.createElement("canvas");
|
||||||
this.pixicanvas.width = window.innerWidth;
|
this.pixicanvas.width = window.innerWidth;
|
||||||
this.pixicanvas.height = window.innerHeight;
|
this.pixicanvas.height = window.innerHeight;
|
||||||
@@ -143,7 +144,7 @@ export class StructureIconsLayer implements Layer {
|
|||||||
this.rootStage.position.set(0, 0);
|
this.rootStage.position.set(0, 0);
|
||||||
this.rootStage.setSize(this.pixicanvas.width, this.pixicanvas.height);
|
this.rootStage.setSize(this.pixicanvas.width, this.pixicanvas.height);
|
||||||
|
|
||||||
await this.renderer.init({
|
await renderer.init({
|
||||||
canvas: this.pixicanvas,
|
canvas: this.pixicanvas,
|
||||||
resolution: 1,
|
resolution: 1,
|
||||||
width: this.pixicanvas.width,
|
width: this.pixicanvas.width,
|
||||||
@@ -153,6 +154,9 @@ export class StructureIconsLayer implements Layer {
|
|||||||
backgroundAlpha: 0,
|
backgroundAlpha: 0,
|
||||||
backgroundColor: 0x00000000,
|
backgroundColor: 0x00000000,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.renderer = renderer;
|
||||||
|
this.rendererInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldTransform(): boolean {
|
shouldTransform(): boolean {
|
||||||
@@ -202,7 +206,7 @@ export class StructureIconsLayer implements Layer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderLayer(mainContext: CanvasRenderingContext2D) {
|
renderLayer(mainContext: CanvasRenderingContext2D) {
|
||||||
if (!this.renderer) {
|
if (!this.renderer || !this.rendererInitialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,16 +4,16 @@ import { EventBus } from "../../../core/EventBus";
|
|||||||
import { TransformHandler } from "../TransformHandler";
|
import { TransformHandler } from "../TransformHandler";
|
||||||
import { Layer } from "./Layer";
|
import { Layer } from "./Layer";
|
||||||
|
|
||||||
import cityIcon from "../../../../resources/images/buildings/cityAlt1.png";
|
|
||||||
import factoryIcon from "../../../../resources/images/buildings/factoryAlt1.png";
|
|
||||||
import shieldIcon from "../../../../resources/images/buildings/fortAlt3.png";
|
|
||||||
import anchorIcon from "../../../../resources/images/buildings/port1.png";
|
|
||||||
import missileSiloIcon from "../../../../resources/images/buildings/silo1.png";
|
|
||||||
import SAMMissileIcon from "../../../../resources/images/buildings/silo4.png";
|
|
||||||
import { Cell, UnitType } from "../../../core/game/Game";
|
import { Cell, UnitType } from "../../../core/game/Game";
|
||||||
import { euclDistFN, isometricDistFN } from "../../../core/game/GameMap";
|
import { euclDistFN, isometricDistFN } from "../../../core/game/GameMap";
|
||||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||||
import { GameView, UnitView } from "../../../core/game/GameView";
|
import { GameView, UnitView } from "../../../core/game/GameView";
|
||||||
|
import cityIcon from "/images/buildings/cityAlt1.png?url";
|
||||||
|
import factoryIcon from "/images/buildings/factoryAlt1.png?url";
|
||||||
|
import shieldIcon from "/images/buildings/fortAlt3.png?url";
|
||||||
|
import anchorIcon from "/images/buildings/port1.png?url";
|
||||||
|
import missileSiloIcon from "/images/buildings/silo1.png?url";
|
||||||
|
import SAMMissileIcon from "/images/buildings/silo4.png?url";
|
||||||
|
|
||||||
const underConstructionColor = colord("rgb(150,150,150)");
|
const underConstructionColor = colord("rgb(150,150,150)");
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,5 @@
|
|||||||
import { LitElement, html } from "lit";
|
import { LitElement, html } from "lit";
|
||||||
import { customElement } from "lit/decorators.js";
|
import { customElement } 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 mirvIcon from "../../../../resources/images/MIRVIcon.svg";
|
|
||||||
import missileSiloIcon from "../../../../resources/images/MissileSiloIconWhite.svg";
|
|
||||||
import hydrogenBombIcon from "../../../../resources/images/MushroomCloudIconWhite.svg";
|
|
||||||
import atomBombIcon from "../../../../resources/images/NukeIconWhite.svg";
|
|
||||||
import portIcon from "../../../../resources/images/PortIcon.svg";
|
|
||||||
import samLauncherIcon from "../../../../resources/images/SamLauncherIconWhite.svg";
|
|
||||||
import defensePostIcon from "../../../../resources/images/ShieldIconWhite.svg";
|
|
||||||
import { EventBus } from "../../../core/EventBus";
|
import { EventBus } from "../../../core/EventBus";
|
||||||
import { Gold, PlayerActions, UnitType } from "../../../core/game/Game";
|
import { Gold, PlayerActions, UnitType } from "../../../core/game/Game";
|
||||||
import { GameView } from "../../../core/game/GameView";
|
import { GameView } from "../../../core/game/GameView";
|
||||||
@@ -20,6 +10,16 @@ import {
|
|||||||
import { renderNumber, translateText } from "../../Utils";
|
import { renderNumber, translateText } from "../../Utils";
|
||||||
import { UIState } from "../UIState";
|
import { UIState } from "../UIState";
|
||||||
import { Layer } from "./Layer";
|
import { Layer } from "./Layer";
|
||||||
|
import warshipIcon from "/images/BattleshipIconWhite.svg?url";
|
||||||
|
import cityIcon from "/images/CityIconWhite.svg?url";
|
||||||
|
import factoryIcon from "/images/FactoryIconWhite.svg?url";
|
||||||
|
import mirvIcon from "/images/MIRVIcon.svg?url";
|
||||||
|
import missileSiloIcon from "/images/MissileSiloIconWhite.svg?url";
|
||||||
|
import hydrogenBombIcon from "/images/MushroomCloudIconWhite.svg?url";
|
||||||
|
import atomBombIcon from "/images/NukeIconWhite.svg?url";
|
||||||
|
import portIcon from "/images/PortIcon.svg?url";
|
||||||
|
import samLauncherIcon from "/images/SamLauncherIconWhite.svg?url";
|
||||||
|
import defensePostIcon from "/images/ShieldIconWhite.svg?url";
|
||||||
|
|
||||||
@customElement("unit-display")
|
@customElement("unit-display")
|
||||||
export class UnitDisplay extends LitElement implements Layer {
|
export class UnitDisplay extends LitElement implements Layer {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Howl } from "howler";
|
|||||||
import of4 from "../../../proprietary/sounds/music/of4.mp3";
|
import of4 from "../../../proprietary/sounds/music/of4.mp3";
|
||||||
import openfront from "../../../proprietary/sounds/music/openfront.mp3";
|
import openfront from "../../../proprietary/sounds/music/openfront.mp3";
|
||||||
import war from "../../../proprietary/sounds/music/war.mp3";
|
import war from "../../../proprietary/sounds/music/war.mp3";
|
||||||
import kaChingSound from "../../../resources/sounds/effects/ka-ching.mp3";
|
import kaChingSound from "/sounds/effects/ka-ching.mp3?url";
|
||||||
|
|
||||||
export enum SoundEffect {
|
export enum SoundEffect {
|
||||||
KaChing = "ka-ching",
|
KaChing = "ka-ching",
|
||||||
|
|||||||
+1
-10
@@ -1,16 +1,7 @@
|
|||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
@import url("./styles/core/flag-animation.css");
|
|
||||||
@import url("./styles/core/variables.css");
|
|
||||||
@import url("./styles/core/typography.css");
|
|
||||||
@import url("./styles/layout/header.css");
|
|
||||||
@import url("./styles/layout/container.css");
|
|
||||||
@import url("./styles/components/button.css");
|
|
||||||
@import url("./styles/components/modal.css");
|
|
||||||
@import url("./styles/modal/chat.css");
|
|
||||||
@import url("./styles/components/setting.css");
|
|
||||||
@import url("./styles/components/controls.css");
|
|
||||||
* {
|
* {
|
||||||
-webkit-box-sizing: border-box;
|
-webkit-box-sizing: border-box;
|
||||||
-moz-box-sizing: border-box;
|
-moz-box-sizing: border-box;
|
||||||
|
|||||||
Vendored
+26
@@ -0,0 +1,26 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare module "*.bin" {
|
||||||
|
const binContent: string;
|
||||||
|
export default binContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.md" {
|
||||||
|
const mdContent: string;
|
||||||
|
export default mdContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.html" {
|
||||||
|
const htmlContent: string;
|
||||||
|
export default htmlContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.xml" {
|
||||||
|
const xmlContent: string;
|
||||||
|
export default xmlContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.webp" {
|
||||||
|
const webpContent: string;
|
||||||
|
export default webpContent;
|
||||||
|
}
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import quickChatData from "../../resources/QuickChat.json" with { type: "json" };
|
import countries from "../assets/data/countries.json" with { type: "json" };
|
||||||
import countries from "../client/data/countries.json" with { type: "json" };
|
import quickChatData from "../assets/data/QuickChat.json" with { type: "json" };
|
||||||
import {
|
import {
|
||||||
ColorPaletteSchema,
|
ColorPaletteSchema,
|
||||||
PatternDataSchema,
|
PatternDataSchema,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { GameConfig } from "../Schemas";
|
|||||||
import { Config, GameEnv, ServerConfig } from "./Config";
|
import { Config, GameEnv, ServerConfig } from "./Config";
|
||||||
import { DefaultConfig } from "./DefaultConfig";
|
import { DefaultConfig } from "./DefaultConfig";
|
||||||
import { DevConfig, DevServerConfig } from "./DevConfig";
|
import { DevConfig, DevServerConfig } from "./DevConfig";
|
||||||
|
import { Env } from "./Env";
|
||||||
import { preprodConfig } from "./PreprodConfig";
|
import { preprodConfig } from "./PreprodConfig";
|
||||||
import { prodConfig } from "./ProdConfig";
|
import { prodConfig } from "./ProdConfig";
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ export async function getConfig(
|
|||||||
console.log("using prod config");
|
console.log("using prod config");
|
||||||
return new DefaultConfig(sc, gameConfig, userSettings, isReplay);
|
return new DefaultConfig(sc, gameConfig, userSettings, isReplay);
|
||||||
default:
|
default:
|
||||||
throw Error(`unsupported server configuration: ${process.env.GAME_ENV}`);
|
throw Error(`unsupported server configuration: ${Env.GAME_ENV}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function getServerConfigFromClient(): Promise<ServerConfig> {
|
export async function getServerConfigFromClient(): Promise<ServerConfig> {
|
||||||
@@ -44,7 +45,7 @@ export async function getServerConfigFromClient(): Promise<ServerConfig> {
|
|||||||
return cachedSC;
|
return cachedSC;
|
||||||
}
|
}
|
||||||
export function getServerConfigFromServer(): ServerConfig {
|
export function getServerConfigFromServer(): ServerConfig {
|
||||||
const gameEnv = process.env.GAME_ENV ?? "dev";
|
const gameEnv = Env.GAME_ENV;
|
||||||
return getServerConfig(gameEnv);
|
return getServerConfig(gameEnv);
|
||||||
}
|
}
|
||||||
export function getServerConfig(gameEnv: string) {
|
export function getServerConfig(gameEnv: string) {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import { GameConfig, GameID, TeamCountConfig } from "../Schemas";
|
|||||||
import { NukeType } from "../StatsSchemas";
|
import { NukeType } from "../StatsSchemas";
|
||||||
import { assertNever, sigmoid, simpleHash, within } from "../Util";
|
import { assertNever, sigmoid, simpleHash, within } from "../Util";
|
||||||
import { Config, GameEnv, NukeMagnitude, ServerConfig, Theme } from "./Config";
|
import { Config, GameEnv, NukeMagnitude, ServerConfig, Theme } from "./Config";
|
||||||
|
import { Env } from "./Env";
|
||||||
import { PastelTheme } from "./PastelTheme";
|
import { PastelTheme } from "./PastelTheme";
|
||||||
import { PastelThemeDark } from "./PastelThemeDark";
|
import { PastelThemeDark } from "./PastelThemeDark";
|
||||||
|
|
||||||
@@ -88,20 +89,20 @@ const numPlayersConfig = {
|
|||||||
|
|
||||||
export abstract class DefaultServerConfig implements ServerConfig {
|
export abstract class DefaultServerConfig implements ServerConfig {
|
||||||
turnstileSecretKey(): string {
|
turnstileSecretKey(): string {
|
||||||
return process.env.TURNSTILE_SECRET_KEY ?? "";
|
return Env.TURNSTILE_SECRET_KEY ?? "";
|
||||||
}
|
}
|
||||||
abstract turnstileSiteKey(): string;
|
abstract turnstileSiteKey(): string;
|
||||||
allowedFlares(): string[] | undefined {
|
allowedFlares(): string[] | undefined {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stripePublishableKey(): string {
|
stripePublishableKey(): string {
|
||||||
return process.env.STRIPE_PUBLISHABLE_KEY ?? "";
|
return Env.STRIPE_PUBLISHABLE_KEY ?? "";
|
||||||
}
|
}
|
||||||
domain(): string {
|
domain(): string {
|
||||||
return process.env.DOMAIN ?? "";
|
return Env.DOMAIN ?? "";
|
||||||
}
|
}
|
||||||
subdomain(): string {
|
subdomain(): string {
|
||||||
return process.env.SUBDOMAIN ?? "";
|
return Env.SUBDOMAIN ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
private publicKey: JWK;
|
private publicKey: JWK;
|
||||||
@@ -134,24 +135,24 @@ export abstract class DefaultServerConfig implements ServerConfig {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
otelEndpoint(): string {
|
otelEndpoint(): string {
|
||||||
return process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "";
|
return Env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "";
|
||||||
}
|
}
|
||||||
otelAuthHeader(): string {
|
otelAuthHeader(): string {
|
||||||
return process.env.OTEL_AUTH_HEADER ?? "";
|
return Env.OTEL_AUTH_HEADER ?? "";
|
||||||
}
|
}
|
||||||
gitCommit(): string {
|
gitCommit(): string {
|
||||||
return process.env.GIT_COMMIT ?? "";
|
return Env.GIT_COMMIT ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
apiKey(): string {
|
apiKey(): string {
|
||||||
return process.env.API_KEY ?? "";
|
return Env.API_KEY ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
adminHeader(): string {
|
adminHeader(): string {
|
||||||
return "x-admin-key";
|
return "x-admin-key";
|
||||||
}
|
}
|
||||||
adminToken(): string {
|
adminToken(): string {
|
||||||
const token = process.env.ADMIN_TOKEN;
|
const token = Env.ADMIN_TOKEN;
|
||||||
if (!token) {
|
if (!token) {
|
||||||
throw new Error("ADMIN_TOKEN not set");
|
throw new Error("ADMIN_TOKEN not set");
|
||||||
}
|
}
|
||||||
@@ -225,7 +226,7 @@ export class DefaultConfig implements Config {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
stripePublishableKey(): string {
|
stripePublishableKey(): string {
|
||||||
return process.env.STRIPE_PUBLISHABLE_KEY ?? "";
|
return Env.STRIPE_PUBLISHABLE_KEY ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
isReplay(): boolean {
|
isReplay(): boolean {
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/**
|
||||||
|
* Safely access environment variables in both Node.js and Vite environments.
|
||||||
|
* - In Vite (Browser), it uses `import.meta.env`.
|
||||||
|
* - In Node.js (Server), it uses `process.env`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface ImportMetaEnv {
|
||||||
|
[key: string]: string | boolean | undefined;
|
||||||
|
}
|
||||||
|
interface ImportMeta {
|
||||||
|
readonly env: ImportMetaEnv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEnv(key: string, viteKey?: string): string | undefined {
|
||||||
|
const vKey = viteKey ?? key;
|
||||||
|
|
||||||
|
// Try import.meta.env (Vite/Browser)
|
||||||
|
// We use a try-catch block or check existence to avoid ReferenceErrors
|
||||||
|
try {
|
||||||
|
if (typeof import.meta !== "undefined" && import.meta.env) {
|
||||||
|
const val = import.meta.env[vKey] ?? import.meta.env[key];
|
||||||
|
if (val !== undefined) {
|
||||||
|
return String(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Ignore errors accessing import.meta
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try process.env (Node.js)
|
||||||
|
try {
|
||||||
|
if (typeof process !== "undefined" && process.env) {
|
||||||
|
const val = process.env[key];
|
||||||
|
if (val !== undefined) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Ignore errors accessing process
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Env = {
|
||||||
|
get GAME_ENV(): string {
|
||||||
|
// Check MODE for Vite, GAME_ENV for Node
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
typeof import.meta !== "undefined" &&
|
||||||
|
import.meta.env &&
|
||||||
|
import.meta.env.MODE
|
||||||
|
) {
|
||||||
|
return import.meta.env.MODE;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Ignore errors accessing import.meta
|
||||||
|
}
|
||||||
|
|
||||||
|
return getEnv("GAME_ENV") ?? "dev";
|
||||||
|
},
|
||||||
|
|
||||||
|
get TURNSTILE_SECRET_KEY() {
|
||||||
|
return getEnv("TURNSTILE_SECRET_KEY");
|
||||||
|
},
|
||||||
|
get STRIPE_PUBLISHABLE_KEY() {
|
||||||
|
return getEnv("STRIPE_PUBLISHABLE_KEY");
|
||||||
|
},
|
||||||
|
get DOMAIN() {
|
||||||
|
return getEnv("DOMAIN");
|
||||||
|
},
|
||||||
|
get SUBDOMAIN() {
|
||||||
|
return getEnv("SUBDOMAIN");
|
||||||
|
},
|
||||||
|
get OTEL_EXPORTER_OTLP_ENDPOINT() {
|
||||||
|
return getEnv("OTEL_EXPORTER_OTLP_ENDPOINT");
|
||||||
|
},
|
||||||
|
get OTEL_AUTH_HEADER() {
|
||||||
|
return getEnv("OTEL_AUTH_HEADER");
|
||||||
|
},
|
||||||
|
get GIT_COMMIT() {
|
||||||
|
return getEnv("GIT_COMMIT");
|
||||||
|
},
|
||||||
|
get API_KEY() {
|
||||||
|
return getEnv("API_KEY");
|
||||||
|
},
|
||||||
|
get ADMIN_TOKEN() {
|
||||||
|
return getEnv("ADMIN_TOKEN");
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -2,14 +2,6 @@ import { GameMapType } from "./Game";
|
|||||||
import { GameMapLoader, MapData } from "./GameMapLoader";
|
import { GameMapLoader, MapData } from "./GameMapLoader";
|
||||||
import { MapManifest } from "./TerrainMapLoader";
|
import { MapManifest } from "./TerrainMapLoader";
|
||||||
|
|
||||||
export interface BinModule {
|
|
||||||
default: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NationMapModule {
|
|
||||||
default: MapManifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BinaryLoaderGameMapLoader implements GameMapLoader {
|
export class BinaryLoaderGameMapLoader implements GameMapLoader {
|
||||||
private maps: Map<GameMapType, MapData>;
|
private maps: Map<GameMapType, MapData>;
|
||||||
|
|
||||||
@@ -36,59 +28,38 @@ export class BinaryLoaderGameMapLoader implements GameMapLoader {
|
|||||||
);
|
);
|
||||||
const fileName = key?.toLowerCase();
|
const fileName = key?.toLowerCase();
|
||||||
|
|
||||||
|
const loadBinary = (url: string) =>
|
||||||
|
fetch(url)
|
||||||
|
.then((res) => {
|
||||||
|
if (!res.ok) throw new Error(`Failed to load ${url}`);
|
||||||
|
return res.arrayBuffer();
|
||||||
|
})
|
||||||
|
.then((buf) => new Uint8Array(buf));
|
||||||
|
|
||||||
|
const mapBasePath = `/maps/${fileName}`;
|
||||||
|
|
||||||
const mapData = {
|
const mapData = {
|
||||||
mapBin: this.createLazyLoader(() =>
|
mapBin: this.createLazyLoader(() => loadBinary(`${mapBasePath}/map.bin`)),
|
||||||
(
|
|
||||||
import(
|
|
||||||
`!!binary-loader!../../../resources/maps/${fileName}/map.bin`
|
|
||||||
) as Promise<BinModule>
|
|
||||||
).then((m) => this.toUInt8Array(m.default)),
|
|
||||||
),
|
|
||||||
map4xBin: this.createLazyLoader(() =>
|
map4xBin: this.createLazyLoader(() =>
|
||||||
(
|
loadBinary(`${mapBasePath}/map4x.bin`),
|
||||||
import(
|
|
||||||
`!!binary-loader!../../../resources/maps/${fileName}/map4x.bin`
|
|
||||||
) as Promise<BinModule>
|
|
||||||
).then((m) => this.toUInt8Array(m.default)),
|
|
||||||
),
|
),
|
||||||
map16xBin: this.createLazyLoader(() =>
|
map16xBin: this.createLazyLoader(() =>
|
||||||
(
|
loadBinary(`${mapBasePath}/map16x.bin`),
|
||||||
import(
|
|
||||||
`!!binary-loader!../../../resources/maps/${fileName}/map16x.bin`
|
|
||||||
) as Promise<BinModule>
|
|
||||||
).then((m) => this.toUInt8Array(m.default)),
|
|
||||||
),
|
),
|
||||||
manifest: this.createLazyLoader(() =>
|
manifest: this.createLazyLoader(() =>
|
||||||
(
|
fetch(`${mapBasePath}/manifest.json`).then((res) => {
|
||||||
import(
|
if (!res.ok) {
|
||||||
`../../../resources/maps/${fileName}/manifest.json`
|
throw new Error(`Failed to load ${mapBasePath}/manifest.json`);
|
||||||
) as Promise<NationMapModule>
|
}
|
||||||
).then((m) => m.default),
|
return res.json() as Promise<MapManifest>;
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
webpPath: this.createLazyLoader(() =>
|
webpPath: this.createLazyLoader(() =>
|
||||||
(
|
Promise.resolve(`${mapBasePath}/thumbnail.webp`),
|
||||||
import(
|
|
||||||
`../../../resources/maps/${fileName}/thumbnail.webp`
|
|
||||||
) as Promise<{ default: string }>
|
|
||||||
).then((m) => m.default),
|
|
||||||
),
|
),
|
||||||
} satisfies MapData;
|
} satisfies MapData;
|
||||||
|
|
||||||
this.maps.set(map, mapData);
|
this.maps.set(map, mapData);
|
||||||
return mapData;
|
return mapData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a given string into a UInt8Array where each character in the string
|
|
||||||
* is represented as an 8-bit unsigned integer.
|
|
||||||
*/
|
|
||||||
private toUInt8Array(data: string) {
|
|
||||||
const rawData = new Uint8Array(data.length);
|
|
||||||
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
|
||||||
rawData[i] = data.charCodeAt(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rawData;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ export class FetchGameMapLoader implements GameMapLoader {
|
|||||||
let url = `${this.prefix}/${map}/${path}`;
|
let url = `${this.prefix}/${map}/${path}`;
|
||||||
|
|
||||||
if (this.cacheBuster) {
|
if (this.cacheBuster) {
|
||||||
url += `${url.includes("?") ? "&" : "?"}v=${this.cacheBuster}`;
|
url += `${url.includes("?") ? "&" : "?"}v=${encodeURIComponent(
|
||||||
|
this.cacheBuster.trim(),
|
||||||
|
)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import version from "../../../resources/version.txt";
|
import version from "../../assets/data/version.txt?raw";
|
||||||
import { createGameRunner, GameRunner } from "../GameRunner";
|
import { createGameRunner, GameRunner } from "../GameRunner";
|
||||||
import { FetchGameMapLoader } from "../game/FetchGameMapLoader";
|
import { FetchGameMapLoader } from "../game/FetchGameMapLoader";
|
||||||
import { ErrorUpdate, GameUpdateViewData } from "../game/GameUpdates";
|
import { ErrorUpdate, GameUpdateViewData } from "../game/GameUpdates";
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ export class WorkerClient {
|
|||||||
private gameStartInfo: GameStartInfo,
|
private gameStartInfo: GameStartInfo,
|
||||||
private clientID: ClientID,
|
private clientID: ClientID,
|
||||||
) {
|
) {
|
||||||
this.worker = new Worker(new URL("./Worker.worker.ts", import.meta.url));
|
this.worker = new Worker(new URL("./Worker.worker.ts", import.meta.url), {
|
||||||
|
type: "module",
|
||||||
|
});
|
||||||
this.messageHandlers = new Map();
|
this.messageHandlers = new Map();
|
||||||
|
|
||||||
// Set up global message handler
|
// Set up global message handler
|
||||||
|
|||||||
Vendored
-47
@@ -1,47 +0,0 @@
|
|||||||
declare module "*.png" {
|
|
||||||
const content: string;
|
|
||||||
export default content;
|
|
||||||
}
|
|
||||||
declare module "*.jpg" {
|
|
||||||
const value: string;
|
|
||||||
export default value;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module "*.webp" {
|
|
||||||
const value: string;
|
|
||||||
export default value;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module "*.jpeg" {
|
|
||||||
const value: string;
|
|
||||||
export default value;
|
|
||||||
}
|
|
||||||
declare module "*.svg" {
|
|
||||||
const value: string;
|
|
||||||
export default value;
|
|
||||||
}
|
|
||||||
declare module "*.bin" {
|
|
||||||
const value: string;
|
|
||||||
export default value;
|
|
||||||
}
|
|
||||||
declare module "*.md" {
|
|
||||||
const value: string;
|
|
||||||
export default value;
|
|
||||||
}
|
|
||||||
declare module "*.txt" {
|
|
||||||
const value: string;
|
|
||||||
export default value;
|
|
||||||
}
|
|
||||||
declare module "*.html" {
|
|
||||||
const content: string;
|
|
||||||
export default content;
|
|
||||||
}
|
|
||||||
declare module "*.xml" {
|
|
||||||
const value: string;
|
|
||||||
export default value;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module "*.mp3" {
|
|
||||||
const value: string;
|
|
||||||
export default value;
|
|
||||||
}
|
|
||||||
Regular → Executable
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
// tailwind.config.js
|
// tailwind.config.js
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
export default {
|
||||||
content: ["./src/**/*.{html,ts,js}"],
|
content: ["./index.html", "./src/**/*.{html,ts,js}"],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ describe("AllianceExtensionExecution", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Successfully extends existing alliance between Humans", () => {
|
test("Successfully extends existing alliance between Humans", () => {
|
||||||
jest.spyOn(player1, "canSendAllianceRequest").mockReturnValue(true);
|
vi.spyOn(player1, "canSendAllianceRequest").mockReturnValue(true);
|
||||||
jest.spyOn(player2, "isAlive").mockReturnValue(true);
|
vi.spyOn(player2, "isAlive").mockReturnValue(true);
|
||||||
jest.spyOn(player1, "isAlive").mockReturnValue(true);
|
vi.spyOn(player1, "isAlive").mockReturnValue(true);
|
||||||
|
|
||||||
game.addExecution(new AllianceRequestExecution(player1, player2.id()));
|
game.addExecution(new AllianceRequestExecution(player1, player2.id()));
|
||||||
game.executeNextTick();
|
game.executeNextTick();
|
||||||
@@ -53,7 +53,7 @@ describe("AllianceExtensionExecution", () => {
|
|||||||
expect(player2.allianceWith(player1)).toBeTruthy();
|
expect(player2.allianceWith(player1)).toBeTruthy();
|
||||||
|
|
||||||
const allianceBefore = player1.allianceWith(player2)!;
|
const allianceBefore = player1.allianceWith(player2)!;
|
||||||
const allianceSpy = jest.spyOn(allianceBefore, "extend");
|
const allianceSpy = vi.spyOn(allianceBefore, "extend");
|
||||||
|
|
||||||
const expirationBefore = allianceBefore.expiresAt();
|
const expirationBefore = allianceBefore.expiresAt();
|
||||||
|
|
||||||
@@ -82,9 +82,9 @@ describe("AllianceExtensionExecution", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Successfully extends existing alliance between Human and non-Human", () => {
|
test("Successfully extends existing alliance between Human and non-Human", () => {
|
||||||
jest.spyOn(player1, "canSendAllianceRequest").mockReturnValue(true);
|
vi.spyOn(player1, "canSendAllianceRequest").mockReturnValue(true);
|
||||||
jest.spyOn(player3, "isAlive").mockReturnValue(true);
|
vi.spyOn(player3, "isAlive").mockReturnValue(true);
|
||||||
jest.spyOn(player1, "isAlive").mockReturnValue(true);
|
vi.spyOn(player1, "isAlive").mockReturnValue(true);
|
||||||
|
|
||||||
game.addExecution(new AllianceRequestExecution(player1, player3.id()));
|
game.addExecution(new AllianceRequestExecution(player1, player3.id()));
|
||||||
game.executeNextTick();
|
game.executeNextTick();
|
||||||
@@ -100,7 +100,7 @@ describe("AllianceExtensionExecution", () => {
|
|||||||
expect(player3.allianceWith(player1)).toBeTruthy();
|
expect(player3.allianceWith(player1)).toBeTruthy();
|
||||||
|
|
||||||
const allianceBefore = player1.allianceWith(player3)!;
|
const allianceBefore = player1.allianceWith(player3)!;
|
||||||
const allianceSpy = jest.spyOn(allianceBefore, "extend");
|
const allianceSpy = vi.spyOn(allianceBefore, "extend");
|
||||||
const expirationBefore = allianceBefore.expiresAt();
|
const expirationBefore = allianceBefore.expiresAt();
|
||||||
|
|
||||||
game.addExecution(new AllianceExtensionExecution(player1, player3.id()));
|
game.addExecution(new AllianceExtensionExecution(player1, player3.id()));
|
||||||
@@ -120,9 +120,9 @@ describe("AllianceExtensionExecution", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Sends message to other player when one player requests renewal", () => {
|
test("Sends message to other player when one player requests renewal", () => {
|
||||||
jest.spyOn(player1, "canSendAllianceRequest").mockReturnValue(true);
|
vi.spyOn(player1, "canSendAllianceRequest").mockReturnValue(true);
|
||||||
jest.spyOn(player2, "isAlive").mockReturnValue(true);
|
vi.spyOn(player2, "isAlive").mockReturnValue(true);
|
||||||
jest.spyOn(player1, "isAlive").mockReturnValue(true);
|
vi.spyOn(player1, "isAlive").mockReturnValue(true);
|
||||||
|
|
||||||
// Create alliance between player1 and player2
|
// Create alliance between player1 and player2
|
||||||
game.addExecution(new AllianceRequestExecution(player1, player2.id()));
|
game.addExecution(new AllianceRequestExecution(player1, player2.id()));
|
||||||
@@ -139,7 +139,7 @@ describe("AllianceExtensionExecution", () => {
|
|||||||
expect(player2.allianceWith(player1)).toBeTruthy();
|
expect(player2.allianceWith(player1)).toBeTruthy();
|
||||||
|
|
||||||
// Spy on displayMessage to verify it's called
|
// Spy on displayMessage to verify it's called
|
||||||
const displayMessageSpy = jest.spyOn(game, "displayMessage");
|
const displayMessageSpy = vi.spyOn(game, "displayMessage");
|
||||||
|
|
||||||
// Player1 requests renewal
|
// Player1 requests renewal
|
||||||
game.addExecution(new AllianceExtensionExecution(player1, player2.id()));
|
game.addExecution(new AllianceExtensionExecution(player1, player2.id()));
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
/**
|
|
||||||
* @jest-environment jsdom
|
|
||||||
*/
|
|
||||||
import { AutoUpgradeEvent } from "../src/client/InputHandler";
|
import { AutoUpgradeEvent } from "../src/client/InputHandler";
|
||||||
import { EventBus } from "../src/core/EventBus";
|
import { EventBus } from "../src/core/EventBus";
|
||||||
|
|
||||||
@@ -19,7 +16,7 @@ describe("AutoUpgrade Feature", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("should emit AutoUpgradeEvent when created", () => {
|
test("should emit AutoUpgradeEvent when created", () => {
|
||||||
const mockEmit = jest.spyOn(eventBus, "emit");
|
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||||
|
|
||||||
const event = new AutoUpgradeEvent(150, 250);
|
const event = new AutoUpgradeEvent(150, 250);
|
||||||
eventBus.emit(event);
|
eventBus.emit(event);
|
||||||
@@ -36,7 +33,7 @@ describe("AutoUpgrade Feature", () => {
|
|||||||
|
|
||||||
describe("AutoUpgradeEvent Integration", () => {
|
describe("AutoUpgradeEvent Integration", () => {
|
||||||
test("should handle multiple AutoUpgradeEvents", () => {
|
test("should handle multiple AutoUpgradeEvents", () => {
|
||||||
const mockEmit = jest.spyOn(eventBus, "emit");
|
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||||
|
|
||||||
const event1 = new AutoUpgradeEvent(100, 200);
|
const event1 = new AutoUpgradeEvent(100, 200);
|
||||||
const event2 = new AutoUpgradeEvent(300, 400);
|
const event2 = new AutoUpgradeEvent(300, 400);
|
||||||
@@ -70,7 +67,7 @@ describe("AutoUpgrade Feature", () => {
|
|||||||
|
|
||||||
describe("AutoUpgradeEvent Event Bus Integration", () => {
|
describe("AutoUpgradeEvent Event Bus Integration", () => {
|
||||||
test("should allow event listeners to subscribe to AutoUpgradeEvent", () => {
|
test("should allow event listeners to subscribe to AutoUpgradeEvent", () => {
|
||||||
const mockListener = jest.fn();
|
const mockListener = vi.fn();
|
||||||
const event = new AutoUpgradeEvent(100, 200);
|
const event = new AutoUpgradeEvent(100, 200);
|
||||||
|
|
||||||
eventBus.on(AutoUpgradeEvent, mockListener);
|
eventBus.on(AutoUpgradeEvent, mockListener);
|
||||||
@@ -80,8 +77,8 @@ describe("AutoUpgrade Feature", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("should allow multiple listeners for AutoUpgradeEvent", () => {
|
test("should allow multiple listeners for AutoUpgradeEvent", () => {
|
||||||
const mockListener1 = jest.fn();
|
const mockListener1 = vi.fn();
|
||||||
const mockListener2 = jest.fn();
|
const mockListener2 = vi.fn();
|
||||||
const event = new AutoUpgradeEvent(100, 200);
|
const event = new AutoUpgradeEvent(100, 200);
|
||||||
|
|
||||||
eventBus.on(AutoUpgradeEvent, mockListener1);
|
eventBus.on(AutoUpgradeEvent, mockListener1);
|
||||||
@@ -93,7 +90,7 @@ describe("AutoUpgrade Feature", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("should not call unsubscribed listeners", () => {
|
test("should not call unsubscribed listeners", () => {
|
||||||
const mockListener = jest.fn();
|
const mockListener = vi.fn();
|
||||||
const event = new AutoUpgradeEvent(100, 200);
|
const event = new AutoUpgradeEvent(100, 200);
|
||||||
|
|
||||||
eventBus.on(AutoUpgradeEvent, mockListener);
|
eventBus.on(AutoUpgradeEvent, mockListener);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Mocking the obscenity library to control its behavior in tests.
|
// Mocking the obscenity library to control its behavior in tests.
|
||||||
jest.mock("obscenity", () => {
|
vi.mock("obscenity", () => {
|
||||||
return {
|
return {
|
||||||
RegExpMatcher: class {
|
RegExpMatcher: class {
|
||||||
private dummy: string[] = ["foo", "bar", "leet", "code"];
|
private dummy: string[] = ["foo", "bar", "leet", "code"];
|
||||||
@@ -26,7 +26,7 @@ jest.mock("obscenity", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Mocks the output of translation functions to return predictable values.
|
// Mocks the output of translation functions to return predictable values.
|
||||||
jest.mock("../src/client/Utils", () => ({
|
vi.mock("../src/client/Utils", () => ({
|
||||||
translateText: (key: string, vars?: any) =>
|
translateText: (key: string, vars?: any) =>
|
||||||
vars ? `${key}:${JSON.stringify(vars)}` : key,
|
vars ? `${key}:${JSON.stringify(vars)}` : key,
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ describe("DeleteUnitExecution Security Tests", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should prevent deleting units during spawn phase", () => {
|
it("should prevent deleting units during spawn phase", () => {
|
||||||
jest.spyOn(game, "inSpawnPhase").mockReturnValue(true);
|
vi.spyOn(game, "inSpawnPhase").mockReturnValue(true);
|
||||||
|
|
||||||
const execution = new DeleteUnitExecution(player, unit.id());
|
const execution = new DeleteUnitExecution(player, unit.id());
|
||||||
execution.init(game, 0);
|
execution.init(game, 0);
|
||||||
@@ -122,7 +122,7 @@ describe("DeleteUnitExecution Security Tests", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should allow deleting units when all conditions are met", () => {
|
it("should allow deleting units when all conditions are met", () => {
|
||||||
jest.spyOn(game, "inSpawnPhase").mockReturnValue(false);
|
vi.spyOn(game, "inSpawnPhase").mockReturnValue(false);
|
||||||
|
|
||||||
const execution = new DeleteUnitExecution(player, unit.id());
|
const execution = new DeleteUnitExecution(player, unit.id());
|
||||||
execution.init(game, 0);
|
execution.init(game, 0);
|
||||||
@@ -131,7 +131,7 @@ describe("DeleteUnitExecution Security Tests", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should delete after deletion delay", () => {
|
it("should delete after deletion delay", () => {
|
||||||
jest.spyOn(game, "inSpawnPhase").mockReturnValue(false);
|
vi.spyOn(game, "inSpawnPhase").mockReturnValue(false);
|
||||||
|
|
||||||
const execution = new DeleteUnitExecution(player, unit.id());
|
const execution = new DeleteUnitExecution(player, unit.id());
|
||||||
game.addExecution(execution);
|
game.addExecution(execution);
|
||||||
@@ -144,7 +144,7 @@ describe("DeleteUnitExecution Security Tests", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should reset deletion if captured", () => {
|
it("should reset deletion if captured", () => {
|
||||||
jest.spyOn(game, "inSpawnPhase").mockReturnValue(false);
|
vi.spyOn(game, "inSpawnPhase").mockReturnValue(false);
|
||||||
|
|
||||||
const execution = new DeleteUnitExecution(player, unit.id());
|
const execution = new DeleteUnitExecution(player, unit.id());
|
||||||
game.addExecution(execution);
|
game.addExecution(execution);
|
||||||
|
|||||||
+19
-22
@@ -1,6 +1,3 @@
|
|||||||
/**
|
|
||||||
* @jest-environment jsdom
|
|
||||||
*/
|
|
||||||
import { AutoUpgradeEvent, InputHandler } from "../src/client/InputHandler";
|
import { AutoUpgradeEvent, InputHandler } from "../src/client/InputHandler";
|
||||||
import { EventBus } from "../src/core/EventBus";
|
import { EventBus } from "../src/core/EventBus";
|
||||||
|
|
||||||
@@ -18,7 +15,7 @@ class MockPointerEvent {
|
|||||||
this.clientX = init.clientX;
|
this.clientX = init.clientX;
|
||||||
this.clientY = init.clientY;
|
this.clientY = init.clientY;
|
||||||
this.pointerId = init.pointerId;
|
this.pointerId = init.pointerId;
|
||||||
this.preventDefault = jest.fn();
|
this.preventDefault = vi.fn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +42,7 @@ describe("InputHandler AutoUpgrade", () => {
|
|||||||
|
|
||||||
describe("Middle Mouse Button Handling", () => {
|
describe("Middle Mouse Button Handling", () => {
|
||||||
test("should emit AutoUpgradeEvent on middle mouse button press", () => {
|
test("should emit AutoUpgradeEvent on middle mouse button press", () => {
|
||||||
const mockEmit = jest.spyOn(eventBus, "emit");
|
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||||
|
|
||||||
const pointerEvent = new PointerEvent("pointerdown", {
|
const pointerEvent = new PointerEvent("pointerdown", {
|
||||||
button: 1,
|
button: 1,
|
||||||
@@ -65,7 +62,7 @@ describe("InputHandler AutoUpgrade", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("should emit MouseDownEvent on left mouse button press instead of AutoUpgradeEvent", () => {
|
test("should emit MouseDownEvent on left mouse button press instead of AutoUpgradeEvent", () => {
|
||||||
const mockEmit = jest.spyOn(eventBus, "emit");
|
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||||
|
|
||||||
const pointerEvent = new PointerEvent("pointerdown", {
|
const pointerEvent = new PointerEvent("pointerdown", {
|
||||||
button: 0,
|
button: 0,
|
||||||
@@ -89,7 +86,7 @@ describe("InputHandler AutoUpgrade", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("should not emit AutoUpgradeEvent on right mouse button press", () => {
|
test("should not emit AutoUpgradeEvent on right mouse button press", () => {
|
||||||
const mockEmit = jest.spyOn(eventBus, "emit");
|
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||||
|
|
||||||
const pointerEvent = new PointerEvent("pointerdown", {
|
const pointerEvent = new PointerEvent("pointerdown", {
|
||||||
button: 2,
|
button: 2,
|
||||||
@@ -109,7 +106,7 @@ describe("InputHandler AutoUpgrade", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("should handle multiple middle mouse button presses", () => {
|
test("should handle multiple middle mouse button presses", () => {
|
||||||
const mockEmit = jest.spyOn(eventBus, "emit");
|
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||||
|
|
||||||
const pointerEvent1 = new PointerEvent("pointerdown", {
|
const pointerEvent1 = new PointerEvent("pointerdown", {
|
||||||
button: 1,
|
button: 1,
|
||||||
@@ -145,7 +142,7 @@ describe("InputHandler AutoUpgrade", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("should handle middle mouse button press with zero coordinates", () => {
|
test("should handle middle mouse button press with zero coordinates", () => {
|
||||||
const mockEmit = jest.spyOn(eventBus, "emit");
|
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||||
|
|
||||||
const pointerEvent = new PointerEvent("pointerdown", {
|
const pointerEvent = new PointerEvent("pointerdown", {
|
||||||
button: 1,
|
button: 1,
|
||||||
@@ -165,7 +162,7 @@ describe("InputHandler AutoUpgrade", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("should handle middle mouse button press with negative coordinates", () => {
|
test("should handle middle mouse button press with negative coordinates", () => {
|
||||||
const mockEmit = jest.spyOn(eventBus, "emit");
|
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||||
|
|
||||||
const pointerEvent = new PointerEvent("pointerdown", {
|
const pointerEvent = new PointerEvent("pointerdown", {
|
||||||
button: 1,
|
button: 1,
|
||||||
@@ -185,7 +182,7 @@ describe("InputHandler AutoUpgrade", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("should handle middle mouse button press with decimal coordinates", () => {
|
test("should handle middle mouse button press with decimal coordinates", () => {
|
||||||
const mockEmit = jest.spyOn(eventBus, "emit");
|
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||||
|
|
||||||
const pointerEvent = new PointerEvent("pointerdown", {
|
const pointerEvent = new PointerEvent("pointerdown", {
|
||||||
button: 1,
|
button: 1,
|
||||||
@@ -207,7 +204,7 @@ describe("InputHandler AutoUpgrade", () => {
|
|||||||
|
|
||||||
describe("Pointer Event Handling", () => {
|
describe("Pointer Event Handling", () => {
|
||||||
test("should handle pointer events with different pointer IDs", () => {
|
test("should handle pointer events with different pointer IDs", () => {
|
||||||
const mockEmit = jest.spyOn(eventBus, "emit");
|
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||||
|
|
||||||
const pointerEvent1 = new PointerEvent("pointerdown", {
|
const pointerEvent1 = new PointerEvent("pointerdown", {
|
||||||
button: 1,
|
button: 1,
|
||||||
@@ -229,7 +226,7 @@ describe("InputHandler AutoUpgrade", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("should handle pointer events with same pointer ID", () => {
|
test("should handle pointer events with same pointer ID", () => {
|
||||||
const mockEmit = jest.spyOn(eventBus, "emit");
|
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||||
|
|
||||||
const pointerEvent1 = new PointerEvent("pointerdown", {
|
const pointerEvent1 = new PointerEvent("pointerdown", {
|
||||||
button: 1,
|
button: 1,
|
||||||
@@ -253,7 +250,7 @@ describe("InputHandler AutoUpgrade", () => {
|
|||||||
|
|
||||||
describe("Edge Cases", () => {
|
describe("Edge Cases", () => {
|
||||||
test("should handle very large coordinates", () => {
|
test("should handle very large coordinates", () => {
|
||||||
const mockEmit = jest.spyOn(eventBus, "emit");
|
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||||
|
|
||||||
const pointerEvent = new PointerEvent("pointerdown", {
|
const pointerEvent = new PointerEvent("pointerdown", {
|
||||||
button: 1,
|
button: 1,
|
||||||
@@ -273,7 +270,7 @@ describe("InputHandler AutoUpgrade", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("should handle very small coordinates", () => {
|
test("should handle very small coordinates", () => {
|
||||||
const mockEmit = jest.spyOn(eventBus, "emit");
|
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||||
|
|
||||||
const pointerEvent = new PointerEvent("pointerdown", {
|
const pointerEvent = new PointerEvent("pointerdown", {
|
||||||
button: 1,
|
button: 1,
|
||||||
@@ -293,7 +290,7 @@ describe("InputHandler AutoUpgrade", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("should handle NaN coordinates", () => {
|
test("should handle NaN coordinates", () => {
|
||||||
const mockEmit = jest.spyOn(eventBus, "emit");
|
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||||
|
|
||||||
const pointerEvent = new PointerEvent("pointerdown", {
|
const pointerEvent = new PointerEvent("pointerdown", {
|
||||||
button: 1,
|
button: 1,
|
||||||
@@ -313,7 +310,7 @@ describe("InputHandler AutoUpgrade", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("should handle Infinity coordinates", () => {
|
test("should handle Infinity coordinates", () => {
|
||||||
const mockEmit = jest.spyOn(eventBus, "emit");
|
const mockEmit = vi.spyOn(eventBus, "emit");
|
||||||
|
|
||||||
const pointerEvent = new PointerEvent("pointerdown", {
|
const pointerEvent = new PointerEvent("pointerdown", {
|
||||||
button: 1,
|
button: 1,
|
||||||
@@ -335,7 +332,7 @@ describe("InputHandler AutoUpgrade", () => {
|
|||||||
|
|
||||||
describe("Integration with Event Bus", () => {
|
describe("Integration with Event Bus", () => {
|
||||||
test("should allow event listeners to receive AutoUpgradeEvents", () => {
|
test("should allow event listeners to receive AutoUpgradeEvents", () => {
|
||||||
const mockListener = jest.fn();
|
const mockListener = vi.fn();
|
||||||
|
|
||||||
eventBus.on(AutoUpgradeEvent, mockListener);
|
eventBus.on(AutoUpgradeEvent, mockListener);
|
||||||
|
|
||||||
@@ -356,8 +353,8 @@ describe("InputHandler AutoUpgrade", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("should allow multiple listeners for AutoUpgradeEvent", () => {
|
test("should allow multiple listeners for AutoUpgradeEvent", () => {
|
||||||
const mockListener1 = jest.fn();
|
const mockListener1 = vi.fn();
|
||||||
const mockListener2 = jest.fn();
|
const mockListener2 = vi.fn();
|
||||||
|
|
||||||
eventBus.on(AutoUpgradeEvent, mockListener1);
|
eventBus.on(AutoUpgradeEvent, mockListener1);
|
||||||
eventBus.on(AutoUpgradeEvent, mockListener2);
|
eventBus.on(AutoUpgradeEvent, mockListener2);
|
||||||
@@ -385,7 +382,7 @@ describe("InputHandler AutoUpgrade", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("should not call unsubscribed listeners", () => {
|
test("should not call unsubscribed listeners", () => {
|
||||||
const mockListener = jest.fn();
|
const mockListener = vi.fn();
|
||||||
|
|
||||||
eventBus.on(AutoUpgradeEvent, mockListener);
|
eventBus.on(AutoUpgradeEvent, mockListener);
|
||||||
eventBus.off(AutoUpgradeEvent, mockListener);
|
eventBus.off(AutoUpgradeEvent, mockListener);
|
||||||
@@ -444,7 +441,7 @@ describe("InputHandler AutoUpgrade", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("handles invalid JSON gracefully and warns", () => {
|
test("handles invalid JSON gracefully and warns", () => {
|
||||||
const spy = jest.spyOn(console, "warn").mockImplementation(() => {});
|
const spy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||||
localStorage.setItem("settings.keybinds", "not a json");
|
localStorage.setItem("settings.keybinds", "not a json");
|
||||||
|
|
||||||
inputHandler.initialize();
|
inputHandler.initialize();
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
|
import { vi, type MockInstance } from "vitest";
|
||||||
import { getMessageTypeClasses, severityColors } from "../src/client/Utils";
|
import { getMessageTypeClasses, severityColors } from "../src/client/Utils";
|
||||||
import { MessageType } from "../src/core/game/Game";
|
import { MessageType } from "../src/core/game/Game";
|
||||||
|
|
||||||
describe("getMessageTypeClasses", () => {
|
describe("getMessageTypeClasses", () => {
|
||||||
// Spy on console.warn to track when the default case is hit
|
// Spy on console.warn to track when the default case is hit
|
||||||
let consoleSpy: jest.SpyInstance;
|
let consoleSpy: MockInstance;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
consoleSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
|
consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -77,19 +77,17 @@ describe("AllianceBehavior.handleAllianceRequests", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
jest.spyOn(player, "alliances").mockReturnValue(new Array(alliancesCount));
|
vi.spyOn(player, "alliances").mockReturnValue(new Array(alliancesCount));
|
||||||
|
|
||||||
const mockRequest = {
|
const mockRequest = {
|
||||||
requestor: () => requestor,
|
requestor: () => requestor,
|
||||||
recipient: () => player,
|
recipient: () => player,
|
||||||
createdAt: () => 0 as unknown as Tick,
|
createdAt: () => 0 as unknown as Tick,
|
||||||
accept: jest.fn(),
|
accept: vi.fn(),
|
||||||
reject: jest.fn(),
|
reject: vi.fn(),
|
||||||
} as unknown as AllianceRequest;
|
} as unknown as AllianceRequest;
|
||||||
|
|
||||||
jest
|
vi.spyOn(player, "incomingAllianceRequests").mockReturnValue([mockRequest]);
|
||||||
.spyOn(player, "incomingAllianceRequests")
|
|
||||||
.mockReturnValue([mockRequest]);
|
|
||||||
|
|
||||||
return mockRequest;
|
return mockRequest;
|
||||||
}
|
}
|
||||||
@@ -151,19 +149,19 @@ describe("AllianceBehavior.handleAllianceExtensionRequests", () => {
|
|||||||
let allianceBehavior: NationAllianceBehavior;
|
let allianceBehavior: NationAllianceBehavior;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockGame = { addExecution: jest.fn() };
|
mockGame = { addExecution: vi.fn() };
|
||||||
mockHuman = { id: jest.fn(() => "human_id") };
|
mockHuman = { id: vi.fn(() => "human_id") };
|
||||||
mockAlliance = {
|
mockAlliance = {
|
||||||
onlyOneAgreedToExtend: jest.fn(() => true),
|
onlyOneAgreedToExtend: vi.fn(() => true),
|
||||||
other: jest.fn(() => mockHuman),
|
other: vi.fn(() => mockHuman),
|
||||||
};
|
};
|
||||||
mockRandom = { chance: jest.fn() };
|
mockRandom = { chance: vi.fn() };
|
||||||
|
|
||||||
mockPlayer = {
|
mockPlayer = {
|
||||||
alliances: jest.fn(() => [mockAlliance]),
|
alliances: vi.fn(() => [mockAlliance]),
|
||||||
relation: jest.fn(),
|
relation: vi.fn(),
|
||||||
id: jest.fn(() => "bot_id"),
|
id: vi.fn(() => "bot_id"),
|
||||||
type: jest.fn(() => PlayerType.Nation),
|
type: vi.fn(() => PlayerType.Nation),
|
||||||
};
|
};
|
||||||
|
|
||||||
allianceBehavior = new NationAllianceBehavior(
|
allianceBehavior = new NationAllianceBehavior(
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
/**
|
|
||||||
* @jest-environment jsdom
|
|
||||||
*/
|
|
||||||
import { FluentSlider } from "../../../src/client/components/FluentSlider";
|
import { FluentSlider } from "../../../src/client/components/FluentSlider";
|
||||||
|
|
||||||
// Mock the translateText function
|
// Mock the translateText function
|
||||||
jest.mock("../../../src/client/Utils", () => ({
|
vi.mock("../../../src/client/Utils", () => ({
|
||||||
translateText: jest.fn((key: string) => key),
|
translateText: vi.fn((key: string) => key),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe("FluentSlider", () => {
|
describe("FluentSlider", () => {
|
||||||
@@ -84,7 +81,7 @@ describe("FluentSlider", () => {
|
|||||||
|
|
||||||
describe("Value-Changed Event - CRITICAL FOR BUG FIX", () => {
|
describe("Value-Changed Event - CRITICAL FOR BUG FIX", () => {
|
||||||
it("should dispatch CustomEvent with detail.value (not event.target.value)", async () => {
|
it("should dispatch CustomEvent with detail.value (not event.target.value)", async () => {
|
||||||
const eventSpy = jest.fn();
|
const eventSpy = vi.fn();
|
||||||
slider.addEventListener("value-changed", eventSpy);
|
slider.addEventListener("value-changed", eventSpy);
|
||||||
|
|
||||||
const rangeInput = slider.shadowRoot?.querySelector(
|
const rangeInput = slider.shadowRoot?.querySelector(
|
||||||
@@ -107,7 +104,7 @@ describe("FluentSlider", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should not dispatch event on input, only on change", async () => {
|
it("should not dispatch event on input, only on change", async () => {
|
||||||
const eventSpy = jest.fn();
|
const eventSpy = vi.fn();
|
||||||
slider.addEventListener("value-changed", eventSpy);
|
slider.addEventListener("value-changed", eventSpy);
|
||||||
|
|
||||||
const rangeInput = slider.shadowRoot?.querySelector(
|
const rangeInput = slider.shadowRoot?.querySelector(
|
||||||
@@ -128,7 +125,7 @@ describe("FluentSlider", () => {
|
|||||||
|
|
||||||
it("should work with the handler pattern used in HostLobbyModal", async () => {
|
it("should work with the handler pattern used in HostLobbyModal", async () => {
|
||||||
// This simulates the actual handler code in HostLobbyModal.ts:656-660
|
// This simulates the actual handler code in HostLobbyModal.ts:656-660
|
||||||
const mockHandler = jest.fn((e: Event) => {
|
const mockHandler = vi.fn((e: Event) => {
|
||||||
const customEvent = e as CustomEvent<{ value: number }>;
|
const customEvent = e as CustomEvent<{ value: number }>;
|
||||||
const value = customEvent.detail.value;
|
const value = customEvent.detail.value;
|
||||||
if (isNaN(value) || value < 0 || value > 400) {
|
if (isNaN(value) || value < 0 || value > 400) {
|
||||||
@@ -154,7 +151,7 @@ describe("FluentSlider", () => {
|
|||||||
|
|
||||||
it("should work with the handler pattern used in SinglePlayerModal", async () => {
|
it("should work with the handler pattern used in SinglePlayerModal", async () => {
|
||||||
// This simulates the actual handler code in SinglePlayerModal.ts:444-451
|
// This simulates the actual handler code in SinglePlayerModal.ts:444-451
|
||||||
const mockHandler = jest.fn((e: Event) => {
|
const mockHandler = vi.fn((e: Event) => {
|
||||||
const customEvent = e as CustomEvent<{ value: number }>;
|
const customEvent = e as CustomEvent<{ value: number }>;
|
||||||
const value = customEvent.detail.value;
|
const value = customEvent.detail.value;
|
||||||
if (isNaN(value) || value < 0 || value > 400) {
|
if (isNaN(value) || value < 0 || value > 400) {
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
/**
|
|
||||||
* @jest-environment jsdom
|
|
||||||
*/
|
|
||||||
import { ProgressBar } from "../../../src/client/graphics/ProgressBar";
|
import { ProgressBar } from "../../../src/client/graphics/ProgressBar";
|
||||||
|
|
||||||
describe("ProgressBar", () => {
|
describe("ProgressBar", () => {
|
||||||
@@ -15,9 +12,9 @@ describe("ProgressBar", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should initialize and draw the background", () => {
|
it("should initialize and draw the background", () => {
|
||||||
const spyClearRect = jest.spyOn(ctx, "clearRect");
|
const spyClearRect = vi.spyOn(ctx, "clearRect");
|
||||||
const spyFillRect = jest.spyOn(ctx, "fillRect");
|
const spyFillRect = vi.spyOn(ctx, "fillRect");
|
||||||
const spyFillStyle = jest.spyOn(ctx, "fillStyle", "set");
|
const spyFillStyle = vi.spyOn(ctx, "fillStyle", "set");
|
||||||
const bar = new ProgressBar(["#ff0000", "#00ff00"], ctx, 2, 2, 80, 10, 0.5);
|
const bar = new ProgressBar(["#ff0000", "#00ff00"], ctx, 2, 2, 80, 10, 0.5);
|
||||||
expect(spyClearRect).toHaveBeenCalledWith(0, 0, 82, 12);
|
expect(spyClearRect).toHaveBeenCalledWith(0, 0, 82, 12);
|
||||||
expect(spyFillRect).toHaveBeenCalledWith(1, 1, 80, 10);
|
expect(spyFillRect).toHaveBeenCalledWith(1, 1, 80, 10);
|
||||||
@@ -28,7 +25,7 @@ describe("ProgressBar", () => {
|
|||||||
|
|
||||||
it("should set progress and draw the progress bar", () => {
|
it("should set progress and draw the progress bar", () => {
|
||||||
const bar = new ProgressBar(["#ff0000", "#00ff00"], ctx, 2, 2, 80, 10);
|
const bar = new ProgressBar(["#ff0000", "#00ff00"], ctx, 2, 2, 80, 10);
|
||||||
const spyFillRect = jest.spyOn(ctx, "fillRect");
|
const spyFillRect = vi.spyOn(ctx, "fillRect");
|
||||||
bar.setProgress(0.5);
|
bar.setProgress(0.5);
|
||||||
expect(bar.getProgress()).toBe(0.5);
|
expect(bar.getProgress()).toBe(0.5);
|
||||||
expect(spyFillRect).toHaveBeenCalledWith(
|
expect(spyFillRect).toHaveBeenCalledWith(
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
/**
|
import { vi, type Mock } from "vitest";
|
||||||
* @jest-environment jsdom
|
|
||||||
*/
|
|
||||||
import {
|
import {
|
||||||
attackMenuElement,
|
attackMenuElement,
|
||||||
buildMenuElement,
|
buildMenuElement,
|
||||||
@@ -13,13 +11,15 @@ import { UnitType } from "../../../src/core/game/Game";
|
|||||||
import { TileRef } from "../../../src/core/game/GameMap";
|
import { TileRef } from "../../../src/core/game/GameMap";
|
||||||
import { GameView, PlayerView } from "../../../src/core/game/GameView";
|
import { GameView, PlayerView } from "../../../src/core/game/GameView";
|
||||||
|
|
||||||
jest.mock("../../../src/client/Utils", () => ({
|
vi.mock("../../../src/client/Utils", () => ({
|
||||||
translateText: jest.fn((key: string) => key),
|
translateText: vi.fn((key: string) => key),
|
||||||
renderNumber: jest.fn((num: number) => num.toString()),
|
renderNumber: vi.fn((num: number) => num.toString()),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock("../../../src/client/graphics/layers/BuildMenu", () => {
|
vi.mock("../../../src/client/graphics/layers/BuildMenu", async () => {
|
||||||
const { UnitType } = jest.requireActual("../../../src/core/game/Game");
|
const { UnitType } = await vi.importActual<
|
||||||
|
typeof import("../../../src/core/game/Game")
|
||||||
|
>("../../../src/core/game/Game");
|
||||||
return {
|
return {
|
||||||
flattenedBuildTable: [
|
flattenedBuildTable: [
|
||||||
{
|
{
|
||||||
@@ -68,14 +68,14 @@ jest.mock("../../../src/client/graphics/layers/BuildMenu", () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
jest.mock("nanoid", () => ({
|
vi.mock("nanoid", () => ({
|
||||||
customAlphabet: jest.fn(() => jest.fn(() => "mock-id")),
|
customAlphabet: vi.fn(() => vi.fn(() => "mock-id")),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock("dompurify", () => ({
|
vi.mock("dompurify", () => ({
|
||||||
__esModule: true,
|
__esModule: true,
|
||||||
default: {
|
default: {
|
||||||
sanitize: jest.fn((str: string) => str),
|
sanitize: vi.fn((str: string) => str),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -90,29 +90,29 @@ describe("RadialMenuElements", () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockPlayer = {
|
mockPlayer = {
|
||||||
id: () => 1,
|
id: () => 1,
|
||||||
isAlliedWith: jest.fn(() => false),
|
isAlliedWith: vi.fn(() => false),
|
||||||
isPlayer: jest.fn(() => true),
|
isPlayer: vi.fn(() => true),
|
||||||
} as unknown as PlayerView;
|
} as unknown as PlayerView;
|
||||||
|
|
||||||
mockGame = {
|
mockGame = {
|
||||||
inSpawnPhase: jest.fn(() => false),
|
inSpawnPhase: vi.fn(() => false),
|
||||||
owner: jest.fn(() => mockPlayer),
|
owner: vi.fn(() => mockPlayer),
|
||||||
isLand: jest.fn(() => true),
|
isLand: vi.fn(() => true),
|
||||||
config: jest.fn(() => ({
|
config: vi.fn(() => ({
|
||||||
theme: () => ({
|
theme: () => ({
|
||||||
territoryColor: () => ({
|
territoryColor: () => ({
|
||||||
lighten: () => ({ alpha: () => ({ toRgbString: () => "#fff" }) }),
|
lighten: () => ({ alpha: () => ({ toRgbString: () => "#fff" }) }),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
isUnitDisabled: jest.fn(() => false),
|
isUnitDisabled: vi.fn(() => false),
|
||||||
})),
|
})),
|
||||||
} as unknown as GameView;
|
} as unknown as GameView;
|
||||||
|
|
||||||
mockBuildMenu = {
|
mockBuildMenu = {
|
||||||
canBuildOrUpgrade: jest.fn(() => true),
|
canBuildOrUpgrade: vi.fn(() => true),
|
||||||
cost: jest.fn(() => 100),
|
cost: vi.fn(() => 100),
|
||||||
count: jest.fn(() => 5),
|
count: vi.fn(() => 5),
|
||||||
sendBuildOrUpgrade: jest.fn(),
|
sendBuildOrUpgrade: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
mockPlayerActions = {
|
mockPlayerActions = {
|
||||||
@@ -148,7 +148,7 @@ describe("RadialMenuElements", () => {
|
|||||||
playerPanel: {} as any,
|
playerPanel: {} as any,
|
||||||
chatIntegration: {} as any,
|
chatIntegration: {} as any,
|
||||||
eventBus: {} as any,
|
eventBus: {} as any,
|
||||||
closeMenu: jest.fn(),
|
closeMenu: vi.fn(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -161,19 +161,19 @@ describe("RadialMenuElements", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should be disabled during spawn phase", () => {
|
it("should be disabled during spawn phase", () => {
|
||||||
mockGame.inSpawnPhase = jest.fn(() => true);
|
mockGame.inSpawnPhase = vi.fn(() => true);
|
||||||
expect(attackMenuElement.disabled(mockParams)).toBe(true);
|
expect(attackMenuElement.disabled(mockParams)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be enabled when not in spawn phase", () => {
|
it("should be enabled when not in spawn phase", () => {
|
||||||
mockGame.inSpawnPhase = jest.fn(() => false);
|
mockGame.inSpawnPhase = vi.fn(() => false);
|
||||||
expect(attackMenuElement.disabled(mockParams)).toBe(false);
|
expect(attackMenuElement.disabled(mockParams)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return attack submenu with attack units only", () => {
|
it("should return attack submenu with attack units only", () => {
|
||||||
const enemyPlayer = {
|
const enemyPlayer = {
|
||||||
id: () => 2,
|
id: () => 2,
|
||||||
isPlayer: jest.fn(() => true),
|
isPlayer: vi.fn(() => true),
|
||||||
} as unknown as PlayerView;
|
} as unknown as PlayerView;
|
||||||
mockParams.selected = enemyPlayer;
|
mockParams.selected = enemyPlayer;
|
||||||
|
|
||||||
@@ -203,7 +203,7 @@ describe("RadialMenuElements", () => {
|
|||||||
it("should not include construction units in attack menu", () => {
|
it("should not include construction units in attack menu", () => {
|
||||||
const enemyPlayer = {
|
const enemyPlayer = {
|
||||||
id: () => 2,
|
id: () => 2,
|
||||||
isPlayer: jest.fn(() => true),
|
isPlayer: vi.fn(() => true),
|
||||||
} as unknown as PlayerView;
|
} as unknown as PlayerView;
|
||||||
mockParams.selected = enemyPlayer;
|
mockParams.selected = enemyPlayer;
|
||||||
|
|
||||||
@@ -237,12 +237,12 @@ describe("RadialMenuElements", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should be disabled during spawn phase", () => {
|
it("should be disabled during spawn phase", () => {
|
||||||
mockGame.inSpawnPhase = jest.fn(() => true);
|
mockGame.inSpawnPhase = vi.fn(() => true);
|
||||||
expect(buildMenuElement.disabled(mockParams)).toBe(true);
|
expect(buildMenuElement.disabled(mockParams)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be enabled when not in spawn phase", () => {
|
it("should be enabled when not in spawn phase", () => {
|
||||||
mockGame.inSpawnPhase = jest.fn(() => false);
|
mockGame.inSpawnPhase = vi.fn(() => false);
|
||||||
expect(buildMenuElement.disabled(mockParams)).toBe(false);
|
expect(buildMenuElement.disabled(mockParams)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -313,9 +313,9 @@ describe("RadialMenuElements", () => {
|
|||||||
it("should show attack and boat menu on enemy territory", () => {
|
it("should show attack and boat menu on enemy territory", () => {
|
||||||
const enemyPlayer = {
|
const enemyPlayer = {
|
||||||
id: () => 2,
|
id: () => 2,
|
||||||
isPlayer: jest.fn(() => true),
|
isPlayer: vi.fn(() => true),
|
||||||
} as unknown as PlayerView;
|
} as unknown as PlayerView;
|
||||||
mockGame.owner = jest.fn(() => enemyPlayer);
|
mockGame.owner = vi.fn(() => enemyPlayer);
|
||||||
|
|
||||||
const subMenu = rootMenuElement.subMenu!(mockParams);
|
const subMenu = rootMenuElement.subMenu!(mockParams);
|
||||||
const buildMenu = subMenu.find((item) => item.id === Slot.Build);
|
const buildMenu = subMenu.find((item) => item.id === Slot.Build);
|
||||||
@@ -337,8 +337,8 @@ describe("RadialMenuElements", () => {
|
|||||||
it("should handle ally menu correctly", () => {
|
it("should handle ally menu correctly", () => {
|
||||||
const allyPlayer = {
|
const allyPlayer = {
|
||||||
id: () => 2,
|
id: () => 2,
|
||||||
isAlliedWith: jest.fn(() => true),
|
isAlliedWith: vi.fn(() => true),
|
||||||
isPlayer: jest.fn(() => true),
|
isPlayer: vi.fn(() => true),
|
||||||
} as unknown as PlayerView;
|
} as unknown as PlayerView;
|
||||||
mockParams.selected = allyPlayer;
|
mockParams.selected = allyPlayer;
|
||||||
|
|
||||||
@@ -367,7 +367,7 @@ describe("RadialMenuElements", () => {
|
|||||||
it("should execute attack action correctly", () => {
|
it("should execute attack action correctly", () => {
|
||||||
const enemyPlayer = {
|
const enemyPlayer = {
|
||||||
id: () => 2,
|
id: () => 2,
|
||||||
isPlayer: jest.fn(() => true),
|
isPlayer: vi.fn(() => true),
|
||||||
} as unknown as PlayerView;
|
} as unknown as PlayerView;
|
||||||
mockParams.selected = enemyPlayer;
|
mockParams.selected = enemyPlayer;
|
||||||
|
|
||||||
@@ -389,7 +389,7 @@ describe("RadialMenuElements", () => {
|
|||||||
|
|
||||||
it("should not execute action when buildable unit is not found", () => {
|
it("should not execute action when buildable unit is not found", () => {
|
||||||
mockPlayerActions.buildableUnits = [];
|
mockPlayerActions.buildableUnits = [];
|
||||||
mockBuildMenu.canBuildOrUpgrade = jest.fn(() => false);
|
mockBuildMenu.canBuildOrUpgrade = vi.fn(() => false);
|
||||||
|
|
||||||
const subMenu = buildMenuElement.subMenu!(mockParams);
|
const subMenu = buildMenuElement.subMenu!(mockParams);
|
||||||
const cityElement = subMenu.find((item) => item.id === "build_City");
|
const cityElement = subMenu.find((item) => item.id === "build_City");
|
||||||
@@ -420,7 +420,7 @@ describe("RadialMenuElements", () => {
|
|||||||
it("should generate correct tooltip items for attack elements", () => {
|
it("should generate correct tooltip items for attack elements", () => {
|
||||||
const enemyPlayer = {
|
const enemyPlayer = {
|
||||||
id: () => 2,
|
id: () => 2,
|
||||||
isPlayer: jest.fn(() => true),
|
isPlayer: vi.fn(() => true),
|
||||||
} as unknown as PlayerView;
|
} as unknown as PlayerView;
|
||||||
mockParams.selected = enemyPlayer;
|
mockParams.selected = enemyPlayer;
|
||||||
|
|
||||||
@@ -452,7 +452,7 @@ describe("RadialMenuElements", () => {
|
|||||||
it("should use correct colors for attack elements", () => {
|
it("should use correct colors for attack elements", () => {
|
||||||
const enemyPlayer = {
|
const enemyPlayer = {
|
||||||
id: () => 2,
|
id: () => 2,
|
||||||
isPlayer: jest.fn(() => true),
|
isPlayer: vi.fn(() => true),
|
||||||
} as unknown as PlayerView;
|
} as unknown as PlayerView;
|
||||||
mockParams.selected = enemyPlayer;
|
mockParams.selected = enemyPlayer;
|
||||||
|
|
||||||
@@ -465,7 +465,7 @@ describe("RadialMenuElements", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should not set color when element is disabled", () => {
|
it("should not set color when element is disabled", () => {
|
||||||
mockBuildMenu.canBuildOrUpgrade = jest.fn(() => false);
|
mockBuildMenu.canBuildOrUpgrade = vi.fn(() => false);
|
||||||
|
|
||||||
const subMenu = buildMenuElement.subMenu!(mockParams);
|
const subMenu = buildMenuElement.subMenu!(mockParams);
|
||||||
const cityElement = subMenu.find((item) => item.id === "build_City");
|
const cityElement = subMenu.find((item) => item.id === "build_City");
|
||||||
@@ -475,10 +475,12 @@ describe("RadialMenuElements", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("Translation integration", () => {
|
describe("Translation integration", () => {
|
||||||
it("should use translateText for tooltip items in build menu", () => {
|
it("should use translateText for tooltip items in build menu", async () => {
|
||||||
const { translateText } = jest.requireMock("../../../src/client/Utils");
|
const { translateText } = await vi.importMock<
|
||||||
|
typeof import("../../../src/client/Utils")
|
||||||
|
>("../../../src/client/Utils");
|
||||||
|
|
||||||
(translateText as jest.Mock).mockClear();
|
(translateText as Mock).mockClear();
|
||||||
|
|
||||||
buildMenuElement.subMenu!(mockParams);
|
buildMenuElement.subMenu!(mockParams);
|
||||||
|
|
||||||
@@ -488,14 +490,16 @@ describe("RadialMenuElements", () => {
|
|||||||
expect(translateText).toHaveBeenCalledWith("unit_type.factory_desc");
|
expect(translateText).toHaveBeenCalledWith("unit_type.factory_desc");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should use translateText for tooltip items in attack menu", () => {
|
it("should use translateText for tooltip items in attack menu", async () => {
|
||||||
const { translateText } = jest.requireMock("../../../src/client/Utils");
|
const { translateText } = await vi.importMock<
|
||||||
|
typeof import("../../../src/client/Utils")
|
||||||
|
>("../../../src/client/Utils");
|
||||||
|
|
||||||
(translateText as jest.Mock).mockClear();
|
(translateText as Mock).mockClear();
|
||||||
|
|
||||||
const enemyPlayer = {
|
const enemyPlayer = {
|
||||||
id: () => 2,
|
id: () => 2,
|
||||||
isPlayer: jest.fn(() => true),
|
isPlayer: vi.fn(() => true),
|
||||||
} as unknown as PlayerView;
|
} as unknown as PlayerView;
|
||||||
mockParams.selected = enemyPlayer;
|
mockParams.selected = enemyPlayer;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
/**
|
|
||||||
* @jest-environment jsdom
|
|
||||||
*/
|
|
||||||
import { UILayer } from "../../../src/client/graphics/layers/UILayer";
|
import { UILayer } from "../../../src/client/graphics/layers/UILayer";
|
||||||
import { UnitSelectionEvent } from "../../../src/client/InputHandler";
|
import { UnitSelectionEvent } from "../../../src/client/InputHandler";
|
||||||
import { UnitView } from "../../../src/core/game/GameView";
|
import { UnitView } from "../../../src/core/game/GameView";
|
||||||
@@ -28,7 +25,7 @@ describe("UILayer", () => {
|
|||||||
ticks: () => 1,
|
ticks: () => 1,
|
||||||
updatesSinceLastTick: () => undefined,
|
updatesSinceLastTick: () => undefined,
|
||||||
};
|
};
|
||||||
eventBus = { on: jest.fn() };
|
eventBus = { on: vi.fn() };
|
||||||
transformHandler = {};
|
transformHandler = {};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -50,7 +47,7 @@ describe("UILayer", () => {
|
|||||||
owner: () => ({}),
|
owner: () => ({}),
|
||||||
};
|
};
|
||||||
const event = { isSelected: true, unit };
|
const event = { isSelected: true, unit };
|
||||||
ui.drawSelectionBox = jest.fn();
|
ui.drawSelectionBox = vi.fn();
|
||||||
ui["onUnitSelection"](event as UnitSelectionEvent);
|
ui["onUnitSelection"](event as UnitSelectionEvent);
|
||||||
expect(ui.drawSelectionBox).toHaveBeenCalledWith(unit);
|
expect(ui.drawSelectionBox).toHaveBeenCalledWith(unit);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
jest.mock("lit", () => ({
|
vi.mock("lit", () => ({
|
||||||
html: () => {},
|
html: () => {},
|
||||||
LitElement: class {},
|
LitElement: class {},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock("lit/decorators.js", () => ({
|
vi.mock("lit/decorators.js", () => ({
|
||||||
customElement: () => (clazz: any) => clazz,
|
customElement: () => (clazz: any) => clazz,
|
||||||
query: () => () => {},
|
query: () => () => {},
|
||||||
state: () => () => {},
|
state: () => () => {},
|
||||||
property: () => () => {},
|
property: () => () => {},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock("lit/directive.js", () => ({
|
vi.mock("lit/directive.js", () => ({
|
||||||
DirectiveResult: class {},
|
DirectiveResult: class {},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock("lit/directives/unsafe-html.js", () => ({
|
vi.mock("lit/directives/unsafe-html.js", () => ({
|
||||||
unsafeHTML: () => {},
|
unsafeHTML: () => {},
|
||||||
UnsafeHTMLDirective: class {},
|
UnsafeHTMLDirective: class {},
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -28,11 +28,11 @@ describe("NukeExecution", () => {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
(game.config() as TestConfig).nukeMagnitudes = jest.fn(() => ({
|
(game.config() as TestConfig).nukeMagnitudes = vi.fn(() => ({
|
||||||
inner: 10,
|
inner: 10,
|
||||||
outer: 10,
|
outer: 10,
|
||||||
}));
|
}));
|
||||||
(game.config() as TestConfig).nukeAllianceBreakThreshold = jest.fn(() => 5);
|
(game.config() as TestConfig).nukeAllianceBreakThreshold = vi.fn(() => 5);
|
||||||
|
|
||||||
while (game.inSpawnPhase()) {
|
while (game.inSpawnPhase()) {
|
||||||
game.executeNextTick();
|
game.executeNextTick();
|
||||||
@@ -51,14 +51,14 @@ describe("NukeExecution", () => {
|
|||||||
player.buildUnit(UnitType.MissileSilo, game.ref(1, 10), {});
|
player.buildUnit(UnitType.MissileSilo, game.ref(1, 10), {});
|
||||||
// Build a SAM out of range
|
// Build a SAM out of range
|
||||||
const sam = player.buildUnit(UnitType.SAMLauncher, game.ref(1, 11), {});
|
const sam = player.buildUnit(UnitType.SAMLauncher, game.ref(1, 11), {});
|
||||||
sam.touch = jest.fn();
|
sam.touch = vi.fn();
|
||||||
// Build a Defense post out of range AND out of redraw range
|
// Build a Defense post out of range AND out of redraw range
|
||||||
const defensePost = player.buildUnit(
|
const defensePost = player.buildUnit(
|
||||||
UnitType.DefensePost,
|
UnitType.DefensePost,
|
||||||
game.ref(1, 27),
|
game.ref(1, 27),
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
defensePost.touch = jest.fn();
|
defensePost.touch = vi.fn();
|
||||||
// Add a nuke execution targeting the city
|
// Add a nuke execution targeting the city
|
||||||
const nukeExec = new NukeExecution(
|
const nukeExec = new NukeExecution(
|
||||||
UnitType.AtomBomb,
|
UnitType.AtomBomb,
|
||||||
|
|||||||
@@ -20,70 +20,70 @@ describe("TradeShipExecution", () => {
|
|||||||
infiniteGold: true,
|
infiniteGold: true,
|
||||||
instantBuild: true,
|
instantBuild: true,
|
||||||
});
|
});
|
||||||
game.displayMessage = jest.fn();
|
game.displayMessage = vi.fn();
|
||||||
origOwner = {
|
origOwner = {
|
||||||
canBuild: jest.fn(() => true),
|
canBuild: vi.fn(() => true),
|
||||||
buildUnit: jest.fn((type, spawn, opts) => tradeShip),
|
buildUnit: vi.fn((type, spawn, opts) => tradeShip),
|
||||||
displayName: jest.fn(() => "Origin"),
|
displayName: vi.fn(() => "Origin"),
|
||||||
addGold: jest.fn(),
|
addGold: vi.fn(),
|
||||||
units: jest.fn(() => [dstPort]),
|
units: vi.fn(() => [dstPort]),
|
||||||
unitCount: jest.fn(() => 1),
|
unitCount: vi.fn(() => 1),
|
||||||
id: jest.fn(() => 1),
|
id: vi.fn(() => 1),
|
||||||
clientID: jest.fn(() => 1),
|
clientID: vi.fn(() => 1),
|
||||||
canTrade: jest.fn(() => true),
|
canTrade: vi.fn(() => true),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
dstOwner = {
|
dstOwner = {
|
||||||
id: jest.fn(() => 2),
|
id: vi.fn(() => 2),
|
||||||
addGold: jest.fn(),
|
addGold: vi.fn(),
|
||||||
displayName: jest.fn(() => "Destination"),
|
displayName: vi.fn(() => "Destination"),
|
||||||
units: jest.fn(() => [dstPort]),
|
units: vi.fn(() => [dstPort]),
|
||||||
unitCount: jest.fn(() => 1),
|
unitCount: vi.fn(() => 1),
|
||||||
clientID: jest.fn(() => 2),
|
clientID: vi.fn(() => 2),
|
||||||
canTrade: jest.fn(() => true),
|
canTrade: vi.fn(() => true),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
pirate = {
|
pirate = {
|
||||||
id: jest.fn(() => 3),
|
id: vi.fn(() => 3),
|
||||||
addGold: jest.fn(),
|
addGold: vi.fn(),
|
||||||
displayName: jest.fn(() => "Destination"),
|
displayName: vi.fn(() => "Destination"),
|
||||||
units: jest.fn(() => [piratePort]),
|
units: vi.fn(() => [piratePort]),
|
||||||
unitCount: jest.fn(() => 1),
|
unitCount: vi.fn(() => 1),
|
||||||
canTrade: jest.fn(() => true),
|
canTrade: vi.fn(() => true),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
piratePort = {
|
piratePort = {
|
||||||
tile: jest.fn(() => 40011),
|
tile: vi.fn(() => 40011),
|
||||||
owner: jest.fn(() => pirate),
|
owner: vi.fn(() => pirate),
|
||||||
isActive: jest.fn(() => true),
|
isActive: vi.fn(() => true),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
srcPort = {
|
srcPort = {
|
||||||
tile: jest.fn(() => 20011),
|
tile: vi.fn(() => 20011),
|
||||||
owner: jest.fn(() => origOwner),
|
owner: vi.fn(() => origOwner),
|
||||||
isActive: jest.fn(() => true),
|
isActive: vi.fn(() => true),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
dstPort = {
|
dstPort = {
|
||||||
tile: jest.fn(() => 30015), // 15x15
|
tile: vi.fn(() => 30015), // 15x15
|
||||||
owner: jest.fn(() => dstOwner),
|
owner: vi.fn(() => dstOwner),
|
||||||
isActive: jest.fn(() => true),
|
isActive: vi.fn(() => true),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
tradeShip = {
|
tradeShip = {
|
||||||
isActive: jest.fn(() => true),
|
isActive: vi.fn(() => true),
|
||||||
owner: jest.fn(() => origOwner),
|
owner: vi.fn(() => origOwner),
|
||||||
move: jest.fn(),
|
move: vi.fn(),
|
||||||
setTargetUnit: jest.fn(),
|
setTargetUnit: vi.fn(),
|
||||||
setSafeFromPirates: jest.fn(),
|
setSafeFromPirates: vi.fn(),
|
||||||
delete: jest.fn(),
|
delete: vi.fn(),
|
||||||
tile: jest.fn(() => 2001),
|
tile: vi.fn(() => 2001),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
tradeShipExecution = new TradeShipExecution(origOwner, srcPort, dstPort);
|
tradeShipExecution = new TradeShipExecution(origOwner, srcPort, dstPort);
|
||||||
tradeShipExecution.init(game, 0);
|
tradeShipExecution.init(game, 0);
|
||||||
tradeShipExecution["pathFinder"] = {
|
tradeShipExecution["pathFinder"] = {
|
||||||
nextTile: jest.fn(() => ({ type: 0, node: 2001 })),
|
nextTile: vi.fn(() => ({ type: 0, node: 2001 })),
|
||||||
} as any;
|
} as any;
|
||||||
tradeShipExecution["tradeShip"] = tradeShip;
|
tradeShipExecution["tradeShip"] = tradeShip;
|
||||||
});
|
});
|
||||||
@@ -94,27 +94,27 @@ describe("TradeShipExecution", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should deactivate if tradeShip is not active", () => {
|
it("should deactivate if tradeShip is not active", () => {
|
||||||
tradeShip.isActive = jest.fn(() => false);
|
tradeShip.isActive = vi.fn(() => false);
|
||||||
tradeShipExecution.tick(1);
|
tradeShipExecution.tick(1);
|
||||||
expect(tradeShipExecution.isActive()).toBe(false);
|
expect(tradeShipExecution.isActive()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should delete ship if port owner changes to current owner", () => {
|
it("should delete ship if port owner changes to current owner", () => {
|
||||||
dstPort.owner = jest.fn(() => origOwner);
|
dstPort.owner = vi.fn(() => origOwner);
|
||||||
tradeShipExecution.tick(1);
|
tradeShipExecution.tick(1);
|
||||||
expect(tradeShip.delete).toHaveBeenCalledWith(false);
|
expect(tradeShip.delete).toHaveBeenCalledWith(false);
|
||||||
expect(tradeShipExecution.isActive()).toBe(false);
|
expect(tradeShipExecution.isActive()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should pick another port if ship is captured", () => {
|
it("should pick another port if ship is captured", () => {
|
||||||
tradeShip.owner = jest.fn(() => pirate);
|
tradeShip.owner = vi.fn(() => pirate);
|
||||||
tradeShipExecution.tick(1);
|
tradeShipExecution.tick(1);
|
||||||
expect(tradeShip.setTargetUnit).toHaveBeenCalledWith(piratePort);
|
expect(tradeShip.setTargetUnit).toHaveBeenCalledWith(piratePort);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should complete trade and award gold", () => {
|
it("should complete trade and award gold", () => {
|
||||||
tradeShipExecution["pathFinder"] = {
|
tradeShipExecution["pathFinder"] = {
|
||||||
nextTile: jest.fn(() => ({ type: 2, node: 2001 })),
|
nextTile: vi.fn(() => ({ type: 2, node: 2001 })),
|
||||||
} as any;
|
} as any;
|
||||||
tradeShipExecution.tick(1);
|
tradeShipExecution.tick(1);
|
||||||
expect(tradeShip.delete).toHaveBeenCalledWith(false);
|
expect(tradeShip.delete).toHaveBeenCalledWith(false);
|
||||||
|
|||||||
@@ -13,52 +13,52 @@ describe("WinCheckExecution", () => {
|
|||||||
maxTimerValue: 5,
|
maxTimerValue: 5,
|
||||||
instantBuild: true,
|
instantBuild: true,
|
||||||
});
|
});
|
||||||
mg.setWinner = jest.fn();
|
mg.setWinner = vi.fn();
|
||||||
winCheck = new WinCheckExecution();
|
winCheck = new WinCheckExecution();
|
||||||
winCheck.init(mg, 0);
|
winCheck.init(mg, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should call checkWinnerFFA in FFA mode", () => {
|
it("should call checkWinnerFFA in FFA mode", () => {
|
||||||
const spy = jest.spyOn(winCheck as any, "checkWinnerFFA");
|
const spy = vi.spyOn(winCheck as any, "checkWinnerFFA");
|
||||||
winCheck.tick(10);
|
winCheck.tick(10);
|
||||||
expect(spy).toHaveBeenCalled();
|
expect(spy).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should call checkWinnerTeam in non-FFA mode", () => {
|
it("should call checkWinnerTeam in non-FFA mode", () => {
|
||||||
mg.config = jest.fn(() => ({
|
mg.config = vi.fn(() => ({
|
||||||
gameConfig: jest.fn(() => ({
|
gameConfig: vi.fn(() => ({
|
||||||
maxTimerValue: 5,
|
maxTimerValue: 5,
|
||||||
gameMode: GameMode.Team,
|
gameMode: GameMode.Team,
|
||||||
})),
|
})),
|
||||||
percentageTilesOwnedToWin: jest.fn(() => 50),
|
percentageTilesOwnedToWin: vi.fn(() => 50),
|
||||||
}));
|
}));
|
||||||
winCheck.init(mg, 0);
|
winCheck.init(mg, 0);
|
||||||
const spy = jest.spyOn(winCheck as any, "checkWinnerTeam");
|
const spy = vi.spyOn(winCheck as any, "checkWinnerTeam");
|
||||||
winCheck.tick(10);
|
winCheck.tick(10);
|
||||||
expect(spy).toHaveBeenCalled();
|
expect(spy).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should set winner in FFA if percentage is reached", () => {
|
it("should set winner in FFA if percentage is reached", () => {
|
||||||
const player = {
|
const player = {
|
||||||
numTilesOwned: jest.fn(() => 81),
|
numTilesOwned: vi.fn(() => 81),
|
||||||
name: jest.fn(() => "P1"),
|
name: vi.fn(() => "P1"),
|
||||||
};
|
};
|
||||||
mg.players = jest.fn(() => [player]);
|
mg.players = vi.fn(() => [player]);
|
||||||
mg.numLandTiles = jest.fn(() => 100);
|
mg.numLandTiles = vi.fn(() => 100);
|
||||||
mg.numTilesWithFallout = jest.fn(() => 0);
|
mg.numTilesWithFallout = vi.fn(() => 0);
|
||||||
winCheck.checkWinnerFFA();
|
winCheck.checkWinnerFFA();
|
||||||
expect(mg.setWinner).toHaveBeenCalledWith(player, expect.anything());
|
expect(mg.setWinner).toHaveBeenCalledWith(player, expect.anything());
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should set winner in FFA if timer is 0", () => {
|
it("should set winner in FFA if timer is 0", () => {
|
||||||
const player = {
|
const player = {
|
||||||
numTilesOwned: jest.fn(() => 10),
|
numTilesOwned: vi.fn(() => 10),
|
||||||
name: jest.fn(() => "P1"),
|
name: vi.fn(() => "P1"),
|
||||||
};
|
};
|
||||||
mg.players = jest.fn(() => [player]);
|
mg.players = vi.fn(() => [player]);
|
||||||
mg.numLandTiles = jest.fn(() => 100);
|
mg.numLandTiles = vi.fn(() => 100);
|
||||||
mg.numTilesWithFallout = jest.fn(() => 0);
|
mg.numTilesWithFallout = vi.fn(() => 0);
|
||||||
mg.stats = jest.fn(() => ({ stats: () => ({ mocked: true }) }));
|
mg.stats = vi.fn(() => ({ stats: () => ({ mocked: true }) }));
|
||||||
// Advance ticks until timeElapsed (in seconds) >= maxTimerValue * 60
|
// Advance ticks until timeElapsed (in seconds) >= maxTimerValue * 60
|
||||||
// timeElapsed = (ticks - numSpawnPhaseTurns) / 10 =>
|
// timeElapsed = (ticks - numSpawnPhaseTurns) / 10 =>
|
||||||
// ticks >= numSpawnPhaseTurns + maxTimerValue * 600
|
// ticks >= numSpawnPhaseTurns + maxTimerValue * 600
|
||||||
@@ -73,7 +73,7 @@ describe("WinCheckExecution", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should not set winner if no players", () => {
|
it("should not set winner if no players", () => {
|
||||||
mg.players = jest.fn(() => []);
|
mg.players = vi.fn(() => []);
|
||||||
winCheck.checkWinnerFFA();
|
winCheck.checkWinnerFFA();
|
||||||
expect(mg.setWinner).not.toHaveBeenCalled();
|
expect(mg.setWinner).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
|
import { vi, type Mocked } from "vitest";
|
||||||
import { Cluster, TrainStation } from "../../../src/core/game/TrainStation";
|
import { Cluster, TrainStation } from "../../../src/core/game/TrainStation";
|
||||||
|
|
||||||
const createMockStation = (id: string): jest.Mocked<TrainStation> => {
|
const createMockStation = (id: string): Mocked<TrainStation> => {
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
setCluster: jest.fn(),
|
setCluster: vi.fn(),
|
||||||
getCluster: jest.fn(() => null),
|
getCluster: vi.fn(() => null),
|
||||||
} as any;
|
} as any;
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("Cluster tests", () => {
|
describe("Cluster tests", () => {
|
||||||
let cluster: Cluster;
|
let cluster: Cluster;
|
||||||
let stationA: jest.Mocked<TrainStation>;
|
let stationA: Mocked<TrainStation>;
|
||||||
let stationB: jest.Mocked<TrainStation>;
|
let stationB: Mocked<TrainStation>;
|
||||||
let stationC: jest.Mocked<TrainStation>;
|
let stationC: Mocked<TrainStation>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cluster = new Cluster();
|
cluster = new Cluster();
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ describe("GameImpl", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Don't become traitor when betraying inactive player", async () => {
|
test("Don't become traitor when betraying inactive player", async () => {
|
||||||
jest.spyOn(attacker, "canSendAllianceRequest").mockReturnValue(true);
|
vi.spyOn(attacker, "canSendAllianceRequest").mockReturnValue(true);
|
||||||
game.addExecution(new AllianceRequestExecution(attacker, defender.id()));
|
game.addExecution(new AllianceRequestExecution(attacker, defender.id()));
|
||||||
|
|
||||||
game.executeNextTick();
|
game.executeNextTick();
|
||||||
@@ -106,7 +106,7 @@ describe("GameImpl", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Do become traitor when betraying active player", async () => {
|
test("Do become traitor when betraying active player", async () => {
|
||||||
jest.spyOn(attacker, "canSendAllianceRequest").mockReturnValue(true);
|
vi.spyOn(attacker, "canSendAllianceRequest").mockReturnValue(true);
|
||||||
game.addExecution(new AllianceRequestExecution(attacker, defender.id()));
|
game.addExecution(new AllianceRequestExecution(attacker, defender.id()));
|
||||||
|
|
||||||
game.executeNextTick();
|
game.executeNextTick();
|
||||||
|
|||||||
@@ -13,15 +13,15 @@ const createMockStation = (unitId: number): any => {
|
|||||||
return {
|
return {
|
||||||
unit: {
|
unit: {
|
||||||
id: unitId,
|
id: unitId,
|
||||||
setTrainStation: jest.fn(),
|
setTrainStation: vi.fn(),
|
||||||
},
|
},
|
||||||
tile: jest.fn(),
|
tile: vi.fn(),
|
||||||
neighbors: jest.fn(() => []),
|
neighbors: vi.fn(() => []),
|
||||||
getCluster: jest.fn(() => cluster),
|
getCluster: vi.fn(() => cluster),
|
||||||
setCluster: jest.fn(),
|
setCluster: vi.fn(),
|
||||||
addRailroad: jest.fn(),
|
addRailroad: vi.fn(),
|
||||||
getRailroads: jest.fn(() => railroads),
|
getRailroads: vi.fn(() => railroads),
|
||||||
clearRailroads: jest.fn(),
|
clearRailroads: vi.fn(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -54,18 +54,18 @@ describe("RailNetworkImpl", () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
stationManager = {
|
stationManager = {
|
||||||
addStation: jest.fn(),
|
addStation: vi.fn(),
|
||||||
removeStation: jest.fn(),
|
removeStation: vi.fn(),
|
||||||
findStation: jest.fn(),
|
findStation: vi.fn(),
|
||||||
getAll: jest.fn(() => new Set()),
|
getAll: vi.fn(() => new Set()),
|
||||||
};
|
};
|
||||||
pathService = {
|
pathService = {
|
||||||
findTilePath: jest.fn(() => [0]),
|
findTilePath: vi.fn(() => [0]),
|
||||||
findStationsPath: jest.fn(() => [0]),
|
findStationsPath: vi.fn(() => [0]),
|
||||||
};
|
};
|
||||||
game = {
|
game = {
|
||||||
nearbyUnits: jest.fn(() => []),
|
nearbyUnits: vi.fn(() => []),
|
||||||
addExecution: jest.fn(),
|
addExecution: vi.fn(),
|
||||||
config: () => ({
|
config: () => ({
|
||||||
trainStationMaxRange: () => 80,
|
trainStationMaxRange: () => 80,
|
||||||
trainStationMinRange: () => 10,
|
trainStationMinRange: () => 10,
|
||||||
@@ -86,7 +86,7 @@ describe("RailNetworkImpl", () => {
|
|||||||
network.connectStation(stationA);
|
network.connectStation(stationA);
|
||||||
|
|
||||||
const cluster = stationB.getCluster();
|
const cluster = stationB.getCluster();
|
||||||
cluster.addStation = jest.fn();
|
cluster.addStation = vi.fn();
|
||||||
expect(cluster.addStation).not.toHaveBeenCalled();
|
expect(cluster.addStation).not.toHaveBeenCalled();
|
||||||
|
|
||||||
pathService.findTilePath.mockReturnValue(new Array(200));
|
pathService.findTilePath.mockReturnValue(new Array(200));
|
||||||
@@ -95,9 +95,9 @@ describe("RailNetworkImpl", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("removeStation removes all neighbor links", () => {
|
test("removeStation removes all neighbor links", () => {
|
||||||
const neighbor = { removeNeighboringRails: jest.fn() };
|
const neighbor = { removeNeighboringRails: vi.fn() };
|
||||||
const station = createMockStation(1);
|
const station = createMockStation(1);
|
||||||
station.neighbors = jest.fn(() => [neighbor]);
|
station.neighbors = vi.fn(() => [neighbor]);
|
||||||
stationManager.findStation.mockReturnValue(station);
|
stationManager.findStation.mockReturnValue(station);
|
||||||
network.removeStation(station);
|
network.removeStation(station);
|
||||||
expect(station.clearRailroads).toHaveBeenCalled();
|
expect(station.clearRailroads).toHaveBeenCalled();
|
||||||
@@ -119,9 +119,9 @@ describe("RailNetworkImpl", () => {
|
|||||||
const cluster = new Cluster();
|
const cluster = new Cluster();
|
||||||
const neighbor = createMockStation(1);
|
const neighbor = createMockStation(1);
|
||||||
const station = createMockStation(2);
|
const station = createMockStation(2);
|
||||||
station.getCluster = jest.fn(() => cluster);
|
station.getCluster = vi.fn(() => cluster);
|
||||||
station.neighbors = jest.fn(() => [neighbor]);
|
station.neighbors = vi.fn(() => [neighbor]);
|
||||||
cluster.removeStation = jest.fn();
|
cluster.removeStation = vi.fn();
|
||||||
|
|
||||||
stationManager.findStation.mockReturnValue(station);
|
stationManager.findStation.mockReturnValue(station);
|
||||||
|
|
||||||
@@ -150,8 +150,8 @@ describe("RailNetworkImpl", () => {
|
|||||||
const neighborStation = createMockStation(2);
|
const neighborStation = createMockStation(2);
|
||||||
const cluster = new Cluster();
|
const cluster = new Cluster();
|
||||||
cluster.addStation(neighborStation);
|
cluster.addStation(neighborStation);
|
||||||
neighborStation.getCluster = jest.fn(() => cluster);
|
neighborStation.getCluster = vi.fn(() => cluster);
|
||||||
cluster.has = jest.fn(() => false);
|
cluster.has = vi.fn(() => false);
|
||||||
|
|
||||||
const neighborUnit = { unit: neighborStation.unit, distSquared: 20 };
|
const neighborUnit = { unit: neighborStation.unit, distSquared: 20 };
|
||||||
|
|
||||||
|
|||||||
@@ -1,47 +1,48 @@
|
|||||||
|
import { vi, type Mocked } from "vitest";
|
||||||
import { TrainExecution } from "../../../src/core/execution/TrainExecution";
|
import { TrainExecution } from "../../../src/core/execution/TrainExecution";
|
||||||
import { Game, Player, Unit, UnitType } from "../../../src/core/game/Game";
|
import { Game, Player, Unit, UnitType } from "../../../src/core/game/Game";
|
||||||
import { Cluster, TrainStation } from "../../../src/core/game/TrainStation";
|
import { Cluster, TrainStation } from "../../../src/core/game/TrainStation";
|
||||||
|
|
||||||
jest.mock("../../../src/core/game/Game");
|
vi.mock("../../../src/core/game/Game");
|
||||||
jest.mock("../../../src/core/execution/TrainExecution");
|
vi.mock("../../../src/core/execution/TrainExecution");
|
||||||
jest.mock("../../../src/core/PseudoRandom");
|
vi.mock("../../../src/core/PseudoRandom");
|
||||||
|
|
||||||
describe("TrainStation", () => {
|
describe("TrainStation", () => {
|
||||||
let game: jest.Mocked<Game>;
|
let game: Mocked<Game>;
|
||||||
let unit: jest.Mocked<Unit>;
|
let unit: Mocked<Unit>;
|
||||||
let player: jest.Mocked<Player>;
|
let player: Mocked<Player>;
|
||||||
let trainExecution: jest.Mocked<TrainExecution>;
|
let trainExecution: Mocked<TrainExecution>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
game = {
|
game = {
|
||||||
ticks: jest.fn().mockReturnValue(123),
|
ticks: vi.fn().mockReturnValue(123),
|
||||||
config: jest.fn().mockReturnValue({
|
config: vi.fn().mockReturnValue({
|
||||||
trainGold: (isFriendly: boolean) =>
|
trainGold: (isFriendly: boolean) =>
|
||||||
isFriendly ? BigInt(1000) : BigInt(500),
|
isFriendly ? BigInt(1000) : BigInt(500),
|
||||||
}),
|
}),
|
||||||
addUpdate: jest.fn(),
|
addUpdate: vi.fn(),
|
||||||
addExecution: jest.fn(),
|
addExecution: vi.fn(),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
player = {
|
player = {
|
||||||
addGold: jest.fn(),
|
addGold: vi.fn(),
|
||||||
id: 1,
|
id: 1,
|
||||||
canTrade: jest.fn().mockReturnValue(true),
|
canTrade: vi.fn().mockReturnValue(true),
|
||||||
isFriendly: jest.fn().mockReturnValue(false),
|
isFriendly: vi.fn().mockReturnValue(false),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
unit = {
|
unit = {
|
||||||
owner: jest.fn().mockReturnValue(player),
|
owner: vi.fn().mockReturnValue(player),
|
||||||
level: jest.fn().mockReturnValue(1),
|
level: vi.fn().mockReturnValue(1),
|
||||||
tile: jest.fn().mockReturnValue({ x: 0, y: 0 }),
|
tile: vi.fn().mockReturnValue({ x: 0, y: 0 }),
|
||||||
type: jest.fn(),
|
type: vi.fn(),
|
||||||
isActive: jest.fn().mockReturnValue(true),
|
isActive: vi.fn().mockReturnValue(true),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
trainExecution = {
|
trainExecution = {
|
||||||
loadCargo: jest.fn(),
|
loadCargo: vi.fn(),
|
||||||
owner: jest.fn().mockReturnValue(player),
|
owner: vi.fn().mockReturnValue(player),
|
||||||
level: jest.fn(),
|
level: vi.fn(),
|
||||||
} as any;
|
} as any;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ describe("TrainStation", () => {
|
|||||||
|
|
||||||
it("checks trade availability (same owner)", () => {
|
it("checks trade availability (same owner)", () => {
|
||||||
const otherUnit = {
|
const otherUnit = {
|
||||||
owner: jest.fn().mockReturnValue(unit.owner()),
|
owner: vi.fn().mockReturnValue(unit.owner()),
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
const station = new TrainStation(game, unit);
|
const station = new TrainStation(game, unit);
|
||||||
|
|||||||
Vendored
-34
@@ -1,34 +0,0 @@
|
|||||||
declare module "*.png" {
|
|
||||||
const content: string;
|
|
||||||
export default content;
|
|
||||||
}
|
|
||||||
declare module "*.jpg" {
|
|
||||||
const value: string;
|
|
||||||
export default value;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module "*.webp" {
|
|
||||||
const value: string;
|
|
||||||
export default value;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module "*.jpeg" {
|
|
||||||
const value: string;
|
|
||||||
export default value;
|
|
||||||
}
|
|
||||||
declare module "*.svg" {
|
|
||||||
const value: string;
|
|
||||||
export default value;
|
|
||||||
}
|
|
||||||
declare module "*.bin" {
|
|
||||||
const value: string;
|
|
||||||
export default value;
|
|
||||||
}
|
|
||||||
declare module "*.txt" {
|
|
||||||
const value: string;
|
|
||||||
export default value;
|
|
||||||
}
|
|
||||||
declare module "*.html" {
|
|
||||||
const content: string;
|
|
||||||
export default content;
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
|
import { vi } from "vitest";
|
||||||
|
|
||||||
// Mock BuildMenu to avoid importing lit and other ESM-heavy deps in this unit test
|
// Mock BuildMenu to avoid importing lit and other ESM-heavy deps in this unit test
|
||||||
jest.mock(
|
vi.mock("../src/client/graphics/layers/BuildMenu", () => ({
|
||||||
"../src/client/graphics/layers/BuildMenu",
|
BuildMenu: class {},
|
||||||
() => ({
|
flattenedBuildTable: [],
|
||||||
BuildMenu: class {},
|
}));
|
||||||
flattenedBuildTable: [],
|
|
||||||
}),
|
|
||||||
{ virtual: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
// Mock Utils to avoid touching DOM (document) during tests
|
// Mock Utils to avoid touching DOM (document) during tests
|
||||||
jest.mock("../src/client/Utils", () => ({
|
vi.mock("../src/client/Utils", () => ({
|
||||||
translateText: (k: string) => k,
|
translateText: (k: string) => k,
|
||||||
getSvgAspectRatio: async () => 1,
|
getSvgAspectRatio: async () => 1,
|
||||||
}));
|
}));
|
||||||
@@ -57,20 +55,20 @@ const makeParams = (opts?: Partial<MenuElementParams>): MenuElementParams => {
|
|||||||
} as any,
|
} as any,
|
||||||
emojiTable: {} as any,
|
emojiTable: {} as any,
|
||||||
playerActionHandler: {
|
playerActionHandler: {
|
||||||
handleBreakAlliance: jest.fn(),
|
handleBreakAlliance: vi.fn(),
|
||||||
handleEmbargo: jest.fn(),
|
handleEmbargo: vi.fn(),
|
||||||
handleDonateGold: jest.fn(),
|
handleDonateGold: vi.fn(),
|
||||||
handleDonateTroops: jest.fn(),
|
handleDonateTroops: vi.fn(),
|
||||||
handleTargetPlayer: jest.fn(),
|
handleTargetPlayer: vi.fn(),
|
||||||
} as any,
|
} as any,
|
||||||
playerPanel: {
|
playerPanel: {
|
||||||
show: jest.fn(),
|
show: vi.fn(),
|
||||||
} as any,
|
} as any,
|
||||||
chatIntegration: {
|
chatIntegration: {
|
||||||
createQuickChatMenu: jest.fn(() => []),
|
createQuickChatMenu: vi.fn(() => []),
|
||||||
} as any,
|
} as any,
|
||||||
eventBus: {} as any,
|
eventBus: {} as any,
|
||||||
closeMenu: jest.fn(),
|
closeMenu: vi.fn(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
// Add global mocks or configuration here if needed
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2020",
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"types": ["jest", "node"]
|
|
||||||
},
|
|
||||||
"include": ["tests/**/*", "src/**/*"]
|
|
||||||
}
|
|
||||||
+9
-5
@@ -3,15 +3,16 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
// Language and Environment
|
// Language and Environment
|
||||||
"target": "ES2020",
|
"target": "ES2020",
|
||||||
|
|
||||||
// Modules
|
// Modules
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"resources/*": ["resources/*"]
|
||||||
|
},
|
||||||
// Emit
|
// Emit
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
|
|
||||||
// Type Checking
|
// Type Checking
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"allowUnusedLabels": false,
|
"allowUnusedLabels": false,
|
||||||
@@ -21,7 +22,9 @@
|
|||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"useDefineForClassFields": false,
|
"useDefineForClassFields": false,
|
||||||
"strictPropertyInitialization": false
|
"strictPropertyInitialization": false,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"types": ["vitest/globals", "node"]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*",
|
"src/**/*",
|
||||||
@@ -29,7 +32,8 @@
|
|||||||
"proprietary/**/*",
|
"proprietary/**/*",
|
||||||
"generated/**/*",
|
"generated/**/*",
|
||||||
"tests/**/*",
|
"tests/**/*",
|
||||||
"src/scripts"
|
"src/scripts",
|
||||||
|
"vite.config.ts"
|
||||||
],
|
],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|||||||
+137
@@ -0,0 +1,137 @@
|
|||||||
|
import { execSync } from "child_process";
|
||||||
|
import path from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { defineConfig, loadEnv } from "vite";
|
||||||
|
import { createHtmlPlugin } from "vite-plugin-html";
|
||||||
|
import { viteStaticCopy } from "vite-plugin-static-copy";
|
||||||
|
import tsconfigPaths from "vite-tsconfig-paths";
|
||||||
|
|
||||||
|
// Vite already handles these, but its good practice to define them explicitly
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
let gitCommit = process.env.GIT_COMMIT;
|
||||||
|
|
||||||
|
if (!gitCommit) {
|
||||||
|
try {
|
||||||
|
gitCommit = execSync("git rev-parse HEAD").toString().trim();
|
||||||
|
} catch (error) {
|
||||||
|
if (process.env.NODE_ENV !== "production") {
|
||||||
|
console.warn("Unable to determine git commit:", error.message);
|
||||||
|
}
|
||||||
|
gitCommit = "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineConfig(({ mode }) => {
|
||||||
|
const env = loadEnv(mode, process.cwd(), "");
|
||||||
|
const isProduction = mode === "production";
|
||||||
|
|
||||||
|
return {
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
environment: "jsdom",
|
||||||
|
setupFiles: "./tests/setup.ts",
|
||||||
|
},
|
||||||
|
root: "./",
|
||||||
|
base: "/",
|
||||||
|
publicDir: "resources", // Access static assets via import or explicit copy
|
||||||
|
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"protobufjs/minimal": path.resolve(
|
||||||
|
__dirname,
|
||||||
|
"node_modules/protobufjs/minimal.js",
|
||||||
|
),
|
||||||
|
resources: path.resolve(__dirname, "resources"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
tsconfigPaths(),
|
||||||
|
createHtmlPlugin({
|
||||||
|
minify: isProduction,
|
||||||
|
entry: "/src/client/Main.ts",
|
||||||
|
template: "index.html",
|
||||||
|
inject: {
|
||||||
|
data: {
|
||||||
|
// In case we need to inject variables into HTML
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
viteStaticCopy({
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
src: "proprietary/*",
|
||||||
|
dest: ".",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
|
||||||
|
define: {
|
||||||
|
"process.env.WEBSOCKET_URL": JSON.stringify(
|
||||||
|
isProduction ? "" : "localhost:3000",
|
||||||
|
),
|
||||||
|
"process.env.GAME_ENV": JSON.stringify(isProduction ? "prod" : "dev"),
|
||||||
|
"process.env.GIT_COMMIT": JSON.stringify(gitCommit),
|
||||||
|
"process.env.STRIPE_PUBLISHABLE_KEY": JSON.stringify(
|
||||||
|
env.STRIPE_PUBLISHABLE_KEY,
|
||||||
|
),
|
||||||
|
"process.env.API_DOMAIN": JSON.stringify(env.API_DOMAIN),
|
||||||
|
// Add other process.env variables if needed, OR migrate code to import.meta.env
|
||||||
|
},
|
||||||
|
|
||||||
|
build: {
|
||||||
|
outDir: "static", // Webpack outputs to 'static', assuming we want to keep this.
|
||||||
|
emptyOutDir: true,
|
||||||
|
assetsDir: "assets", // Sub-directory for assets
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
manualChunks: {
|
||||||
|
vendor: ["pixi.js", "howler", "zod", "protobufjs"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
server: {
|
||||||
|
port: 9000,
|
||||||
|
proxy: {
|
||||||
|
"/socket": {
|
||||||
|
target: "ws://localhost:3000",
|
||||||
|
ws: true,
|
||||||
|
changeOrigin: true,
|
||||||
|
},
|
||||||
|
// Worker proxies
|
||||||
|
"/w0": {
|
||||||
|
target: "ws://localhost:3001",
|
||||||
|
ws: true,
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path.replace(/^\/w0/, ""),
|
||||||
|
},
|
||||||
|
"/w1": {
|
||||||
|
target: "ws://localhost:3002",
|
||||||
|
ws: true,
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path.replace(/^\/w1/, ""),
|
||||||
|
},
|
||||||
|
"/w2": {
|
||||||
|
target: "ws://localhost:3003",
|
||||||
|
ws: true,
|
||||||
|
secure: false,
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path.replace(/^\/w2/, ""),
|
||||||
|
},
|
||||||
|
// API proxies
|
||||||
|
"/api": {
|
||||||
|
target: "http://localhost:3000",
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -1,263 +0,0 @@
|
|||||||
import { execSync } from "child_process";
|
|
||||||
import CopyPlugin from "copy-webpack-plugin";
|
|
||||||
import ESLintPlugin from "eslint-webpack-plugin";
|
|
||||||
import HtmlWebpackPlugin from "html-webpack-plugin";
|
|
||||||
import path from "path";
|
|
||||||
import { fileURLToPath } from "url";
|
|
||||||
import webpack from "webpack";
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
|
||||||
const __dirname = path.dirname(__filename);
|
|
||||||
|
|
||||||
const gitCommit =
|
|
||||||
process.env.GIT_COMMIT ?? execSync("git rev-parse HEAD").toString().trim();
|
|
||||||
|
|
||||||
export default async (env, argv) => {
|
|
||||||
const isProduction = argv.mode === "production";
|
|
||||||
|
|
||||||
return {
|
|
||||||
entry: "./src/client/Main.ts",
|
|
||||||
output: {
|
|
||||||
publicPath: "/",
|
|
||||||
filename: "js/[name].[contenthash].js", // Added content hash
|
|
||||||
path: path.resolve(__dirname, "static"),
|
|
||||||
clean: isProduction,
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.bin$/,
|
|
||||||
type: "asset/resource", // Changed from raw-loader
|
|
||||||
generator: {
|
|
||||||
filename: "binary/[name].[contenthash][ext]", // Added content hash
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.txt$/,
|
|
||||||
type: "asset/source",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.md$/,
|
|
||||||
type: "asset/resource", // Changed from raw-loader
|
|
||||||
generator: {
|
|
||||||
filename: "text/[name].[contenthash][ext]", // Added content hash
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.ts$/,
|
|
||||||
use: "ts-loader",
|
|
||||||
exclude: /node_modules/,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.css$/,
|
|
||||||
use: [
|
|
||||||
"style-loader",
|
|
||||||
{
|
|
||||||
loader: "css-loader",
|
|
||||||
options: {
|
|
||||||
importLoaders: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
loader: "postcss-loader",
|
|
||||||
options: {
|
|
||||||
postcssOptions: {
|
|
||||||
plugins: ["tailwindcss", "autoprefixer"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.(webp|png|jpe?g|gif)$/i,
|
|
||||||
type: "asset/resource",
|
|
||||||
generator: {
|
|
||||||
filename: "images/[name].[contenthash][ext]", // Added content hash
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.html$/,
|
|
||||||
use: ["html-loader"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.svg$/,
|
|
||||||
type: "asset/resource", // Changed from asset/inline for caching
|
|
||||||
generator: {
|
|
||||||
filename: "images/[name].[contenthash][ext]", // Added content hash
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.(woff|woff2|eot|ttf|otf|xml)$/,
|
|
||||||
type: "asset/resource", // Changed from file-loader
|
|
||||||
generator: {
|
|
||||||
filename: "fonts/[name].[contenthash][ext]", // Added content hash and fixed path
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.(mp3|wav|ogg)$/i,
|
|
||||||
type: "asset/resource",
|
|
||||||
generator: {
|
|
||||||
filename: "sounds/[name].[contenthash][ext]",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
extensions: [".tsx", ".ts", ".js"],
|
|
||||||
alias: {
|
|
||||||
"protobufjs/minimal": path.resolve(
|
|
||||||
__dirname,
|
|
||||||
"node_modules/protobufjs/minimal.js",
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
template: "./src/client/index.html",
|
|
||||||
filename: "index.html",
|
|
||||||
// Add optimization for HTML
|
|
||||||
minify: isProduction
|
|
||||||
? {
|
|
||||||
collapseWhitespace: true,
|
|
||||||
removeComments: true,
|
|
||||||
removeRedundantAttributes: true,
|
|
||||||
removeScriptTypeAttributes: true,
|
|
||||||
removeStyleLinkTypeAttributes: true,
|
|
||||||
useShortDoctype: true,
|
|
||||||
}
|
|
||||||
: false,
|
|
||||||
}),
|
|
||||||
new webpack.DefinePlugin({
|
|
||||||
"process.env.WEBSOCKET_URL": JSON.stringify(
|
|
||||||
isProduction ? "" : "localhost:3000",
|
|
||||||
),
|
|
||||||
"process.env.GAME_ENV": JSON.stringify(isProduction ? "prod" : "dev"),
|
|
||||||
"process.env.GIT_COMMIT": JSON.stringify(gitCommit),
|
|
||||||
"process.env.STRIPE_PUBLISHABLE_KEY": JSON.stringify(
|
|
||||||
process.env.STRIPE_PUBLISHABLE_KEY,
|
|
||||||
),
|
|
||||||
"process.env.API_DOMAIN": JSON.stringify(process.env.API_DOMAIN),
|
|
||||||
}),
|
|
||||||
new CopyPlugin({
|
|
||||||
patterns: [
|
|
||||||
{
|
|
||||||
from: path.resolve(__dirname, "resources"),
|
|
||||||
to: path.resolve(__dirname, "static"),
|
|
||||||
noErrorOnMissing: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
from: path.resolve(__dirname, "proprietary"),
|
|
||||||
to: path.resolve(__dirname, "static"),
|
|
||||||
noErrorOnMissing: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
options: { concurrency: 100 },
|
|
||||||
}),
|
|
||||||
new ESLintPlugin({
|
|
||||||
context: __dirname,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
optimization: {
|
|
||||||
// Add optimization configuration for better caching
|
|
||||||
runtimeChunk: "single",
|
|
||||||
splitChunks: {
|
|
||||||
cacheGroups: {
|
|
||||||
vendor: {
|
|
||||||
test: /[\\/]node_modules[\\/]/,
|
|
||||||
name: "vendors",
|
|
||||||
chunks: "all",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user