Merge branch 'main' into bomb-confirmation

This commit is contained in:
Ryan
2026-01-08 00:40:06 +00:00
committed by GitHub
78 changed files with 1471 additions and 1213 deletions
-2
View File
@@ -13,8 +13,6 @@ RUN --mount=type=cache,target=/root/.npm \
# Copy only what's needed for build
COPY tsconfig.json ./
COPY vite.config.ts ./
COPY tailwind.config.js ./
COPY postcss.config.js ./
COPY eslint.config.js ./
COPY index.html ./
COPY resources ./resources
-2
View File
@@ -26,8 +26,6 @@ export default [
allowDefaultProject: [
"__mocks__/fileMock.js",
"eslint.config.js",
"postcss.config.js",
"tailwind.config.js",
"scripts/sync-assets.mjs",
],
},
+34 -27
View File
@@ -120,9 +120,22 @@
}
</style>
<!-- Immediate execution to prevent FOUC -->
<!-- Immediate execution to prevent FOUC + apply persisted dark mode -->
<script>
document.documentElement.className = "preload";
(function () {
const root = document.documentElement;
root.classList.add("preload");
try {
const storedDarkMode = localStorage.getItem("settings.darkMode");
if (storedDarkMode === "true") {
root.classList.add("dark");
} else if (storedDarkMode === "false") {
root.classList.remove("dark");
}
} catch (error) {
// Ignore storage access errors.
}
})();
</script>
<!-- CrazyGames SDK -->
@@ -183,7 +196,7 @@
</head>
<body
class="h-full select-none font-sans min-h-screen bg-opacity-0 bg-cover bg-center bg-fixed transition-opacity duration-300 ease-in-out flex flex-col"
class="h-full select-none font-sans min-h-screen bg-black/0 bg-cover bg-center bg-fixed transition-opacity duration-300 ease-in-out flex flex-col"
>
<header class="l-header">
<div class="l-header__content">
@@ -251,7 +264,7 @@
<div class="bg-image"></div>
<div
id="turnstile-container"
class="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-[99999]"
class="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-99999"
></div>
<gutter-ads></gutter-ads>
@@ -265,7 +278,7 @@
<territory-patterns-modal class="w-[20%] md:w-[15%]">
<button
id="territory-patterns-input-preview-button"
class="w-full border p-[4px] rounded-lg flex cursor-pointer border-black/30 dark:border-gray-300/60 bg-white/70 dark:bg-[rgba(55,65,81,0.7)] justify-center"
class="w-full border p-1 rounded-lg flex cursor-pointer border-black/30 dark:border-gray-300/60 bg-white/70 dark:bg-[rgba(55,65,81,0.7)] justify-center"
title="Pick a pattern!"
></button>
</territory-patterns-modal>
@@ -323,14 +336,10 @@
<button
id="settings-button"
title="Settings"
class="fixed bottom-4 right-4 z-50 rounded-full p-2 shadow-lg transition-colors duration-300 flex items-center justify-center"
style="width: 60px; height: 60px; background-color: #0075ff"
class="fixed bottom-4 right-4 z-50 rounded-full p-2 shadow-lg transition-colors duration-300 flex items-center justify-center size-15 bg-(--primaryColor)"
style="--primaryColor: #0075ff"
>
<img
src="/images/SettingIconWhite.svg"
alt="Settings"
style="width: 52px; height: 52px"
/>
<img src="/images/SettingIconWhite.svg" alt="Settings" class="size-18" />
</button>
<!-- Game components -->
@@ -339,22 +348,20 @@
</div>
<div id="app"></div>
<div id="radialMenu" class="radial-menu"></div>
<div class="flex gap-2 fixed right-[10px] top-[10px] z-50 flex-col">
<div class="flex gap-2 fixed right-2.5 top-2.5 z-50 flex-col">
<player-info-overlay></player-info-overlay>
</div>
<div
class="fixed bottom-[30px] sm:bottom-auto sm:top-[20px] z-50 mx-auto max-w-max inset-x-0 items-center"
class="fixed bottom-7.5 sm:bottom-auto sm:top-5 z-50 mx-auto max-w-max inset-x-0 items-center"
>
<heads-up-message></heads-up-message>
</div>
<div
class="left-0 bottom-0 sm:left-4 sm:bottom-4 w-full flex-col-reverse sm:flex-row z-50 md:w-[320px]"
style="position: fixed; pointer-events: none"
class="left-0 bottom-0 sm:left-4 sm:bottom-4 w-full flex-col-reverse sm:flex-row z-50 md:w-[320px] fixed pointer-events-none"
>
<div
class="w-full md:w-2/3 md:fixed sm:right-0 md:bottom-0 md:flex flex-col items-end"
style="pointer-events: none"
class="w-full md:w-2/3 md:fixed sm:right-0 md:bottom-0 md:flex flex-col items-end pointer-events-none"
>
<chat-display></chat-display>
<events-display></events-display>
@@ -366,13 +373,13 @@
<!-- Footer section -->
<footer
class="flex justify-center px-3 py-3 md:px-6 md:py-3 bg-[var(--boxBackgroundColor)] backdrop-blur-sm"
class="flex justify-center px-3 py-3 md:px-6 md:py-3 bg-(--boxBackgroundColor) backdrop-blur-xs"
>
<div
class="flex flex-col md:flex-row flex-nowrap justify-between items-center gap-4 md:gap-0 w-full max-w-[860px] flex-1"
class="flex flex-col md:flex-row flex-nowrap justify-between items-center gap-4 md:gap-0 w-full max-w-215 flex-1"
>
<div
class="flex flex-col sm:flex-row gap-4 sm:gap-5 text-white/70 justify-center md:justify-start flex-shrink-0"
class="flex flex-col sm:flex-row gap-4 sm:gap-5 text-white/70 justify-center md:justify-start shrink-0"
>
<a
href="https://openfront.wiki/Main_Page"
@@ -397,7 +404,7 @@
<span data-i18n="main.join_discord"> Discord </span>
</a>
</div>
<div class="flex justify-center text-white/70 flex-shrink-0">
<div class="flex justify-center text-white/70 shrink-0">
<a
href="https://github.com/openfrontio/OpenFrontIO"
class="text-white/70 hover:text-white transition-colors duration-300 ease-in-out inline-flex items-center gap-2 whitespace-nowrap"
@@ -414,7 +421,7 @@
</a>
</div>
<div
class="flex flex-col sm:flex-row gap-4 sm:gap-4 text-white/70 justify-center md:justify-end flex-shrink-0"
class="flex flex-col sm:flex-row gap-4 sm:gap-4 text-white/70 justify-center md:justify-end shrink-0"
>
<a
href="/privacy-policy.html"
@@ -446,7 +453,7 @@
<game-starting-modal></game-starting-modal>
<game-top-bar></game-top-bar>
<unit-display></unit-display>
<div class="flex flex-col items-end fixed top-4 right-4 z-[1000] gap-2">
<div class="flex flex-col items-end fixed top-4 right-4 z-1000 gap-2">
<game-right-sidebar></game-right-sidebar>
<replay-panel></replay-panel>
</div>
@@ -468,16 +475,16 @@
<performance-overlay></performance-overlay>
<div
id="language-modal"
class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden flex justify-center items-center"
class="fixed inset-0 bg-black/50 z-50 hidden flex justify-center items-center"
>
<div class="bg-white rounded-lg shadow-lg p-6 w-96 max-w-full">
<h2 class="text-xl font-semibold mb-4">Select Language</h2>
<div
id="language-list"
class="space-y-2 max-h-80 overflow-y-auto"
class="flex flex-col gap-2 max-h-80 overflow-y-auto"
></div>
<button
class="mt-4 w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded"
class="mt-4 w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-sm"
onclick="
document.getElementById('language-modal').classList.add('hidden')
"
Binary file not shown.

After

Width:  |  Height:  |  Size: 799 KiB

@@ -0,0 +1,110 @@
{
"name": "Amazon River",
"nations": [
{
"coordinates": [524, 60],
"name": "Boa Esperança",
"flag": "br"
},
{
"coordinates": [298, 242],
"name": "Santa Rosa",
"flag": "br"
},
{
"coordinates": [1056, 8],
"name": "Paraíso",
"flag": "br"
},
{
"coordinates": [1161, 8],
"name": "Bom Futuro",
"flag": "br"
},
{
"coordinates": [1662, 254],
"name": "São Paulo de Olivença",
"flag": "br"
},
{
"coordinates": [1807, 8],
"name": "São João",
"flag": "br"
},
{
"coordinates": [1977, 7],
"name": "Castro Alves",
"flag": "br"
},
{
"coordinates": [2487, 70],
"name": "Flora",
"flag": "br"
},
{
"coordinates": [2458, 217],
"name": "Esperito Santo",
"flag": "br"
},
{
"coordinates": [2590, 149],
"name": "Recreio",
"flag": "br"
},
{
"coordinates": [2937, 199],
"name": "Caturiá",
"flag": "br"
},
{
"coordinates": [3101, 222],
"name": "Correnteza",
"flag": "br"
},
{
"coordinates": [3354, 75],
"name": "Botafogo",
"flag": "br"
},
{
"coordinates": [3405, 237],
"name": "São João",
"flag": "br"
},
{
"coordinates": [3744, 228],
"name": "Cajual",
"flag": "br"
},
{
"coordinates": [3861, 244],
"name": "União",
"flag": "br"
},
{
"coordinates": [4409, 211],
"name": "Amaturá",
"flag": "br"
},
{
"coordinates": [4361, 55],
"name": "Monte Cristo",
"flag": "br"
},
{
"coordinates": [4649, 54],
"name": "Floresta",
"flag": "br"
},
{
"coordinates": [5399, 128],
"name": "Vargem Grande",
"flag": "br"
},
{
"coordinates": [5373, 7],
"name": "Baia",
"flag": "br"
}
]
}
+1
View File
@@ -63,6 +63,7 @@ var maps = []struct {
{Name: "lemnos"},
{Name: "twolakes"},
{Name: "didier"},
{Name: "amazonriver"},
{Name: "big_plains", IsTest: true},
{Name: "half_land_half_ocean", IsTest: true},
{Name: "ocean_and_land", IsTest: true},
+619 -793
View File
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -32,6 +32,7 @@
"@datastructures-js/priority-queue": "^6.3.3",
"@eslint/compat": "^1.2.7",
"@eslint/js": "^9.21.0",
"@tailwindcss/vite": "^4.1.18",
"@types/benchmark": "^2.1.5",
"@types/chai": "^4.3.17",
"@types/d3": "^7.4.3",
@@ -60,6 +61,7 @@
"eslint": "^9.21.0",
"eslint-config-prettier": "^10.1.1",
"eslint-formatter-gha": "^1.5.2",
"glob": "^13.0.0",
"globals": "^16.0.0",
"husky": "^9.1.7",
"jsdom": "^27.4.0",
@@ -69,14 +71,13 @@
"mrmime": "^2.0.0",
"pixi-filters": "^6.1.4",
"pixi.js": "^8.11.0",
"postcss": "^8.5.1",
"prettier": "^3.5.3",
"prettier-plugin-organize-imports": "^4.1.0",
"prettier-plugin-sh": "^0.17.4",
"protobufjs": "^7.5.3",
"sinon": "^21.0.0",
"sinon-chai": "^4.0.0",
"tailwindcss": "^3.4.17",
"tailwindcss": "^4.1.18",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.7.2",
"typescript-eslint": "^8.26.0",
-6
View File
@@ -1,6 +0,0 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
+6 -1
View File
@@ -254,7 +254,8 @@
"twolakes": "Two Lakes",
"straitofhormuz": "Strait of Hormuz",
"surrounded": "Surrounded",
"didier": "Didier"
"didier": "Didier",
"amazonriver": "Amazon River"
},
"map_categories": {
"continental": "Continental",
@@ -362,6 +363,10 @@
"ffa": "Free for All",
"teams": "Teams"
},
"public_game_modifier": {
"random_spawn": "Random Spawn",
"compact_map": "Compact Map"
},
"select_lang": {
"title": "Select Language"
},
+125
View File
@@ -0,0 +1,125 @@
{
"map": {
"height": 280,
"num_land_tiles": 1172808,
"width": 5536
},
"map16x": {
"height": 70,
"num_land_tiles": 71047,
"width": 1384
},
"map4x": {
"height": 140,
"num_land_tiles": 290101,
"width": 2768
},
"name": "Amazon River",
"nations": [
{
"coordinates": [524, 60],
"flag": "br",
"name": "Boa Esperança"
},
{
"coordinates": [298, 242],
"flag": "br",
"name": "Santa Rosa"
},
{
"coordinates": [1056, 8],
"flag": "br",
"name": "Paraíso"
},
{
"coordinates": [1161, 8],
"flag": "br",
"name": "Bom Futuro"
},
{
"coordinates": [1662, 254],
"flag": "br",
"name": "São Paulo de Olivença"
},
{
"coordinates": [1807, 8],
"flag": "br",
"name": "São João"
},
{
"coordinates": [1977, 7],
"flag": "br",
"name": "Castro Alves"
},
{
"coordinates": [2487, 70],
"flag": "br",
"name": "Flora"
},
{
"coordinates": [2458, 217],
"flag": "br",
"name": "Esperito Santo"
},
{
"coordinates": [2590, 149],
"flag": "br",
"name": "Recreio"
},
{
"coordinates": [2937, 199],
"flag": "br",
"name": "Caturiá"
},
{
"coordinates": [3101, 222],
"flag": "br",
"name": "Correnteza"
},
{
"coordinates": [3354, 75],
"flag": "br",
"name": "Botafogo"
},
{
"coordinates": [3405, 237],
"flag": "br",
"name": "São João"
},
{
"coordinates": [3744, 228],
"flag": "br",
"name": "Cajual"
},
{
"coordinates": [3861, 244],
"flag": "br",
"name": "União"
},
{
"coordinates": [4409, 211],
"flag": "br",
"name": "Amaturá"
},
{
"coordinates": [4361, 55],
"flag": "br",
"name": "Monte Cristo"
},
{
"coordinates": [4649, 54],
"flag": "br",
"name": "Floresta"
},
{
"coordinates": [5399, 128],
"flag": "br",
"name": "Vargem Grande"
},
{
"coordinates": [5373, 7],
"flag": "br",
"name": "Baia"
}
]
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

+9 -9
View File
@@ -157,7 +157,7 @@ export class AccountModal extends LitElement {
return html`
<button
@click="${this.handleLogout}"
class="px-6 py-3 text-sm font-medium text-white bg-red-600 border border-transparent rounded-md hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition-colors duration-200"
class="px-6 py-3 text-sm font-medium text-white bg-red-600 border border-transparent rounded-md hover:bg-red-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition-colors duration-200"
>
Log Out
</button>
@@ -172,7 +172,7 @@ export class AccountModal extends LitElement {
<div class="mb-6">
<button
@click="${this.handleDiscordLogin}"
class="w-full px-6 py-3 text-sm font-medium text-white bg-[#5865F2] border border-transparent rounded-md hover:bg-[#4752C4] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#5865F2] transition-colors duration-200 flex items-center justify-center space-x-2"
class="w-full px-6 py-3 text-sm font-medium text-white bg-[#5865F2] border border-transparent rounded-md hover:bg-[#4752C4] focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-[#5865F2] transition-colors duration-200 flex items-center justify-center gap-2"
>
<img
src="/images/DiscordLogo.svg"
@@ -209,23 +209,23 @@ export class AccountModal extends LitElement {
name="email"
.value="${this.email}"
@input="${this.handleEmailInput}"
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-black"
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-xs focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-black"
placeholder="Enter your email address"
required
/>
</div>
</div>
<div class="flex justify-end space-x-3">
<div class="flex justify-end gap-3">
<button
@click="${this.close}"
class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Cancel
</button>
<button
@click="${this.handleSubmit}"
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Submit
</button>
@@ -233,7 +233,7 @@ export class AccountModal extends LitElement {
</div>
<button
@click="${this.handleLogout}"
class="px-3 py-1 text-xs font-medium text-white bg-red-600 border border-transparent rounded-md hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition-colors duration-200"
class="px-3 py-1 text-xs font-medium text-white bg-red-600 border border-transparent rounded-md hover:bg-red-700 focus:outline-hidden focus:ring-2 focus:ring-offset-2 focus:ring-red-500 transition-colors duration-200"
>
${translateText("account_modal.clear_session")}
</button>
@@ -376,10 +376,10 @@ export class AccountButton extends LitElement {
}
return html`
<div class="fixed top-4 right-4 z-[9998]">
<div class="fixed top-4 right-4 z-9998">
<button
@click="${this.open}"
class="w-12 h-12 bg-blue-600 hover:bg-blue-700 text-white rounded-full shadow-2xl hover:shadow-3xl transition-all duration-200 flex items-center justify-center text-xl focus:outline-none focus:ring-4 focus:ring-blue-500 focus:ring-offset-4"
class="w-12 h-12 bg-blue-600 hover:bg-blue-700 text-white rounded-full shadow-2xl hover:shadow-2xl transition-all duration-200 flex items-center justify-center text-xl focus:outline-hidden focus:ring-4 focus:ring-blue-500 focus:ring-offset-4"
title="${buttonTitle}"
>
${this.renderIcon()}
+1 -1
View File
@@ -35,7 +35,7 @@ export class DarkModeButton extends LitElement {
return html`
<button
title="Toggle Dark Mode"
class="absolute top-0 left-0 md:top-[10px] md:left-[10px] border-none bg-none cursor-pointer text-2xl"
class="absolute top-0 left-0 md:top-2.5 md:left-2.5 border-none bg-none cursor-pointer text-2xl"
@click=${() => this.toggleDarkMode()}
>
${this.darkMode ? "☀️" : "🌙"}
+3 -5
View File
@@ -73,16 +73,14 @@ export class FlagInput extends LitElement {
<div class="flex relative">
<button
id="flag-input_"
class="border rounded-lg flex cursor-pointer border-black/30
class="w-full border rounded-lg flex cursor-pointer border-black/30
dark:border-gray-300/60 bg-white/70 dark:bg-[rgba(55,65,81,0.7)]
"
justify-center aspect-square"
title=${translateText("flag_input.button_title")}
>
<span
id="flag-preview"
style="display:inline-block;
vertical-align:middle; background:#333; border-radius:6px;
overflow:hidden;"
class="block w-full aspect-3/2 bg-[#333] overflow-hidden rounded-md"
></span>
</button>
</div>
+8 -8
View File
@@ -20,10 +20,10 @@ export class FlagInputModal extends LitElement {
render() {
return html`
<o-modal alwaysMaximized title=${translateText("flag_input.title")}>
<div class="flex justify-center w-full p-[1rem]">
<div class="flex justify-center w-full p-4">
<input
class="h-[2rem] border-none border border-gray-300
rounded-xl shadow-sm text-2xl text-center focus:outline-none
class="h-8 border-none border border-gray-300
rounded-xl shadow-xs text-2xl text-center focus:outline-hidden
focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-black
dark:border-gray-300/60 dark:bg-gray-700 dark:text-white"
type="text"
@@ -34,7 +34,7 @@ export class FlagInputModal extends LitElement {
</div>
<div
class="flex flex-wrap justify-evenly gap-[1rem] overflow-y-auto overflow-x-hidden h-[90%]"
class="flex flex-wrap justify-evenly gap-4 overflow-y-auto overflow-x-hidden h-[90%]"
>
${this.isModalOpen
? Countries.filter(
@@ -47,10 +47,10 @@ export class FlagInputModal extends LitElement {
this.setFlag(country.code);
this.close();
}}
class="text-center cursor-pointer border-none bg-none opacity-70
w-[calc(100%/2-15px)] sm:w-[calc(100%/4-15px)]
md:w-[calc(100%/6-15px)] lg:w-[calc(100%/8-15px)]
xl:w-[calc(100%/10-15px)] min-w-[80px]"
class="text-center cursor-pointer border-none bg-none opacity-70
w-[calc(100%/2-15px)] sm:w-[calc(100%/4-15px)]
md:w-[calc(100%/6-15px)] lg:w-[calc(100%/8-15px)]
xl:w-[calc(100%/10-15px)] min-w-20"
>
<img
class="country-flag w-full h-auto"
+8 -8
View File
@@ -1,7 +1,7 @@
import { html, LitElement } from "lit";
import { customElement, property, query, state } from "lit/decorators.js";
import { GameEndInfo } from "../core/Schemas";
import { GameMapType } from "../core/game/Game";
import { GameMapType, hasUnusualThumbnailSize } from "../core/game/Game";
import { fetchGameById } from "./Api";
import { terrainMapFileLoader } from "./TerrainMapFileLoader";
import { UsernameInput } from "./UsernameInput";
@@ -49,10 +49,8 @@ export class GameInfoModal extends LitElement {
title="${translateText("game_info_modal.title")}"
translationKey="main.game_info"
>
<div
class="flex flex-col items-center pl-[100px] pr-[100px] text-center mb-4"
>
<div class="w-[300px] sm:w-[500px]">
<div class="flex flex-col items-center px-25 text-center mb-4">
<div class="w-75 sm:w-125">
${this.isLoadingGame
? this.renderLoadingAnimation()
: this.renderRanking()}
@@ -107,15 +105,17 @@ export class GameInfoModal extends LitElement {
if (!info) {
return html``;
}
const isUnusualThumbnailSize = hasUnusualThumbnailSize(info.config.gameMap);
return html`
<div
class="h-[150px] flex relative justify-between rounded-xl bg-blue-600 items-center"
class="h-37.5 flex relative justify-between rounded-xl bg-blue-600 items-center"
>
${this.mapImage
? html`<img
src="${this.mapImage}"
class="absolute place-self-start col-span-full row-span-full h-full rounded-xl"
style="mask-image: linear-gradient(to left, transparent, #fff)"
class="absolute place-self-start col-span-full row-span-full h-full rounded-xl mask-[linear-gradient(to_left,transparent,#fff)] ${isUnusualThumbnailSize
? "object-cover object-center"
: ""}"
/>`
: html`<div
class="place-self-start col-span-full row-span-full h-full rounded-xl bg-gray-300"
+1 -2
View File
@@ -55,8 +55,7 @@ export class GoogleAdElement extends LitElement {
return html`
<div class="google-ad-container">
<ins
class="adsbygoogle"
style="display:block"
class="adsbygoogle block"
data-ad-client="${this.adClient}"
data-ad-slot="${this.adSlot}"
data-ad-format="${this.adFormat}"
+6 -7
View File
@@ -417,8 +417,7 @@ export class HelpModal extends LitElement {
<li class="mb-4">
<img
src="/images/InfoIcon.svg"
class="inline-block icon"
style="fill: white; background: transparent;"
class="inline-block icon fill-white bg-transparent"
loading="lazy"
/>
<span>${translateText("help_modal.radial_info")}</span>
@@ -614,7 +613,7 @@ export class HelpModal extends LitElement {
class="flex flex-col items-center w-full md:w-1/3 mb-2 md:mb-0"
>
<div
class="text-gray-300 flex flex-col justify-start min-h-[3rem] w-full px-2 mb-1"
class="text-gray-300 flex flex-col justify-start min-h-12 w-full px-2 mb-1"
>
${translateText("help_modal.icon_crown")}
</div>
@@ -631,7 +630,7 @@ export class HelpModal extends LitElement {
class="flex flex-col items-center w-full md:w-1/3 mb-2 md:mb-0"
>
<div
class="text-gray-300 flex flex-col justify-start min-h-[3rem] w-full px-2 mb-1"
class="text-gray-300 flex flex-col justify-start min-h-12 w-full px-2 mb-1"
>
${translateText("help_modal.icon_traitor")}
</div>
@@ -648,7 +647,7 @@ export class HelpModal extends LitElement {
class="flex flex-col items-center w-full md:w-1/3 mb-2 md:mb-0"
>
<div
class="text-gray-300 flex flex-col justify-start min-h-[3rem] w-full px-2 mb-1"
class="text-gray-300 flex flex-col justify-start min-h-12 w-full px-2 mb-1"
>
${translateText("help_modal.icon_ally")}
</div>
@@ -667,7 +666,7 @@ export class HelpModal extends LitElement {
class="flex flex-col items-center w-full md:w-1/3 mb-2 md:mb-0"
>
<div
class="text-gray-300 flex flex-col justify-start min-h-[3rem] w-full px-2 mb-1"
class="text-gray-300 flex flex-col justify-start min-h-12 w-full px-2 mb-1"
>
${translateText("help_modal.icon_embargo")}
</div>
@@ -684,7 +683,7 @@ export class HelpModal extends LitElement {
class="flex flex-col items-center w-full md:w-1/3 mb-2 md:mb-0"
>
<div
class="text-gray-300 flex flex-col justify-start min-h-[3rem] w-full px-2 mb-1"
class="text-gray-300 flex flex-col justify-start min-h-12 w-full px-2 mb-1"
>
${translateText("help_modal.icon_request")}
</div>
+15 -16
View File
@@ -14,6 +14,7 @@ import {
UnitType,
mapCategories,
} from "../core/game/Game";
import { getCompactMapNationCount } from "../core/game/NationCreation";
import { UserSettings } from "../core/game/UserSettings";
import {
ClientInfo,
@@ -97,12 +98,11 @@ export class HostLobbyModal extends LitElement {
${
this.lobbyIdVisible
? html`<svg
class="visibility-icon"
class="visibility-icon mr-2 cursor-pointer"
@click=${() => {
this.lobbyIdVisible = !this.lobbyIdVisible;
this.requestUpdate();
}}
style="margin-right: 8px; cursor: pointer;"
stroke="currentColor"
fill="currentColor"
stroke-width="0"
@@ -116,12 +116,11 @@ export class HostLobbyModal extends LitElement {
></path>
</svg>`
: html`<svg
class="visibility-icon"
class="visibility-icon mr-2 cursor-pointer"
@click=${() => {
this.lobbyIdVisible = !this.lobbyIdVisible;
this.requestUpdate();
}}
style="margin-right: 8px; cursor: pointer;"
stroke="currentColor"
fill="currentColor"
stroke-width="0"
@@ -146,12 +145,12 @@ export class HostLobbyModal extends LitElement {
</svg>`
}
<!-- Lobby ID (conditionally shown) -->
<span class="lobby-id" @click=${this.copyToClipboard} style="cursor: pointer;">
<span class="lobby-id cursor-pointer" @click=${this.copyToClipboard}>
${this.lobbyIdVisible ? this.lobbyId : "••••••••"}
</span>
<!-- Copy icon/success indicator -->
<div @click=${this.copyToClipboard} style="margin-left: 8px; cursor: pointer;">
<div @click=${this.copyToClipboard} class="cursor-pointer ml-2">
${
this.copySuccess
? html`<span class="copy-success-icon">✓</span>`
@@ -225,7 +224,7 @@ export class HostLobbyModal extends LitElement {
<img
src=${randomMap}
alt="Random Map"
style="width:100%; aspect-ratio: 4/2; object-fit:cover; border-radius:8px;"
class="w-full aspect-2/1 object-cover rounded-lg"
/>
</div>
<div class="option-card-title">
@@ -516,8 +515,8 @@ export class HostLobbyModal extends LitElement {
id="end-timer-value"
min="0"
max="120"
.value=${String(this.maxTimerValue ?? 0)}
style="width: 60px; color: black; text-align: right; border-radius: 8px;"
.value=${String(this.maxTimerValue ?? "")}
class="w-15 text-black text-right rounded-lg"
@input=${this.handleMaxTimerValueChanges}
@keydown=${this.handleMaxTimerValueKeyDown}
/>`
@@ -526,7 +525,6 @@ export class HostLobbyModal extends LitElement {
${translateText("host_modal.max_timer")}
</div>
</label>
<label
for="spawn-immunity"
class="option-card ${this.spawnImmunity ? "selected" : ""}"
@@ -566,17 +564,17 @@ export class HostLobbyModal extends LitElement {
<span>${translateText("host_modal.player_immunity_duration")}</span>
</div>
</label>
<hr style="width: 100%; border-top: 1px solid #444; margin: 16px 0;" />
<hr class="w-full border-t border-t-[#444] my-4" />
<!-- Individual disables for structures/weapons -->
<div
style="margin: 8px 0 12px 0; font-weight: bold; color: #ccc; text-align: center;"
class="mt-2 mb-3 font-bold text-[#ccc] text-center"
>
${translateText("host_modal.enables_title")}
</div>
<div
style="display: flex; flex-wrap: wrap; justify-content: center; gap: 12px;"
class="flex flex-wrap justify-center gap-3"
>
${renderUnitTypeOptions({
disabledUnits: this.disabledUnits,
@@ -597,7 +595,7 @@ export class HostLobbyModal extends LitElement {
? translateText("host_modal.player")
: translateText("host_modal.players")
}
<span style="margin: 0 8px;">•</span>
<span class="mx-2">•</span>
${this.getEffectiveNationCount()}
${
this.getEffectiveNationCount() === 1
@@ -947,6 +945,7 @@ export class HostLobbyModal extends LitElement {
/**
* Returns the effective nation count for display purposes.
* In HumansVsNations mode, this equals the number of human players.
* For compact maps, only 25% of nations are used.
* Otherwise, it uses the manifest nation count (or 0 if nations are disabled).
*/
private getEffectiveNationCount(): number {
@@ -956,7 +955,7 @@ export class HostLobbyModal extends LitElement {
if (this.gameMode === GameMode.Team && this.teamCount === HumansVsNations) {
return this.clients.length;
}
return this.nationCount;
return getCompactMapNationCount(this.nationCount, this.compactMap);
}
}
+11
View File
@@ -20,6 +20,7 @@ export class LangSelector extends LitElement {
@state() private languageList: any[] = [];
@state() private showModal: boolean = false;
@state() private debugMode: boolean = false;
@state() isVisible = true;
private debugKeyPressed: boolean = false;
private languageMetadata: LanguageMetadata[] = metadata;
@@ -195,6 +196,7 @@ export class LangSelector extends LitElement {
"o-modal",
"o-button",
"territory-patterns-modal",
"fluent-slider",
];
document.title = this.translateText("main.title") ?? document.title;
@@ -247,7 +249,16 @@ export class LangSelector extends LitElement {
await this.loadLanguageList();
}
public close() {
this.showModal = false;
this.isVisible = false;
this.requestUpdate();
}
render() {
if (!this.isVisible) {
return html``;
}
const currentLang =
this.languageList.find((l) => l.code === this.currentLang) ??
(this.currentLang === "debug"
+2 -2
View File
@@ -64,10 +64,10 @@ export class LanguageModal extends LitElement {
return html`
<aside
class="fixed p-4 z-[9999] inset-0 bg-black/50 overflow-y-auto flex items-center justify-center"
class="fixed p-4 z-9999 inset-0 bg-black/50 overflow-y-auto flex items-center justify-center"
>
<div
class="bg-gray-800/80 dark:bg-gray-900/90 backdrop-blur-md rounded-lg min-w-[340px] max-w-[480px] w-full"
class="bg-gray-800/80 dark:bg-gray-900/90 backdrop-blur-md rounded-lg min-w-85 max-w-120 w-full"
>
<header
class="relative rounded-t-md text-lg bg-black/60 dark:bg-black/80 text-center text-white px-6 py-4 pr-10"
+1
View File
@@ -560,6 +560,7 @@ class Client {
"stats-button",
"token-login",
"matchmaking-modal",
"lang-selector",
].forEach((tag) => {
const modal = document.querySelector(tag) as HTMLElement & {
close?: () => void;
+2 -2
View File
@@ -170,10 +170,10 @@ export class MatchmakingButton extends LitElement {
}
return html`
<div class="z-[9999]">
<div class="z-9999">
<button
@click="${this.open}"
class="w-full h-16 bg-blue-600 hover:bg-blue-700 text-white rounded-full shadow-2xl hover:shadow-3xl transition-all duration-200 flex items-center justify-center text-xl focus:outline-none focus:ring-4 focus:ring-blue-500 focus:ring-offset-4"
class="w-full h-16 bg-blue-600 hover:bg-blue-700 text-white rounded-full shadow-2xl hover:shadow-2xl transition-all duration-200 flex items-center justify-center text-xl focus:outline-hidden focus:ring-4 focus:ring-blue-500 focus:ring-offset-4"
title="${translateText("matchmaking_modal.title")}"
>
Matchmaking
+2 -2
View File
@@ -158,11 +158,11 @@ export class NewsButton extends LitElement {
return html`
<div class="flex relative">
<button
class="border p-[4px] rounded-lg flex cursor-pointer border-black/30 dark:border-gray-300/60 bg-white/70 dark:bg-[rgba(55,65,81,0.7)]"
class="border p-1 rounded-lg flex cursor-pointer border-black/30 dark:border-gray-300/60 bg-white/70 dark:bg-[rgba(55,65,81,0.7)]"
@click=${this.openNewsModel}
>
<img
class="size-[48px] dark:invert"
class="size-12 dark:invert"
src="${megaphone}"
alt=${translateText("news.title")}
/>
+49 -14
View File
@@ -1,11 +1,13 @@
import { LitElement, html } from "lit";
import { html, LitElement } from "lit";
import { customElement, state } from "lit/decorators.js";
import { renderDuration, translateText } from "../client/Utils";
import {
Duos,
GameMapType,
GameMode,
hasUnusualThumbnailSize,
HumansVsNations,
PublicGameModifiers,
Quads,
Trios,
} from "../core/game/Game";
@@ -113,7 +115,14 @@ export class PublicLobby extends LitElement {
: `${modeLabel} ${teamDetailLabel}`;
}
const modifierLabel = this.getModifierLabels(
lobby.gameConfig.publicGameModifiers,
);
const mapImageSrc = this.mapImages.get(lobby.gameID);
const isUnusualThumbnailSize = hasUnusualThumbnailSize(
lobby.gameConfig.gameMap,
);
return html`
<button
@@ -121,8 +130,8 @@ export class PublicLobby extends LitElement {
?disabled=${this.isButtonDebounced}
class="isolate grid h-40 grid-cols-[100%] grid-rows-[100%] place-content-stretch w-full overflow-hidden ${this
.isLobbyHighlighted
? "bg-gradient-to-r from-green-600 to-green-500"
: "bg-gradient-to-r from-blue-600 to-blue-500"} text-white font-medium rounded-xl transition-opacity duration-200 hover:opacity-90 ${this
? "bg-linear-to-r via-none from-green-600 to-green-500"
: "bg-linear-to-r via-none from-blue-600 to-blue-500"} text-white font-medium rounded-xl transition-opacity duration-200 hover:opacity-90 ${this
.isButtonDebounced
? "opacity-70 cursor-not-allowed"
: ""}"
@@ -131,8 +140,9 @@ export class PublicLobby extends LitElement {
? html`<img
src="${mapImageSrc}"
alt="${lobby.gameConfig.gameMap}"
class="place-self-start col-span-full row-span-full h-full -z-10"
style="mask-image: linear-gradient(to left, transparent, #fff)"
class="place-self-start col-span-full row-span-full h-full -z-10 mask-[linear-gradient(to_left,transparent,#fff)] ${isUnusualThumbnailSize
? "object-cover object-center"
: ""}"
/>`
: html`<div
class="place-self-start col-span-full row-span-full h-full -z-10 bg-gray-300"
@@ -151,16 +161,25 @@ export class PublicLobby extends LitElement {
.join("")}`
: translateText("public_lobby.join")}
</div>
<div class="text-md font-medium text-white-400">
${fullModeLabel
? html`<span
class="text-sm ${this.isLobbyHighlighted
? "text-green-600"
: "text-blue-600"} bg-white rounded-sm px-1 ml-1"
>${fullModeLabel}</span
>`
: ""}
<div
class="text-md font-medium text-white-400 flex flex-wrap justify-end items-center gap-1"
>
<span
class="text-sm whitespace-nowrap ${this.isLobbyHighlighted
? "text-green-600"
: "text-blue-600"} bg-white rounded-xs px-1"
>${fullModeLabel}</span
>
${modifierLabel.map(
(label) =>
html`<span
class="text-sm whitespace-nowrap ${this.isLobbyHighlighted
? "text-green-600"
: "text-blue-600"} bg-white rounded-xs px-1"
>${label}</span
>`,
)}
<span class="whitespace-nowrap"
>${translateText(
`map.${lobby.gameConfig.gameMap.toLowerCase().replace(/[\s.]+/g, "")}`,
)}</span
@@ -291,6 +310,22 @@ export class PublicLobby extends LitElement {
return { label: null, isFullLabel: false };
}
private getModifierLabels(
publicGameModifiers: PublicGameModifiers | undefined,
): string[] {
if (!publicGameModifiers) {
return [];
}
const labels: string[] = [];
if (publicGameModifiers.isRandomSpawn) {
labels.push(translateText("public_game_modifier.random_spawn"));
}
if (publicGameModifiers.isCompact) {
labels.push(translateText("public_game_modifier.compact_map"));
}
return labels;
}
private lobbyClicked(lobby: GameInfo) {
if (this.isButtonDebounced) return;
+5 -11
View File
@@ -200,7 +200,7 @@ export class SinglePlayerModal extends LitElement {
<img
src=${randomMap}
alt="Random Map"
style="width:100%; aspect-ratio: 4/2; object-fit:cover; border-radius:8px;"
class="w-full aspect-2/1 object-cover rounded-lg"
/>
</div>
<div class="option-card-title">
@@ -457,7 +457,7 @@ export class SinglePlayerModal extends LitElement {
min="0"
max="120"
.value=${String(this.maxTimerValue ?? "")}
style="width: 60px; color: black; text-align: right; border-radius: 8px;"
class="w-15 text-black text-right rounded-lg"
@input=${this.handleMaxTimerValueChanges}
@keydown=${this.handleMaxTimerValueKeyDown}
/>`}
@@ -467,17 +467,11 @@ export class SinglePlayerModal extends LitElement {
</label>
</div>
<hr
style="width: 100%; border-top: 1px solid #444; margin: 16px 0;"
/>
<div
style="margin: 8px 0 12px 0; font-weight: bold; color: #ccc; text-align: center;"
>
<hr class="w-full border-t border-t-[#444] my-4" />
<div class="mt-2 mb-3 font-bold text-[#ccc] text-center">
${translateText("single_modal.enables_title")}
</div>
<div
style="display: flex; flex-wrap: wrap; justify-content: center; gap: 12px;"
>
<div class="flex flex-wrap justify-center gap-3">
${renderUnitTypeOptions({
disabledUnits: this.disabledUnits,
toggleUnit: this.toggleUnit.bind(this),
+3 -3
View File
@@ -91,7 +91,7 @@ export class StatsModal extends LitElement {
<div class="flex flex-col items-center justify-center p-6 text-white">
<p class="mb-4 text-center">${this.error}</p>
<button
class="px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded text-sm font-medium"
class="px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-sm text-sm font-medium"
@click=${() => this.loadLeaderboard()}
>
Retry
@@ -222,10 +222,10 @@ export class StatsButton extends LitElement {
}
return html`
<div class="fixed top-20 right-4 z-[9998]">
<div class="fixed top-20 right-4 z-9998">
<button
@click="${this.open}"
class="w-12 h-12 bg-blue-600 hover:bg-blue-700 text-white rounded-full shadow-2xl hover:shadow-3xl transition-all duration-200 flex items-center justify-center text-xl focus:outline-none focus:ring-4 focus:ring-blue-500 focus:ring-offset-4"
class="w-12 h-12 bg-blue-600 hover:bg-blue-700 text-white rounded-full shadow-2xl hover:shadow-2xl transition-all duration-200 flex items-center justify-center text-xl focus:outline-hidden focus:ring-4 focus:ring-blue-500 focus:ring-offset-4"
title="${translateText("stats_modal.title")}"
>
<img src="/icons/stats.svg" alt="Stats" class="w-6 h-6" />
+5 -8
View File
@@ -140,10 +140,7 @@ export class TerritoryPatternsModal extends LitElement {
? this.renderMySkinsButton()
: this.renderNotLoggedInWarning()}
</div>
<div
class="flex flex-wrap gap-4 p-2"
style="justify-content: center; align-items: flex-start;"
>
<div class="flex flex-wrap gap-4 p-2 justify-center items-start">
${this.affiliateCode === null
? html`
<pattern-button
@@ -193,8 +190,8 @@ export class TerritoryPatternsModal extends LitElement {
${hexCodes.map(
(hexCode) => html`
<div
class="w-12 h-12 rounded-lg border-2 border-white/30 cursor-pointer transition-all duration-200 hover:scale-110 hover:shadow-lg"
style="background-color: ${hexCode};"
class="w-12 h-12 rounded-lg border-2 border-white/30 bg-(--bg) cursor-pointer transition-all duration-200 hover:scale-110 hover:shadow-lg"
style="--bg: ${hexCode};"
title="${hexCode}"
@click=${() => this.selectColor(hexCode)}
></div>
@@ -267,8 +264,8 @@ export class TerritoryPatternsModal extends LitElement {
): TemplateResult {
return html`
<div
class="rounded"
style="width: ${width}px; height: ${height}px; background-color: ${hexCode};"
class="rounded-sm size-(--size) bg-(--bg)"
style="--size: ${width}px; --bg: ${hexCode};"
></div>
`;
}
+2 -2
View File
@@ -40,12 +40,12 @@ export class UsernameInput extends LitElement {
@change=${this.handleChange}
placeholder="${translateText("username.enter_username")}"
maxlength="${MAX_USERNAME_LENGTH}"
class="w-full px-4 py-2 border border-gray-300 rounded-xl shadow-sm text-2xl text-center focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:border-gray-300/60 dark:bg-gray-700 dark:text-white"
class="w-full px-4 py-2 border border-gray-300 rounded-xl shadow-xs text-2xl text-center focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-blue-500 dark:border-gray-300/60 dark:bg-gray-700 dark:text-white"
/>
${this.validationError
? html`<div
id="username-validation-error"
class="absolute z-10 w-full mt-2 px-3 py-1 text-lg border rounded bg-white text-red-600 border-red-600 dark:bg-gray-700 dark:text-red-300 dark:border-red-300"
class="absolute z-10 w-full mt-2 px-3 py-1 text-lg border rounded-sm bg-white text-red-600 border-red-600 dark:bg-gray-700 dark:text-red-300 dark:border-red-300"
>
${this.validationError}
</div>`
+5 -5
View File
@@ -83,7 +83,7 @@ export class LobbyTeamView extends LitElement {
this.clients,
(c) => c.clientID ?? c.username,
(client) =>
html`<div class="px-2 py-1 rounded bg-gray-700/70 mb-1 text-xs">
html`<div class="px-2 py-1 rounded-sm bg-gray-700/70 mb-1 text-xs">
${client.username}
</div>`,
)}
@@ -162,9 +162,9 @@ export class LobbyTeamView extends LitElement {
class="px-2 py-1 font-bold flex items-center justify-between text-white rounded-t-xl text-[13px] gap-2 bg-gray-700/70"
>
${this.showTeamColors
? html`<span
class="inline-block w-2.5 h-2.5 rounded-full border-2 border-white/90 shadow-inner"
style="background:${this.teamHeaderColor(preview.team)};"
? html` <span
class="inline-block w-2.5 h-2.5 rounded-full border-2 border-white/90 shadow-inner bg-(--bg)"
style="--bg:${this.teamHeaderColor(preview.team)};"
></span>`
: null}
<span class="truncate">${preview.team}</span>
@@ -180,7 +180,7 @@ export class LobbyTeamView extends LitElement {
(p) => p.clientID ?? p.username,
(p) =>
html` <div
class="bg-gray-700/70 px-2 py-1 rounded text-xs flex items-center justify-between"
class="bg-gray-700/70 px-2 py-1 rounded-sm text-xs flex items-center justify-between"
>
<span class="truncate">${p.username}</span>
${p.clientID === this.lobbyCreatorClientID
+15 -1
View File
@@ -1,6 +1,10 @@
import { LitElement, css, html } from "lit";
import { customElement, property, state } from "lit/decorators.js";
import { Difficulty, GameMapType } from "../../core/game/Game";
import {
Difficulty,
GameMapType,
hasUnusualThumbnailSize,
} from "../../core/game/Game";
import { terrainMapFileLoader } from "../TerrainMapFileLoader";
import { translateText } from "../Utils";
@@ -48,6 +52,7 @@ export const MapDescription: Record<keyof typeof GameMapType, string> = {
StraitOfHormuz: "Strait of Hormuz",
Surrounded: "Surrounded",
Didier: "Didier",
AmazonRiver: "Amazon River",
};
@customElement("map-display")
@@ -153,6 +158,14 @@ export class MapDisplay extends LitElement {
}
render() {
const mapType = GameMapType[this.mapKey as keyof typeof GameMapType];
const isUnusualThumbnailSize = mapType
? hasUnusualThumbnailSize(mapType)
: false;
const objectFitStyle = isUnusualThumbnailSize
? "object-fit: cover; object-position: center;"
: "";
return html`
<div class="option-card ${this.selected ? "selected" : ""}">
${this.isLoading
@@ -164,6 +177,7 @@ export class MapDisplay extends LitElement {
src="${this.mapWebpPath}"
alt="${this.mapKey}"
class="option-image"
style="${objectFitStyle}"
/>`
: html`<div class="option-image">Error</div>`}
${this.showMedals
+1 -1
View File
@@ -1,5 +1,5 @@
import { LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property } from "lit/decorators.js";
@customElement("modal-overlay")
export class ModalOverlay extends LitElement {
+12 -29
View File
@@ -70,7 +70,7 @@ export class PatternButton extends LitElement {
return html`
<div
class="flex flex-col items-center gap-1 p-1 bg-white/10 rounded-lg max-w-[200px]"
class="flex flex-col items-center gap-1 p-1 bg-white/10 rounded-lg max-w-50"
>
<button
class="bg-white/90 border-2 border-black/10 rounded-lg cursor-pointer transition-all duration-200 w-full
@@ -98,8 +98,7 @@ export class PatternButton extends LitElement {
`
: null}
<div
class="w-[120px] h-[120px] flex items-center justify-center bg-white rounded p-1 mx-auto"
style="overflow: hidden;"
class="size-30 flex items-center justify-center bg-white rounded-sm p-1 mx-auto overflow-hidden"
>
${renderPatternPreview(
this.pattern !== null
@@ -143,43 +142,27 @@ export function renderPatternPreview(
return html`<img
src="${generatePreviewDataUrl(pattern, width, height)}"
alt="Pattern preview"
class="w-full h-full object-contain"
style="image-rendering: pixelated; image-rendering: -moz-crisp-edges; image-rendering: crisp-edges;"
<!-- pixelated should also handle crisp-edges -->
class="w-full h-full object-contain [image-rendering:pixelated]"
/>`;
}
function renderBlankPreview(width: number, height: number): TemplateResult {
return html`
<div
class="flex items-center justify-center bg-white rounded-sm box-border overflow-hidden relative border border-[#ccc] w-(--width) h-(--height)"
style="
display: flex;
align-items: center;
justify-content: center;
height: ${height}px;
width: ${width}px;
background-color: #ffffff;
border-radius: 4px;
box-sizing: border-box;
overflow: hidden;
position: relative;
border: 1px solid #ccc;
--height: ${height}px;
--width: ${width}px;
"
>
<div
style="display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; gap: 0; width: calc(100% - 1px); height: calc(100% - 2px); box-sizing: border-box;"
class="grid grid-cols-2 grid-rows-2 gap-0 w-[calc(100%-1px)] h-[calc(100%-2px)] box-border"
>
<div
style="background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.1); box-sizing: border-box;"
></div>
<div
style="background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.1); box-sizing: border-box;"
></div>
<div
style="background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.1); box-sizing: border-box;"
></div>
<div
style="background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.1); box-sizing: border-box;"
></div>
<div class="bg-white border border-black/10 box-border"></div>
<div class="bg-white border border-black/10 box-border"></div>
<div class="bg-white border border-black/10 box-border"></div>
<div class="bg-white border border-black/10 box-border"></div>
</div>
</div>
`;
@@ -22,16 +22,18 @@ export class PlayerRow extends LitElement {
const visibleBorder = player.winner || this.currentPlayer;
return html`
<li
class="bg-gradient-to-r ${player.winner
? "from-sky-400 to-blue-700"
: "bg-slate-700"} border-[2px]
class="${player.winner
? "bg-linear-to-r via-none from-sky-400 to-blue-700"
: "bg-slate-700"} border-2
${player.winner
? "border-yellow-500"
: "border-yellow-50"} ${visibleBorder ? "" : "border-opacity-0"}
relative pt-1 pb-1 pr-2 pl-2 sm:pl-5 sm:pr-5 mb-[5px] rounded-lg flex justify-between items-center hover:bg-slate-500 transition duration-150 ease-in-out"
: visibleBorder
? "border-yellow-50"
: "border-yellow-50/0"}
relative pt-1 pb-1 pr-2 pl-2 sm:pl-5 sm:pr-5 mb-1.25 rounded-lg flex justify-between items-center hover:bg-slate-500 transition duration-150 ease-in-out"
>
<div
class="font-bold text-right w-[30px] text-lg text-white absolute left-[-40px]"
class="font-bold text-right w-7.5 text-lg text-white absolute -left-10"
>
${this.rank}
</div>
@@ -50,7 +52,7 @@ export class PlayerRow extends LitElement {
return html`
<img
src="/images/CrownIcon.svg"
class="absolute top-[-3px] left-[16px] w-[15px] h-[15px] sm:top-[-7px] sm:left-[30px] sm:w-[20px] sm:h-[20px]"
class="absolute -top-0.75 left-4 size-3.75 sm:-top-1.75 sm:left-7.5 sm:size-5"
/>
`;
}
@@ -84,7 +86,7 @@ export class PlayerRow extends LitElement {
</div>
<div>
<div
class="font-bold rounded-[50%] w-[30px] h-[30px] leading-[1.6rem] border text-center bg-white text-black"
class="font-bold rounded-[50%] size-7.5 leading-[1.6rem] border border-gray-200 text-center bg-white text-black"
>
${Number(this.score).toFixed(0)}
</div>
@@ -96,10 +98,13 @@ export class PlayerRow extends LitElement {
const bestScore = Math.max(this.bestScore, 1);
const width = Math.min(Math.max((this.score / bestScore) * 100, 0), 100);
return html`
<div class="w-full pr-[10px] m-auto">
<div class="h-[7px] bg-neutral-800" style="width: 100%;">
<div class="w-full pr-2.5 m-auto">
<div class="h-1.75 bg-neutral-800 w-full">
<!-- bar background -->
<div class="h-[7px] bg-white" style="width: ${width}%;"></div>
<div
class="h-1.75 bg-white w-(--width)"
style="--width: ${width}%;"
></div>
</div>
</div>
`;
@@ -109,7 +114,7 @@ export class PlayerRow extends LitElement {
<div
class="${highlight
? "font-bold text-[18px]"
: ""} min-w-[30px] sm:min-w-[60px] inline-block text-center"
: ""} min-w-7.5 sm:min-w-15 inline-block text-center"
>
${value}
</div>
@@ -152,32 +157,27 @@ export class PlayerRow extends LitElement {
return html`
<div class="flex gap-3 items-center">
${this.renderPlayerIcon()}
<div
class="text-left w-[125px] max-w-[125px] sm:w-[250px] sm:max-w-[250px]"
>
<div class="text-left w-31.25 sm:w-62.5">
${this.renderPlayerName()}
</div>
</div>
<div class="flex gap-2">
<div
class="font-bold rounded-md w-[60px] max-w-[60px] h-[30px] text-sm sm:w-[100px] sm:h-[30px] leading-[1.9rem] text-center"
class="font-bold rounded-md w-15 shrink-0 h-7.5 text-sm sm:w-25 sm:h-7.5 leading-[1.9rem] text-center"
>
${renderNumber(this.score)}
</div>
<img
src="/images/GoldCoinIcon.svg"
class="w-[14px] h-[14px] sm:w-[20px] sm:h-[20px] m-auto"
/>
<img src="/images/GoldCoinIcon.svg" class="size-3.5 sm:size-5 m-auto" />
</div>
`;
}
private renderPlayerName() {
return html`
<div class="flex gap-1 items-center max-w-[200px] min-w-[200px]">
<div class="flex gap-1 items-center w-50 shrink-0">
${this.player.tag ? this.renderTag(this.player.tag) : ""}
<div
class="text-xs sm:text-sm font-bold text-ellipsis max-w-[150px] min-w-[150px] overflow-hidden whitespace-nowrap"
class="text-xs sm:text-sm font-bold text-ellipsis w-37.5 shrink-0 overflow-hidden whitespace-nowrap"
>
${this.player.username}
</div>
@@ -188,7 +188,7 @@ export class PlayerRow extends LitElement {
private renderTag(tag: string) {
return html`
<div
class="bg-white text-black rounded-lg sm:rounded-xl border text-xs leading-[12px] sm:leading-[18px] text-blue-900 h-[15px] pr-[4px] pl-[4px] sm:h-[20px] sm:pr-[8px] sm:pl-[8px] font-bold"
class="bg-white text-black rounded-lg sm:rounded-xl border border-gray-200 text-xs leading-3 sm:leading-4.5 text-blue-900 h-3.75 px-1 sm:h-5 sm:px-2 font-bold"
>
${tag}
</div>
@@ -198,24 +198,24 @@ export class PlayerRow extends LitElement {
private renderIcon() {
if (this.player.killedAt) {
return html` <div
class="w-[30px] h-[30px] leading-[5px] text-lg sm:min-w-[40px] sm:w-[40px] sm:h-[40px] pt-[12px] sm:leading-[15px] sm:rounded-[50%] sm:border text-center sm:bg-slate-500 sm:text-2xl"
class="size-7.5 leading-1.25 shrink-0 text-lg sm:size-10 pt-3 sm:leading-3.75 sm:rounded-[50%] sm:border sm:border-gray-200 text-center sm:bg-slate-500 sm:text-2xl"
>
💀
</div>`;
} else if (this.player.flag) {
return html`<img
src="/flags/${this.player.flag}.svg"
class="min-w-[30px] h-[30px] sm:min-w-[40px] sm:h-[40px]"
class="min-w-7.5 h-7.5 sm:min-w-10 sm:h-10 shrink-0"
/>`;
}
return html`
<div
class="w-[30px] h-[30px] min-w-[30px] leading-[5px] rounded-[50%] sm:min-w-[40px] sm:w-[40px] sm:h-[40px] sm:pt-[10px] sm:leading-[14px] border text-center bg-slate-500"
class="size-7.5 leading-1.25 shrink-0 rounded-[50%] sm:size-10 sm:pt-2.5 sm:leading-3.5 border border-gray-200 text-center bg-slate-500"
>
<img
src="/images/ProfileIcon.svg"
class="w-[20px] h-[20px] mt-[2px] sm:w-[25px] sm:h-[25px] sm:mt-[-5px] m-auto"
class="size-5 mt-0.5 sm:size-6.25 sm:-mt-1.25 m-auto"
/>
</div>
`;
@@ -55,9 +55,8 @@ export class RankingControls extends LitElement {
return html`
<button
class="rounded-lg bg-blue-600 text-white text-lg p-3 hover:bg-blue-400 ${active
? "active"
? "active outline-2 outline-white font-bold"
: ""}"
style="${active ? "outline: solid 2px white; font-weight: bold;" : ""}"
@click=${() => this.onSort(type)}
>
${translateText(label)}
@@ -107,8 +106,9 @@ export class RankingControls extends LitElement {
return html`
<button
@click=${() => this.onSort(type)}
class="rounded-md bg-blue-50 text-black text-sm p-2 hover:bg-blue-200"
style="${active ? "outline: solid 2px white; font-weight: bold;" : ""}"
class="rounded-md bg-blue-50 text-black text-sm p-2 hover:bg-blue-200 ${active
? "outline-2 outline-white font-bold"
: ""}"
>
${translateText(label)}
</button>
@@ -14,7 +14,7 @@ export class RankingHeader extends LitElement {
render() {
return html`
<li
class="text-lg bg-gray-800 font-bold relative pt-2 pb-2 pr-5 pl-5 mb-[5px] rounded-md flex justify-between items-center"
class="text-lg bg-gray-800 font-bold relative pt-2 pb-2 pr-5 pl-5 mb-1.25 rounded-md flex justify-between items-center"
>
${this.renderHeaderContent()}
</li>
@@ -35,7 +35,7 @@ export class RankingHeader extends LitElement {
case RankType.Hydros:
case RankType.MIRV:
return html`
<div class="flex justify-between sm:pl-[70px] sm:pr-[70px] w-full">
<div class="flex justify-between sm:px-17.5 w-full">
${this.renderBombHeaderButton(
translateText("game_info_modal.atoms"),
RankType.Atoms,
@@ -78,8 +78,8 @@ export class RankingHeader extends LitElement {
return html`
<button
@click=${() => this.onSort(type)}
style="${this.rankType === type
? "border-bottom: solid 2px white;"
class="${this.rankType === type
? "border-b-2 border-b-white"
: nothing}"
>
${label}
@@ -21,12 +21,11 @@ export class SettingKeybind extends LitElement {
return html`
<div class="setting-item column${this.easter ? " easter-egg" : ""}">
<div class="setting-label-group">
<label class="setting-label block mb-1">${this.label}</label>
<label class="setting-label block mb-1">${this.label} </label>
<div class="setting-keybind-box flex flex-wrap items-start gap-2">
<div
class="setting-keybind-description flex-1 min-w-[240px] max-w-full whitespace-normal break-words text-sm text-gray-300"
style="word-break: break-word;"
class="setting-keybind-description flex-1 min-w-60 max-w-full whitespace-normal wrap-break-words text-sm text-gray-300 [word-break:break-word]"
>
${this.description}
</div>
@@ -44,13 +43,13 @@ export class SettingKeybind extends LitElement {
</span>
<button
class="text-xs text-gray-400 hover:text-white border border-gray-500 px-2 py-0.5 rounded transition whitespace-normal break-words max-w-full"
class="text-xs text-gray-400 hover:text-white border border-gray-500 px-2 py-0.5 rounded-sm transition whitespace-normal wrap-break-words max-w-full"
@click=${this.resetToDefault}
>
${translateText("user_setting.reset")}
</button>
<button
class="text-xs text-gray-400 hover:text-white border border-gray-500 px-2 py-0.5 rounded transition whitespace-normal break-words max-w-full"
class="text-xs text-gray-400 hover:text-white border border-gray-500 px-2 py-0.5 rounded-sm transition whitespace-normal wrap-break-words max-w-full"
@click=${this.unbindKey}
>
${translateText("user_setting.unbind")}
@@ -120,21 +120,19 @@ export class GameList extends LitElement {
</div>
</div>
<div
class="details"
style="max-height:${this.expandedGameId === game.gameId
? "200px"
: "0"}; ${this.expandedGameId === game.gameId
? ""
: "padding-top:0; padding-bottom:0;"}"
class="details max-h-(--max-height) ${this.expandedGameId ===
game.gameId
? "max-h-50"
: "py-0"}"
>
<div>
<span class="title" style="font-size:0.75rem;"
<span class="title text-xs"
>${translateText("game_list.started")}:</span
>
${new Date(game.start).toLocaleString()}
</div>
<div>
<span class="title" style="font-size:0.75rem;"
<span class="title text-xs"
>${translateText("game_list.mode")}:</span
>
${game.mode === GameMode.FFA
@@ -142,19 +140,19 @@ export class GameList extends LitElement {
: translateText("game_list.mode_team")}
</div>
<div>
<span class="title" style="font-size:0.75rem;"
<span class="title text-xs"
>${translateText("game_list.map")}:</span
>
${game.map}
</div>
<div>
<span class="title" style="font-size:0.75rem;"
<span class="title text-xs"
>${translateText("game_list.difficulty")}:</span
>
${game.difficulty}
</div>
<div>
<span class="title" style="font-size:0.75rem;"
<span class="title text-xs"
>${translateText("game_list.type")}:</span
>
${game.type}
@@ -117,16 +117,16 @@ export class PlayerStatsTable extends LitElement {
<table>
<thead>
<tr>
<th class="text-left" style="width:40%">
<th class="text-left w-2/5">
${translateText("player_stats_table.weapon")}
</th>
<th class="text-center" style="width:20%">
<th class="text-center w-1/5">
${translateText("player_stats_table.launched")}
</th>
<th class="text-center" style="width:20%">
<th class="text-center w-1/5">
${translateText("player_stats_table.landed")}
</th>
<th class="text-center" style="width:20%">
<th class="text-center w-1/5">
${translateText("player_stats_table.hits")}
</th>
</tr>
@@ -170,7 +170,7 @@ export class PlayerStatsTable extends LitElement {
</tr>
</tbody>
</table>
<table style="margin-top: 0.75rem;">
<table class="mt-3">
<thead>
<tr>
<th>${translateText("player_stats_table.gold")}</th>
@@ -122,8 +122,8 @@ export class PlayerStatsTreeView extends LitElement {
${types.map(
(t) => html`
<button
class="text-xs px-2 py-0.5 rounded border ${this.selectedType ===
t
class="text-xs px-2 py-0.5 rounded-sm border ${this
.selectedType === t
? "border-white/60 text-white"
: "border-white/20 text-gray-300"}"
@click=${() => this.setGameType(t)}
@@ -143,7 +143,7 @@ export class PlayerStatsTreeView extends LitElement {
${modes.map(
(m) => html`
<button
class="text-xs px-2 py-0.5 rounded border ${this
class="text-xs px-2 py-0.5 rounded-sm border ${this
.selectedMode === m
? "border-white/60 text-white"
: "border-white/20 text-gray-300"}"
@@ -162,7 +162,7 @@ export class PlayerStatsTreeView extends LitElement {
${diffs.map(
(d) =>
html` <button
class="text-xs px-2 py-0.5 rounded border ${this
class="text-xs px-2 py-0.5 rounded-sm border ${this
.selectedDifficulty === d
? "border-white/60 text-white"
: "border-white/20 text-gray-300"}"
+2 -2
View File
@@ -25,9 +25,9 @@ const TEXT_SIZE =
const getButtonStyles = () => {
const btnBase =
"group w-full min-w-[50px] select-none flex flex-col items-center justify-center " +
"gap-1 rounded-lg py-1.5 border border-white/10 bg-white/[0.04] shadow-sm " +
"gap-1 rounded-lg py-1.5 border border-white/10 bg-white/4 shadow-xs " +
"transition-all duration-150 " +
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/20 " +
"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-white/20 " +
"active:translate-y-[1px]";
return {
+1 -1
View File
@@ -460,7 +460,7 @@ export class BuildMenu extends LitElement implements Layer {
alt="gold"
width="12"
height="12"
style="vertical-align: middle;"
class="align-middle"
/>
</span>
${item.countable
+7 -9
View File
@@ -125,13 +125,12 @@ export class ChatDisplay extends LitElement implements Layer {
}
return html`
<div
class="${this._hidden
? "w-fit px-[10px] py-[5px]"
: ""} rounded-md bg-black bg-opacity-60 relative max-h-[30vh] flex flex-col-reverse overflow-y-auto w-full lg:bottom-2.5 lg:right-2.5 z-50 lg:max-w-[30vw] lg:w-full lg:w-auto"
style="pointer-events: auto"
class="pointer-events-auto ${this._hidden
? "w-fit px-2.5 py-1.25"
: ""} rounded-md bg-black/60 relative max-h-[30vh] flex flex-col-reverse overflow-y-auto w-full lg:bottom-2.5 lg:right-2.5 z-50 lg:max-w-[30vw] lg:w-full lg:w-auto"
>
<div>
<div class="w-full bg-black/80 sticky top-0 px-[10px]">
<div class="w-full bg-black/80 sticky top-0 px-2.5">
<button
class="text-white cursor-pointer pointer-events-auto ${this
._hidden
@@ -153,22 +152,21 @@ export class ChatDisplay extends LitElement implements Layer {
<span
class="${this.newEvents
? ""
: "hidden"} inline-block px-2 bg-red-500 rounded-sm"
: "hidden"} inline-block px-2 bg-red-500 rounded-xs"
>${this.newEvents}</span
>
</button>
<table
class="w-full border-collapse text-white shadow-lg lg:text-xl text-xs ${this
class="w-full border-collapse text-white shadow-lg lg:text-xl text-xs pointer-events-none ${this
._hidden
? "hidden"
: ""}"
style="pointer-events: auto;"
>
<tbody>
${this.chatEvents.map(
(chat) => html`
<tr class="border-b border-opacity-0">
<tr class="border-b border-gray-200/0">
<td class="lg:p-3 p-1 text-left">
${this.getChatContent(chat)}
</td>
+10 -11
View File
@@ -160,13 +160,12 @@ export class ControlPanel extends LitElement implements Layer {
}
</style>
<div
class="${this._isVisible
? "w-full sm:max-w-[320px] text-sm sm:text-base bg-gray-800/70 p-2 pr-3 sm:p-4 shadow-lg sm:rounded-lg backdrop-blur"
class="pointer-events-auto ${this._isVisible
? "w-full sm:max-w-[320px] text-sm sm:text-base bg-gray-800/70 p-2 pr-3 sm:p-4 shadow-lg sm:rounded-lg backdrop-blur-sm"
: "hidden"}"
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
style="pointer-events: auto;"
>
<div class="block bg-black/30 text-white mb-4 p-2 rounded">
<div class="block bg-black/30 text-white mb-4 p-2 rounded-sm">
<div class="flex justify-between mb-1">
<span class="font-bold"
>${translateText("control_panel.troops")}:</span
@@ -192,30 +191,30 @@ export class ControlPanel extends LitElement implements Layer {
<div class="relative mb-0 sm:mb-4">
<label class="block text-white mb-1">
${translateText("control_panel.attack_ratio")}:
${translateText("control_panel.attack_ratio")} :
<span
class="inline-flex items-center gap-1"
class="inline-flex items-center gap-1 [unicode-bidi:isolate]"
dir="ltr"
style="unicode-bidi: isolate;"
translate="no"
>
<span>${(this.attackRatio * 100).toFixed(0)}%</span>
<span>
(${renderTroops(
(this.game?.myPlayer()?.troops() ?? 0) * this.attackRatio,
)})
)}
)
</span>
</span>
</label>
<div class="relative h-8">
<!-- Background track -->
<div
class="absolute left-0 right-0 top-3 h-2 bg-white/20 rounded"
class="absolute left-0 right-0 top-3 h-2 bg-white/20 rounded-sm"
></div>
<!-- Fill track -->
<div
class="absolute left-0 top-3 h-2 bg-red-500/60 rounded transition-all duration-300"
style="width: ${this.attackRatio * 100}%"
class="absolute left-0 top-3 h-2 bg-red-500/60 rounded-sm transition-all duration-300 w-(--width)"
style="--width: ${this.attackRatio * 100}%"
></div>
<!-- Range input - exactly overlaying the visual elements -->
<input
+4 -4
View File
@@ -73,22 +73,22 @@ export class EmojiTable extends LitElement {
return html`
<div
class="fixed inset-0 bg-black/15 backdrop-brightness-110 flex items-start sm:items-center justify-center z-[10002] pt-4 sm:pt-0"
class="fixed inset-0 bg-black/15 backdrop-brightness-110 flex items-start sm:items-center justify-center z-10002 pt-4 sm:pt-0"
@click=${this.handleBackdropClick}
>
<div class="relative">
<!-- Close button -->
<button
class="absolute -top-3 -right-3 w-7 h-7 flex items-center justify-center
bg-zinc-700 hover:bg-red-500 text-white rounded-full shadow transition-colors z-[10004]"
bg-zinc-700 hover:bg-red-500 text-white rounded-full shadow-sm transition-colors z-10004"
@click=${this.hideTable}
>
</button>
<div
class="bg-zinc-900/95 p-2 sm:p-3 rounded-[10px] z-[10003] shadow-2xl shadow-black/50 ring-1 ring-white/5
w-[calc(100vw-32px)] sm:w-[400px] max-h-[calc(100vh-60px)] overflow-y-auto"
class="bg-zinc-900/95 p-2 sm:p-3 rounded-[10px] z-10003 shadow-2xl shadow-black/50 ring-1 ring-white/5
w-[calc(100vw-32px)] sm:w-100 max-h-[calc(100vh-60px)] overflow-y-auto"
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
@wheel=${(e: WheelEvent) => e.stopPropagation()}
@click=${(e: MouseEvent) => e.stopPropagation()}
+14 -15
View File
@@ -153,9 +153,9 @@ export class EventsDisplay extends LitElement implements Layer {
content: html`<img
src="${src}"
class="${toggleButtonSizeMap["default"]}"
style="filter: ${this.eventsFilters.get(category)
? "grayscale(1) opacity(0.5)"
: "none"}"
style="${this.eventsFilters.get(category)
? "filter: grayscale(1) opacity(0.5);"
: ""}"
/>`,
onClick: () => this.toggleEventFilter(category),
className: "cursor-pointer pointer-events-auto",
@@ -816,7 +816,7 @@ export class EventsDisplay extends LitElement implements Layer {
content: translateText("events_display.retaliate"),
onClick: () => this.handleRetaliate(attack),
className:
"inline-block px-3 py-1 text-white rounded text-md md:text-sm cursor-pointer transition-colors duration-300 bg-red-600 hover:bg-red-700",
"inline-block px-3 py-1 text-white rounded-sm text-md md:text-sm cursor-pointer transition-colors duration-300 bg-red-600 hover:bg-red-700",
translate: true,
})
: ""}
@@ -854,10 +854,10 @@ export class EventsDisplay extends LitElement implements Layer {
? this.renderButton({
content: "❌",
onClick: () => this.emitCancelAttackIntent(attack.id),
className: "text-left flex-shrink-0",
className: "text-left shrink-0",
disabled: attack.retreating,
})
: html`<span class="flex-shrink-0 text-blue-400"
: html`<span class="shrink-0 text-blue-400"
>(${translateText(
"events_display.retreating",
)}...)</span
@@ -890,10 +890,10 @@ export class EventsDisplay extends LitElement implements Layer {
content: "❌",
onClick: () =>
this.emitCancelAttackIntent(landAttack.id),
className: "text-left flex-shrink-0",
className: "text-left shrink-0",
disabled: landAttack.retreating,
})
: html`<span class="flex-shrink-0 text-blue-400"
: html`<span class="shrink-0 text-blue-400"
>(${translateText(
"events_display.retreating",
)}...)</span
@@ -926,10 +926,10 @@ export class EventsDisplay extends LitElement implements Layer {
? this.renderButton({
content: "❌",
onClick: () => this.emitBoatCancelIntent(boat.id()),
className: "text-left flex-shrink-0",
className: "text-left shrink-0",
disabled: boat.retreating(),
})
: html`<span class="flex-shrink-0 text-blue-400"
: html`<span class="shrink-0 text-blue-400"
>(${translateText(
"events_display.retreating",
)}...)</span
@@ -1026,14 +1026,14 @@ export class EventsDisplay extends LitElement implements Layer {
`,
onClick: this.toggleHidden,
className:
"text-white cursor-pointer pointer-events-auto w-fit p-2 lg:p-3 rounded-lg bg-gray-800/70 backdrop-blur",
"text-white cursor-pointer pointer-events-auto w-fit p-2 lg:p-3 rounded-lg bg-gray-800/70 backdrop-blur-sm",
})}
</div>
`
: html`
<!-- Main Events Display -->
<div
class="relative w-full sm:bottom-4 sm:right-4 z-50 sm:w-96 backdrop-blur"
class="relative w-full sm:bottom-4 sm:right-4 z-50 sm:w-96 backdrop-blur-sm"
>
<!-- Button Bar -->
<div class="w-full p-2 lg:p-3 bg-gray-800/70 rounded-t-lg">
@@ -1083,8 +1083,7 @@ export class EventsDisplay extends LitElement implements Layer {
>
<div>
<table
class="w-full max-h-none border-collapse text-white shadow-lg lg:text-base text-md md:text-xs"
style="pointer-events: auto;"
class="w-full max-h-none border-collapse text-white shadow-lg lg:text-base text-md md:text-xs pointer-events-auto"
>
<tbody>
${filteredEvents.map(
@@ -1123,7 +1122,7 @@ export class EventsDisplay extends LitElement implements Layer {
${event.buttons.map(
(btn) => html`
<button
class="inline-block px-3 py-1 text-white rounded text-md md:text-sm cursor-pointer transition-colors duration-300
class="inline-block px-3 py-1 text-white rounded-sm text-md md:text-sm cursor-pointer transition-colors duration-300
${btn.className.includes("btn-info")
? "bg-blue-500 hover:bg-blue-600"
: btn.className.includes(
@@ -87,7 +87,7 @@ export class GameLeftSidebar extends LitElement implements Layer {
render() {
return html`
<aside
class=${`fixed top-4 left-4 z-[1000] flex flex-col max-h-[calc(100vh-80px)] overflow-y-auto p-2 bg-slate-800/40 backdrop-blur-sm shadow-xs rounded-lg transition-transform duration-300 ease-out transform ${
class=${`fixed top-4 left-4 z-1000 flex flex-col max-h-[calc(100vh-80px)] overflow-y-auto p-2 bg-slate-800/40 backdrop-blur-xs shadow-xs rounded-lg transition-transform duration-300 ease-out transform ${
this.isVisible ? "translate-x-0" : "hidden"
}`}
>
@@ -98,14 +98,17 @@ export class GameLeftSidebar extends LitElement implements Layer {
@contextmenu=${(e: Event) => e.preventDefault()}
>
${translateText("help_modal.ui_your_team")}
<span style="color: ${this.playerColor.toRgbString()}">
<span
style="--color: ${this.playerColor.toRgbString()}"
class="text-(--color)"
>
&nbsp;${this.getTranslatedPlayerTeamLabel()} &#10687;
</span>
</div>
`
: null}
<div
class=${`flex items-center gap-2 space-x-2 text-white ${
class=${`flex items-center gap-2 text-white ${
this.isLeaderboardShow || this.isTeamLeaderboardShow ? "mb-2" : ""
}`}
>
@@ -139,7 +139,7 @@ export class GameRightSidebar extends LitElement implements Layer {
return html`
<aside
class=${`w-fit flex flex-row items-center gap-3 py-2 px-3 bg-gray-800/70 backdrop-blur-sm shadow-xs rounded-lg transition-transform duration-300 ease-out transform text-white ${
class=${`w-fit flex flex-row items-center gap-3 py-2 px-3 bg-gray-800/70 backdrop-blur-xs shadow-xs rounded-lg transition-transform duration-300 ease-out transform text-white ${
this._isVisible ? "translate-x-0" : "translate-x-full"
}`}
@contextmenu=${(e: Event) => e.preventDefault()}
+2 -2
View File
@@ -59,8 +59,8 @@ export class HeadsUpMessage extends LitElement implements Layer {
return html`
<div
class="flex items-center relative
w-full justify-evenly h-8 lg:h-10 md:top-[70px] left-0 lg:left-4
bg-opacity-60 bg-gray-900 rounded-md lg:rounded-lg
w-full justify-evenly h-8 lg:h-10 md:top-17.5 left-0 lg:left-4
bg-gray-900/60 rounded-md lg:rounded-lg
backdrop-blur-md text-white text-md lg:text-xl p-1 lg:p-2"
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
>
+1 -1
View File
@@ -82,7 +82,7 @@ export class ImmunityTimer extends LitElement implements Layer {
const widthPercent = this.progressRatio * 100;
return html`
<div class="w-full h-full flex z-[999]">
<div class="w-full h-full flex z-999">
<div
class="h-full transition-all duration-100 ease-in-out"
style="width: ${widthPercent}%; background-color: rgba(255, 165, 0, 0.9);"
+3 -2
View File
@@ -171,8 +171,9 @@ export class MultiTabModal extends LitElement implements Layer {
class="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2.5 mb-4"
>
<div
class="bg-red-600 dark:bg-red-500 h-2.5 rounded-full transition-all duration-1000 ease-linear"
style="width: ${(this.countdown / (this.duration / 1000)) * 100}%"
class="bg-red-600 dark:bg-red-500 h-2.5 rounded-full transition-all duration-1000 ease-linear w-(--width)"
style="--width: ${(this.countdown / (this.duration / 1000)) *
100}%"
></div>
</div>
@@ -537,12 +537,6 @@ export class PerformanceOverlay extends LitElement implements Layer {
return html``;
}
const style = `
left: ${this.position.x}px;
top: ${this.position.y}px;
transform: none;
`;
const copyLabel =
this.copyStatus === "success"
? translateText("performance_overlay.copied")
@@ -557,8 +551,10 @@ export class PerformanceOverlay extends LitElement implements Layer {
return html`
<div
class="performance-overlay ${this.isDragging ? "dragging" : ""}"
style="${style}"
class="performance-overlay ${this.isDragging
? "dragging"
: ""} transform-none left-(--left) top-(--top)"
style="--left: ${this.position.x}; --top: ${this.position.y};"
@mousedown="${this.handleMouseDown}"
>
<button class="reset-button" @click="${this.handleReset}">
@@ -612,10 +608,13 @@ export class PerformanceOverlay extends LitElement implements Layer {
);
return html`<div class="layer-row">
<span class="layer-name" title=${layer.name}
>${layer.name}</span
>
>${layer.name}
</span>
<div class="layer-bar">
<div class="layer-bar-fill" style="width: ${width}%;"></div>
<div
class="layer-bar-fill w-(--width)"
style="--width: ${width}%;"
></div>
</div>
<span class="layer-metrics">
${layer.avg.toFixed(2)} / ${layer.max.toFixed(2)}ms
@@ -203,7 +203,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
width="20"
height="20"
alt="${translateText(description)}"
style="vertical-align: middle;"
class="align-middle"
/>
<span class="w-full text-right p-1"
>${player.totalUnitLevels(type)}</span
@@ -285,7 +285,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
alt=${translateText("player_info_overlay.alliance_timeout")}
width="20"
height="20"
style="vertical-align: middle;"
class="align-middle"
/>
${this.allianceExpirationText(alliance)}
</span>`;
@@ -318,7 +318,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
${player.cosmetics.flag
? player.cosmetics.flag!.startsWith("!")
? html`<div
class="h-8 mr-1 aspect-[3/4] player-flag"
class="h-8 mr-1 aspect-3/4 player-flag"
${ref((el) => {
if (el instanceof HTMLElement) {
requestAnimationFrame(() => {
@@ -328,7 +328,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
})}
></div>`
: html`<img
class="h-8 mr-1 aspect-[3/4]"
class="h-8 mr-1 aspect-3/4"
src=${"/flags/" + player.cosmetics.flag! + ".svg"}
/>`
: html``}
@@ -390,7 +390,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
alt=${translateText("player_info_overlay.gold")}
width="15"
height="15"
style="vertical-align: middle;"
class="align-middle"
/>
<span class="w-full text-center"
>${renderNumber(player.gold())}</span
@@ -514,11 +514,11 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
return html`
<div
class="block lg:flex fixed top-[150px] right-4 w-full z-50 flex-col max-w-[180px]"
class="block lg:flex fixed top-37.5 right-4 w-full z-50 flex-col max-w-45"
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
>
<div
class="bg-gray-800/70 backdrop-blur-sm shadow-xs rounded-lg shadow-lg transition-all duration-300 text-white text-lg md:text-base ${containerClasses}"
class="bg-gray-800/70 backdrop-blur-xs shadow-xs rounded-lg shadow-lg transition-all duration-300 text-white text-lg md:text-base ${containerClasses}"
>
${this.player !== null ? this.renderPlayerInfo(this.player) : ""}
${this.unit !== null ? this.renderUnitInfo(this.unit) : ""}
+28 -31
View File
@@ -391,7 +391,7 @@ export class PlayerPanel extends LitElement implements Layer {
const label = secs !== null ? renderDuration(secs) : null;
const dotCls =
secs !== null
? `mx-1 h-[4px] w-[4px] rounded-full bg-red-400/70 ${secs <= 10 ? "animate-pulse" : ""}`
? `mx-1 size-1 rounded-full bg-red-400/70 ${secs <= 10 ? "animate-pulse" : ""}`
: "";
return html`
@@ -402,12 +402,7 @@ export class PlayerPanel extends LitElement implements Layer {
shadow-[inset_0_0_8px_rgba(239,68,68,0.12)]"
title=${translateText("player_panel.traitor")}
>
<img
src=${traitorIcon}
alt=""
aria-hidden="true"
class="h-[18px] w-[18px]"
/>
<img src=${traitorIcon} alt="" aria-hidden="true" class="size-4.5" />
<span class="tracking-tight"
>${translateText("player_panel.traitor")}</span
>
@@ -498,8 +493,8 @@ export class PlayerPanel extends LitElement implements Layer {
return html`
<div class="mb-1 flex justify-between gap-2">
<div
class="inline-flex items-center gap-1.5 rounded-lg bg-white/[0.04] px-3 py-1.5
text-white w-[140px] min-w-[140px] flex-shrink-0"
class="inline-flex items-center gap-1.5 rounded-lg bg-white/4 px-3 py-1.5 shrink-0
text-white w-35"
>
<span class="mr-0.5">💰</span>
<span translate="no" class="tabular-nums w-[5ch] font-semibold">
@@ -511,8 +506,8 @@ export class PlayerPanel extends LitElement implements Layer {
</div>
<div
class="inline-flex items-center gap-1.5 rounded-lg bg-white/[0.04] px-3 py-1.5
text-white w-[140px] min-w-[140px] flex-shrink-0"
class="inline-flex items-center gap-1.5 rounded-lg bg-white/4 px-3 py-1.5
text-white w-35 shrink-0"
>
<span class="mr-0.5">🛡️</span>
<span translate="no" class="tabular-nums w-[5ch] font-semibold">
@@ -530,7 +525,7 @@ export class PlayerPanel extends LitElement implements Layer {
return html`
<ui-divider></ui-divider>
<button
class="flex w-full items-center justify-between rounded-xl bg-white/[0.05] px-3 py-2 text-left text-white hover:bg-white/[0.08] active:scale-[0.995] transition"
class="flex w-full items-center justify-between rounded-xl bg-white/5 px-3 py-2 text-left text-white hover:bg-white/8 active:scale-[0.995] transition"
@click=${(e: Event) => this.handleToggleRocketDirection(e)}
>
<div class="flex flex-col">
@@ -551,7 +546,7 @@ export class PlayerPanel extends LitElement implements Layer {
private renderStats(other: PlayerView, my: PlayerView) {
return html`
<!-- Betrayals -->
<div class="grid grid-cols-[auto,1fr] gap-x-6 gap-y-2">
<div class="grid grid-cols-[auto_1fr] gap-x-6 gap-y-2">
<div
class="flex items-center gap-2 text-[15px] font-medium text-zinc-100 leading-snug"
>
@@ -564,7 +559,7 @@ export class PlayerPanel extends LitElement implements Layer {
</div>
<!-- Trading / Embargo -->
<div class="grid grid-cols-[auto,1fr] gap-x-6 gap-y-2">
<div class="grid grid-cols-[auto_1fr] gap-x-6 gap-y-2">
<div
class="flex items-center gap-2 text-[15px] font-medium text-zinc-100 leading-snug"
>
@@ -605,7 +600,7 @@ export class PlayerPanel extends LitElement implements Layer {
</div>
<span
aria-labelledby="alliances-title"
class="inline-flex items-center justify-center min-w-[20px] h-5 px-[6px] rounded-[10px]
class="inline-flex items-center justify-center min-w-5 h-5 px-1.5 rounded-[10px]
text-[12px] text-zinc-100 bg-white/10 border border-white/20"
>
${allies.length}
@@ -616,7 +611,7 @@ export class PlayerPanel extends LitElement implements Layer {
class="rounded-lg bg-zinc-800/70 ring-1 ring-zinc-700/60 w-full min-w-0"
>
<ul
class="max-h-[120px] overflow-y-auto p-2
class="max-h-30 overflow-y-auto p-2
flex flex-wrap gap-1.5
scrollbar-thin scrollbar-thumb-zinc-600 hover:scrollbar-thumb-zinc-500 scrollbar-track-zinc-800"
role="list"
@@ -631,9 +626,9 @@ export class PlayerPanel extends LitElement implements Layer {
(p) =>
html`<li
class="max-w-full inline-flex items-center gap-1.5
rounded-md border border-white/10 bg-white/[0.05]
rounded-md border border-white/10 bg-white/5
px-2.5 py-1 text-[14px] text-zinc-100
hover:bg-white/[0.08] active:scale-[0.99] transition"
hover:bg-white/8 active:scale-[0.99] transition"
title=${p.name()}
>
<span class="truncate">${p.name()}</span>
@@ -648,7 +643,7 @@ export class PlayerPanel extends LitElement implements Layer {
private renderAllianceExpiry() {
if (this.allianceExpiryText === null) return html``;
return html`
<div class="grid grid-cols-[auto,1fr] gap-x-6 gap-y-2 text-base">
<div class="grid grid-cols-[auto_1fr] gap-x-6 gap-y-2 text-base">
<div class="font-semibold text-zinc-300">
${translateText("player_panel.alliance_time_remaining")}
</div>
@@ -859,14 +854,14 @@ export class PlayerPanel extends LitElement implements Layer {
</style>
<div
class="fixed inset-0 z-[10001] flex items-center justify-center overflow-auto
class="fixed inset-0 z-10001 flex items-center justify-center overflow-auto
bg-black/15 backdrop-brightness-110 pointer-events-auto"
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
@wheel=${(e: MouseEvent) => e.stopPropagation()}
@click=${() => this.hide()}
>
<div
class="pointer-events-auto max-h-[90vh] min-w-[300px] max-w-[400px] px-4 py-2"
class="pointer-events-auto max-h-[90vh] min-w-75 max-w-100 px-4 py-2"
@click=${(e: MouseEvent) => e.stopPropagation()}
>
<div class="relative">
@@ -877,18 +872,20 @@ export class PlayerPanel extends LitElement implements Layer {
class=${`relative w-full bg-zinc-900/95 rounded-2xl text-zinc-100 shadow-2xl shadow-black/50
${other.isTraitor() ? "traitor-ring" : "ring-1 ring-white/5"}`}
>
<div style="overflow: visible;">
<div class="overflow-visible">
<div
style="max-height: calc(100vh - 120px - env(safe-area-inset-bottom)); overflow:auto; -webkit-overflow-scrolling: touch; resize: vertical;"
class="overflow-auto [-webkit-overflow-scrolling:touch] resize-y max-h-[calc(100vh-120px-env(safe-area-inset-bottom))]"
>
<button
@click=${this.handleClose}
class="absolute right-3 top-3 z-20 flex h-7 w-7 items-center justify-center rounded-full bg-zinc-700 text-white shadow hover:bg-red-500 transition-colors"
aria-label=${translateText("common.close") || "Close"}
title=${translateText("common.close") || "Close"}
>
</button>
<div class="sticky top-0 z-20 flex justify-end p-2">
<button
@click=${this.handleClose}
class="absolute right-3 top-3 z-20 flex h-7 w-7 items-center justify-center rounded-full bg-zinc-700 text-white shadow-sm hover:bg-red-500 transition-colors"
aria-label=${translateText("common.close") || "Close"}
title=${translateText("common.close") || "Close"}
>
</button>
</div>
<div
class="p-6 flex flex-col gap-2 font-sans antialiased text-[14.5px] leading-relaxed"
+2 -2
View File
@@ -66,7 +66,7 @@ export class ReplayPanel extends LitElement implements Layer {
return html`
<div
class="p-2 bg-gray-800/70 backdrop-blur-sm shadow-xs rounded-lg"
class="p-2 bg-gray-800/70 backdrop-blur-xs shadow-xs rounded-lg"
@contextmenu=${(e: Event) => e.preventDefault()}
>
<label class="block mb-2 text-white" translate="no">
@@ -93,7 +93,7 @@ export class ReplayPanel extends LitElement implements Layer {
return html`
<button
class="py-0.5 px-1 text-sm text-white rounded border transition border-gray-500 ${backgroundColor} hover:border-gray-200"
class="py-0.5 px-1 text-sm text-white rounded-sm border transition border-gray-500 ${backgroundColor} hover:border-gray-200"
@click=${() => this.onReplaySpeedChange(value)}
>
${label}
+14 -16
View File
@@ -255,7 +255,7 @@ export class SendResourceModal extends LitElement {
<button
type="button"
@click=${() => this.closeModal()}
class="absolute -top-3 -right-3 flex h-7 w-7 items-center justify-center rounded-full bg-zinc-700 text-white shadow hover:bg-red-500 transition-colors focus-visible:ring-2 focus-visible:ring-white/30 focus:outline-none"
class="absolute -top-3 -right-3 flex h-7 w-7 items-center justify-center rounded-full bg-zinc-700 text-white shadow-sm hover:bg-red-500 transition-colors focus-visible:ring-2 focus-visible:ring-white/30 focus:outline-hidden"
aria-label=${this.i18n.closeLabel()}
title=${this.i18n.closeLabel()}
>
@@ -361,7 +361,7 @@ export class SendResourceModal extends LitElement {
const clamped = Math.min(raw, hardMax);
this.sendAmount = this.clampSend(clamped);
}}
class="w-full appearance-none bg-transparent range-x focus:outline-none"
class="w-full appearance-none bg-transparent range-x focus:outline-hidden"
aria-label=${this.i18n.ariaSlider()}
aria-valuemin="0"
aria-valuemax=${hardMax}
@@ -374,11 +374,11 @@ export class SendResourceModal extends LitElement {
<!-- Tooltip -->
<div
class="pointer-events-none absolute -top-6 -translate-x-1/2 select-none"
style="left:${percentNow}%"
class="pointer-events-none absolute -top-6 -translate-x-1/2 select-none left-(--pos)"
style="--pos: ${percentNow}%"
>
<div
class="rounded bg-[#0f1116] ring-1 ring-zinc-700 text-zinc-100 px-1.5 py-0.5 text-[12px] shadow whitespace-nowrap w-max z-50"
class="rounded-sm bg-[#0f1116] ring-1 ring-zinc-700 text-zinc-100 px-1.5 py-0.5 text-[12px] shadow-sm whitespace-nowrap w-max z-50"
>
${percentNow}% • ${this.format(this.sendAmount)}
</div>
@@ -388,16 +388,16 @@ export class SendResourceModal extends LitElement {
${capPercent !== null
? html`
<div
class="pointer-events-none absolute top-1/2 -translate-y-1/2 h-3 w-[2px] bg-amber-400/80 shadow"
style="left:${capPercent}%;"
class="pointer-events-none absolute top-1/2 -translate-y-1/2 h-3 w-0.5 bg-amber-400/80 shadow-sm left-(--pos)"
style="--pos:${capPercent}%;"
title=${this.i18n.capTooltip()}
></div>
<div
class="pointer-events-none absolute top-full mt-1.5 -translate-x-1/2 select-none"
style="left:${capPercent}%"
class="pointer-events-none absolute top-full mt-1.5 -translate-x-1/2 select-none left-(--pos)"
style="--pos:${capPercent}%"
>
<div
class="rounded bg-[#0f1116] ring-1 ring-amber-400/40 text-amber-200 px-1 py-0.5 text-[11px] shadow whitespace-nowrap"
class="rounded-sm bg-[#0f1116] ring-1 ring-amber-400/40 text-amber-200 px-1 py-0.5 text-[11px] shadow-sm whitespace-nowrap"
>
${this.i18n.cap()}
</div>
@@ -451,7 +451,7 @@ export class SendResourceModal extends LitElement {
<button
class="h-10 min-w-24 rounded-lg px-3 text-sm font-semibold
text-zinc-100 bg-zinc-800 ring-1 ring-zinc-700
hover:bg-zinc-700 focus:outline-none
hover:bg-zinc-700 focus:outline-hidden
focus-visible:ring-2 focus-visible:ring-white/20"
@click=${() => this.closeModal()}
>
@@ -460,7 +460,7 @@ export class SendResourceModal extends LitElement {
<button
class="h-10 min-w-24 rounded-lg px-3 text-sm font-semibold text-white
bg-indigo-600 enabled:hover:bg-indigo-500
focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-400/50
focus:outline-hidden focus-visible:ring-2 focus-visible:ring-indigo-400/50
disabled:cursor-not-allowed disabled:opacity-50"
?disabled=${disabled}
@click=${() => this.confirm()}
@@ -541,9 +541,7 @@ export class SendResourceModal extends LitElement {
const allowed = this.limitAmount(this.sendAmount);
return html`
<div
class="absolute inset-0 z-[1100] flex items-center justify-center p-4"
>
<div class="absolute inset-0 z-1100 flex items-center justify-center p-4">
<div
class="absolute inset-0 bg-black/60 rounded-2xl"
@click=${() => this.closeModal()}
@@ -553,7 +551,7 @@ export class SendResourceModal extends LitElement {
role="dialog"
aria-modal="true"
aria-labelledby="send-title"
class="relative z-10 w-full max-w-[540px] focus:outline-none"
class="relative z-10 w-full max-w-135 focus:outline-hidden"
tabindex="0"
@keydown=${this.handleKeydown}
>
+16 -16
View File
@@ -189,7 +189,7 @@ export class SettingsModal extends LitElement implements Layer {
return html`
<div
class="modal-overlay fixed inset-0 bg-black/50 backdrop-blur-sm z-[2000] flex items-center justify-center p-4"
class="modal-overlay fixed inset-0 bg-black/50 backdrop-blur-xs z-2000 flex items-center justify-center p-4"
@contextmenu=${(e: Event) => e.preventDefault()}
>
<div
@@ -204,7 +204,7 @@ export class SettingsModal extends LitElement implements Layer {
alt="settings"
width="24"
height="24"
style="vertical-align: middle;"
class="align-middle"
/>
<h2 class="text-xl font-semibold text-white">
${translateText("user_setting.tab_basic")}
@@ -218,9 +218,9 @@ export class SettingsModal extends LitElement implements Layer {
</button>
</div>
<div class="p-4 space-y-3">
<div class="p-4 flex flex-col gap-3">
<div
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
>
<img src=${musicIcon} alt="musicIcon" width="20" height="20" />
<div class="flex-1">
@@ -242,7 +242,7 @@ export class SettingsModal extends LitElement implements Layer {
</div>
<div
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
>
<img
src=${musicIcon}
@@ -269,7 +269,7 @@ export class SettingsModal extends LitElement implements Layer {
</div>
<button
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
@click="${this.onTerrainButtonClick}"
>
<img src=${treeIcon} alt="treeIcon" width="20" height="20" />
@@ -289,7 +289,7 @@ export class SettingsModal extends LitElement implements Layer {
</button>
<button
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
@click="${this.onToggleEmojisButtonClick}"
>
<img src=${emojiIcon} alt="emojiIcon" width="20" height="20" />
@@ -309,7 +309,7 @@ export class SettingsModal extends LitElement implements Layer {
</button>
<button
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
@click="${this.onToggleDarkModeButtonClick}"
>
<img
@@ -334,7 +334,7 @@ export class SettingsModal extends LitElement implements Layer {
</button>
<button
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
@click="${this.onToggleSpecialEffectsButtonClick}"
>
<img
@@ -359,7 +359,7 @@ export class SettingsModal extends LitElement implements Layer {
</button>
<button
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
@click="${this.onToggleAlertFrameButtonClick}"
>
<img src=${sirenIcon} alt="alertFrame" width="20" height="20" />
@@ -379,7 +379,7 @@ export class SettingsModal extends LitElement implements Layer {
</button>
<button
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
@click="${this.onToggleStructureSpritesButtonClick}"
>
<img
@@ -404,7 +404,7 @@ export class SettingsModal extends LitElement implements Layer {
</button>
<button
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
@click="${this.onToggleCursorCostLabelButtonClick}"
>
<img
@@ -429,7 +429,7 @@ export class SettingsModal extends LitElement implements Layer {
</button>
<button
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
@click="${this.onToggleRandomNameModeButtonClick}"
>
<img src=${ninjaIcon} alt="ninjaIcon" width="20" height="20" />
@@ -449,7 +449,7 @@ export class SettingsModal extends LitElement implements Layer {
</button>
<button
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
@click="${this.onToggleLeftClickOpensMenu}"
>
<img src=${mouseIcon} alt="mouseIcon" width="20" height="20" />
@@ -469,7 +469,7 @@ export class SettingsModal extends LitElement implements Layer {
</button>
<button
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded text-white transition-colors"
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
@click="${this.onTogglePerformanceOverlayButtonClick}"
>
<img
@@ -495,7 +495,7 @@ export class SettingsModal extends LitElement implements Layer {
<div class="border-t border-slate-600 pt-3 mt-4">
<button
class="flex gap-3 items-center w-full text-left p-3 hover:bg-red-600/20 rounded text-red-400 transition-colors"
class="flex gap-3 items-center w-full text-left p-3 hover:bg-red-600/20 rounded-sm text-red-400 transition-colors"
@click="${this.onExitButtonClick}"
>
<img src=${exitIcon} alt="exitIcon" width="20" height="20" />
+3 -3
View File
@@ -93,13 +93,13 @@ export class SpawnTimer extends LitElement implements Layer {
}
return html`
<div class="w-full h-full flex z-[999]">
<div class="w-full h-full flex z-999">
${this.ratios.map((ratio, i) => {
const color = this.colors[i] || "rgba(0, 0, 0, 0.5)";
return html`
<div
class="h-full transition-all duration-100 ease-in-out"
style="width: ${ratio * 100}%; background-color: ${color};"
class="h-full transition-all duration-100 ease-in-out w-(--width) bg-(--bg)"
style="--width: ${ratio * 100}%; --bg: ${color};"
></div>
`;
})}
+2 -2
View File
@@ -129,8 +129,8 @@ export class TeamStats extends LitElement implements Layer {
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
>
<div
class="grid w-full"
style="grid-template-columns: repeat(${this.showUnits ? 5 : 4}, 1fr);"
class="grid w-full grid-cols-[repeat(var(--cols),1fr)]"
style="--cols:${this.showUnits ? 5 : 4};"
>
<!-- Header -->
<div class="contents font-bold bg-slate-700/50">
@@ -263,6 +263,62 @@ export class TerritoryLayer implements Layer {
baseColor, // Always draw white static semi-transparent ring
teamColor, // Pass the breathing ring color. White for FFA, Duos, Trios, Quads. Transparent team color for TEAM games.
);
// Draw breathing rings for teammates in team games (helps colorblind players identify teammates)
this.drawTeammateHighlights(minRad, maxRad, radius);
}
private drawTeammateHighlights(
minRad: number,
maxRad: number,
radius: number,
) {
const myPlayer = this.game.myPlayer();
if (myPlayer === null || myPlayer.team() === null) {
return;
}
const teammates = this.game
.playerViews()
.filter((p) => p !== myPlayer && myPlayer.isOnSameTeam(p));
// Smaller radius for teammates (more subtle than self highlight)
const teammateMinRad = 5;
const teammateMaxRad = 14;
const teammateRadius =
teammateMinRad +
(teammateMaxRad - teammateMinRad) *
((radius - minRad) / (maxRad - minRad));
const teamColors = Object.values(ColoredTeams);
for (const teammate of teammates) {
const center = teammate.nameLocation();
if (!center) {
continue;
}
const team = teammate.team();
let baseColor: Colord;
let breathingColor: Colord;
if (team !== null && teamColors.includes(team)) {
baseColor = this.theme.teamColor(team).alpha(0.5);
breathingColor = this.theme.teamColor(team).alpha(0.5);
} else {
baseColor = this.theme.spawnHighlightTeamColor();
breathingColor = this.theme.spawnHighlightTeamColor();
}
this.drawBreathingRing(
center.x,
center.y,
teammateMinRad,
teammateMaxRad,
teammateRadius,
baseColor,
breathingColor,
);
}
}
init() {
+7 -11
View File
@@ -130,9 +130,9 @@ export class UnitDisplay extends LitElement implements Layer {
return html`
<div
class="hidden 2xl:flex lg:flex fixed bottom-4 left-1/2 transform -translate-x-1/2 z-[1100] 2xl:flex-row xl:flex-col lg:flex-col 2xl:gap-5 xl:gap-2 lg:gap-2 justify-center items-center"
class="hidden 2xl:flex lg:flex fixed bottom-4 left-1/2 transform -translate-x-1/2 z-1100 2xl:flex-row xl:flex-col lg:flex-col 2xl:gap-5 xl:gap-2 lg:gap-2 justify-center items-center"
>
<div class="bg-gray-800/70 backdrop-blur-sm rounded-lg p-0.5">
<div class="bg-gray-800/70 backdrop-blur-xs rounded-lg p-0.5">
<div class="grid grid-rows-1 auto-cols-max grid-flow-col gap-1 w-fit">
${this.renderUnitItem(
cityIcon,
@@ -178,7 +178,7 @@ export class UnitDisplay extends LitElement implements Layer {
)}
</div>
</div>
<div class="bg-gray-800/70 backdrop-blur-sm rounded-lg p-0.5 w-fit">
<div class="bg-gray-800/70 backdrop-blur-xs rounded-lg p-0.5 w-fit">
<div class="grid grid-rows-1 auto-cols-max grid-flow-col gap-1">
${this.renderUnitItem(
warshipIcon,
@@ -242,7 +242,7 @@ export class UnitDisplay extends LitElement implements Layer {
${hovered
? html`
<div
class="absolute -top-[250%] left-1/2 -translate-x-1/2 text-gray-200 text-center w-max text-xs bg-gray-800/90 backdrop-blur-sm rounded p-1 z-20 shadow-lg pointer-events-none"
class="absolute -top-[250%] left-1/2 -translate-x-1/2 text-gray-200 text-center w-max text-xs bg-gray-800/90 backdrop-blur-xs rounded-sm p-1 z-20 shadow-lg pointer-events-none"
>
<div class="font-bold text-sm mb-1">
${translateText(
@@ -264,9 +264,9 @@ export class UnitDisplay extends LitElement implements Layer {
<div
class="${this.canBuild(unitType)
? ""
: "opacity-40"} border border-slate-500 rounded pr-2 pb-1 flex items-center gap-2 cursor-pointer
: "opacity-40"} border border-slate-500 rounded-sm pr-2 pb-1 flex items-center gap-2 cursor-pointer
${selected ? "hover:bg-gray-400/10" : "hover:bg-gray-800"}
rounded text-white ${selected ? "bg-slate-400/20" : ""}"
rounded-sm text-white ${selected ? "bg-slate-400/20" : ""}"
@click=${() => {
if (selected) {
this.uiState.ghostStructure = null;
@@ -302,11 +302,7 @@ export class UnitDisplay extends LitElement implements Layer {
${hotkey.toUpperCase()}
</div>`}
<div class="flex items-center gap-1 pt-1">
<img
src=${icon}
alt=${structureKey}
style="vertical-align: middle; width: 24px; height: 24px;"
/>
<img src=${icon} alt=${structureKey} class="align-middle size-6" />
${number !== null ? renderNumber(number) : null}
</div>
</div>
+11 -10
View File
@@ -56,7 +56,7 @@ export class WinModal extends LitElement implements Layer {
return html`
<div
class="${this.isVisible
? "fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-gray-800/70 p-6 rounded-lg z-[9999] shadow-2xl backdrop-blur-sm text-white w-[350px] max-w-[90%] md:w-[700px] md:max-w-[700px] animate-fadeIn"
? "fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-gray-800/70 p-6 shrink-0 rounded-lg z-9999 shadow-2xl backdrop-blur-xs text-white w-87.5 max-w-[90%] md:w-175 animate-fadeIn"
: "hidden"}"
>
<h2 class="m-0 mb-4 text-[26px] text-center text-white">
@@ -70,13 +70,13 @@ export class WinModal extends LitElement implements Layer {
>
<button
@click=${this._handleExit}
class="flex-1 px-3 py-3 text-base cursor-pointer bg-blue-500/60 text-white border-0 rounded transition-all duration-200 hover:bg-blue-500/80 hover:-translate-y-px active:translate-y-px"
class="flex-1 px-3 py-3 text-base cursor-pointer bg-blue-500/60 text-white border-0 rounded-sm transition-all duration-200 hover:bg-blue-500/80 hover:-translate-y-px active:translate-y-px"
>
${translateText("win_modal.exit")}
</button>
<button
@click=${this.hide}
class="flex-1 px-3 py-3 text-base cursor-pointer bg-blue-500/60 text-white border-0 rounded transition-all duration-200 hover:bg-blue-500/80 hover:-translate-y-px active:translate-y-px"
class="flex-1 px-3 py-3 text-base cursor-pointer bg-blue-500/60 text-white border-0 rounded-sm transition-all duration-200 hover:bg-blue-500/80 hover:-translate-y-px active:translate-y-px"
>
${this.isWin
? translateText("win_modal.keep")
@@ -123,13 +123,14 @@ export class WinModal extends LitElement implements Layer {
renderYoutubeTutorial() {
return html`
<div class="text-center mb-6 bg-black/30 p-2.5 rounded">
<div class="text-center mb-6 bg-black/30 p-2.5 rounded-sm">
<h3 class="text-xl font-semibold text-white mb-3">
${translateText("win_modal.youtube_tutorial")}
</h3>
<div class="relative w-full" style="padding-bottom: 56.25%;">
<!-- 56.25% = 9:16 -->
<div class="relative w-full pb-[56.25%]">
<iframe
class="absolute top-0 left-0 w-full h-full rounded"
class="absolute top-0 left-0 w-full h-full rounded-sm"
src="${this.isVisible
? "https://www.youtube.com/embed/EN2oOog3pSs"
: ""}"
@@ -145,7 +146,7 @@ export class WinModal extends LitElement implements Layer {
renderPatternButton() {
return html`
<div class="text-center mb-6 bg-black/30 p-2.5 rounded">
<div class="text-center mb-6 bg-black/30 p-2.5 rounded-sm">
<h3 class="text-xl font-semibold text-white mb-3">
${translateText("win_modal.support_openfront")}
</h3>
@@ -215,7 +216,7 @@ export class WinModal extends LitElement implements Layer {
}
steamWishlist(): TemplateResult {
return html`<p class="m-0 mb-5 text-center bg-black/30 p-2.5 rounded">
return html`<p class="m-0 mb-5 text-center bg-black/30 p-2.5 rounded-sm">
<a
href="https://store.steampowered.com/app/3560670"
target="_blank"
@@ -229,7 +230,7 @@ export class WinModal extends LitElement implements Layer {
discordDisplay(): TemplateResult {
return html`
<div class="text-center mb-6 bg-black/30 p-2.5 rounded">
<div class="text-center mb-6 bg-black/30 p-2.5 rounded-sm">
<h3 class="text-xl font-semibold text-white mb-3">
${translateText("win_modal.join_discord")}
</h3>
@@ -240,7 +241,7 @@ export class WinModal extends LitElement implements Layer {
href="https://discord.com/invite/openfront"
target="_blank"
rel="noopener noreferrer"
class="inline-block px-6 py-3 bg-indigo-600 text-white rounded font-semibold transition-all duration-200 hover:bg-indigo-700 hover:-translate-y-px no-underline"
class="inline-block px-6 py-3 bg-indigo-600 text-white rounded-sm font-semibold transition-all duration-200 hover:bg-indigo-700 hover:-translate-y-px no-underline"
>
${translateText("win_modal.join_server")}
</a>
+32 -3
View File
@@ -1,6 +1,35 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss";
@custom-variant hover (&:hover);
@theme {
--default-ring-width: 3px;
--default-ring-color: var(--color-blue-500);
}
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentColor);
}
input::placeholder,
textarea::placeholder {
color: var(--color-gray-400);
}
button:not(:disabled),
[role="button"]:not(:disabled) {
cursor: pointer;
}
dialog {
margin: auto;
}
}
* {
-webkit-box-sizing: border-box;
@@ -28,8 +28,9 @@ export function renderUnitTypeOptions({
return unitOptions.map(
({ type, translationKey }) => html`
<label
class="option-card ${disabledUnits.includes(type) ? "" : "selected"}"
style="width: 140px;"
class="option-card ${disabledUnits.includes(type)
? ""
: "selected"} w-35"
>
<div class="checkbox-icon"></div>
<input
@@ -40,7 +41,7 @@ export function renderUnitTypeOptions({
toggleUnit(type, checked);
}}
/>
<div class="option-card-title" style="text-align: center;">
<div class="option-card-title text-center">
${translateText(translationKey)}
</div>
</label>
+6
View File
@@ -171,6 +171,12 @@ export const GameConfigSchema = z.object({
gameType: z.enum(GameType),
gameMode: z.enum(GameMode),
gameMapSize: z.enum(GameMapSize),
publicGameModifiers: z
.object({
isCompact: z.boolean(),
isRandomSpawn: z.boolean(),
})
.optional(),
disableNations: z.boolean(),
bots: z.number().int().min(0).max(400),
infiniteGold: z.boolean(),
+4
View File
@@ -7,6 +7,7 @@ import {
Gold,
Player,
PlayerInfo,
PublicGameModifiers,
Team,
TerraNullius,
Tick,
@@ -34,6 +35,7 @@ export interface ServerConfig {
map: GameMapType,
mode: GameMode,
numPlayerTeams: TeamCountConfig | undefined,
isCompactMap?: boolean,
): number;
numWorkers(): number;
workerIndex(gameID: GameID): number;
@@ -57,6 +59,8 @@ export interface ServerConfig {
stripePublishableKey(): string;
allowedFlares(): string[] | undefined;
enableMatchmaking(): boolean;
getRandomPublicGameModifiers(): PublicGameModifiers;
supportsCompactMapForTeams(map: GameMapType): boolean;
}
export interface NukeMagnitude {
+21
View File
@@ -12,6 +12,7 @@ import {
Player,
PlayerInfo,
PlayerType,
PublicGameModifiers,
Quads,
TerrainType,
TerraNullius,
@@ -89,6 +90,7 @@ const numPlayersConfig = {
[GameMapType.StraitOfHormuz]: [40, 36, 30],
[GameMapType.Surrounded]: [42, 28, 14], // 3, 2, 1 player(s) per island
[GameMapType.Didier]: [100, 70, 50],
[GameMapType.AmazonRiver]: [50, 40, 30],
} as const satisfies Record<GameMapType, [number, number, number]>;
export abstract class DefaultServerConfig implements ServerConfig {
@@ -175,11 +177,16 @@ export abstract class DefaultServerConfig implements ServerConfig {
map: GameMapType,
mode: GameMode,
numPlayerTeams: TeamCountConfig | undefined,
isCompactMap?: boolean,
): number {
const [l, m, s] = numPlayersConfig[map] ?? [50, 30, 20];
const r = Math.random();
const base = r < 0.3 ? l : r < 0.6 ? m : s;
let p = Math.min(mode === GameMode.Team ? Math.ceil(base * 1.5) : base, l);
// Apply compact map 75% player reduction
if (isCompactMap) {
p = Math.max(3, Math.floor(p * 0.25));
}
if (numPlayerTeams === undefined) return p;
switch (numPlayerTeams) {
case Duos:
@@ -217,6 +224,20 @@ export abstract class DefaultServerConfig implements ServerConfig {
enableMatchmaking(): boolean {
return false;
}
getRandomPublicGameModifiers(): PublicGameModifiers {
return {
isRandomSpawn: Math.random() < 0.1, // 10% chance
isCompact: Math.random() < 0.05, // 5% chance
};
}
supportsCompactMapForTeams(map: GameMapType): boolean {
// Maps with smallest player count < 50 don't support compact map in team games
// The smallest player count is the 3rd number in numPlayersConfig
const [, , smallest] = numPlayersConfig[map] ?? [50, 30, 20];
return smallest >= 50;
}
}
export class DefaultConfig implements Config {
+12
View File
@@ -111,10 +111,16 @@ export enum GameMapType {
StraitOfHormuz = "Strait of Hormuz",
Surrounded = "Surrounded",
Didier = "Didier",
AmazonRiver = "Amazon River",
}
export type GameMapName = keyof typeof GameMapType;
/** Maps that have unusual thumbnail dimensions requiring object-fit: cover */
export function hasUnusualThumbnailSize(map: GameMapType): boolean {
return map === GameMapType.AmazonRiver;
}
export const mapCategories: Record<string, GameMapType[]> = {
continental: [
GameMapType.World,
@@ -151,6 +157,7 @@ export const mapCategories: Record<string, GameMapType[]> = {
GameMapType.Lemnos,
GameMapType.TwoLakes,
GameMapType.StraitOfHormuz,
GameMapType.AmazonRiver,
],
fantasy: [
GameMapType.Pangaea,
@@ -186,6 +193,11 @@ export enum GameMapSize {
Normal = "Normal",
}
export interface PublicGameModifiers {
isCompact: boolean;
isRandomSpawn: boolean;
}
export interface UnitInfo {
cost: (game: Game, player: Player) => Gold;
// Determines if its owner changes when its tile is conquered.
+28 -2
View File
@@ -2,6 +2,7 @@ import { PseudoRandom } from "../PseudoRandom";
import { GameStartInfo } from "../Schemas";
import {
Cell,
GameMapSize,
GameMode,
GameType,
HumansVsNations,
@@ -14,6 +15,7 @@ import { Nation as ManifestNation } from "./TerrainMapLoader";
/**
* Creates the nations array for a game, handling HumansVsNations mode specially.
* In HumansVsNations mode, the number of nations matches the number of human players to ensure fair gameplay.
* For compact maps, only 25% of the nations are used.
*/
export function createNationsForGame(
gameStart: GameStartInfo,
@@ -31,13 +33,23 @@ export function createNationsForGame(
new PlayerInfo(n.name, PlayerType.Nation, null, random.nextID()),
);
const isCompactMap = gameStart.config.gameMapSize === GameMapSize.Compact;
const isHumansVsNations =
gameStart.config.gameMode === GameMode.Team &&
gameStart.config.playerTeams === HumansVsNations;
// For non-HumansVsNations modes, simply use the manifest nations
// For compact maps, use only 25% of nations (minimum 1)
let effectiveNations = manifestNations;
if (isCompactMap && !isHumansVsNations) {
const targetCount = getCompactMapNationCount(manifestNations.length, true);
const shuffled = random.shuffleArray(manifestNations);
effectiveNations = shuffled.slice(0, targetCount);
}
// For non-HumansVsNations modes, simply use the effective nations
if (!isHumansVsNations) {
return manifestNations.map(toNation);
return effectiveNations.map(toNation);
}
// HumansVsNations mode: balance nation count to match human count
@@ -71,6 +83,20 @@ export function createNationsForGame(
return nations;
}
// For compact maps, only 25% of nations are used (minimum 1).
export function getCompactMapNationCount(
manifestNationCount: number,
isCompactMap: boolean,
): number {
if (manifestNationCount === 0) {
return 0;
}
if (isCompactMap) {
return Math.max(1, Math.floor(manifestNationCount * 0.25));
}
return manifestNationCount;
}
const PLURAL_NOUN = Symbol("plural!");
const NOUN = Symbol("noun!");
+23 -4
View File
@@ -61,6 +61,7 @@ const frequency: Partial<Record<GameMapName, number>> = {
StraitOfHormuz: 4,
Surrounded: 4,
Didier: 2,
AmazonRiver: 3,
};
interface MapWithMode {
@@ -92,25 +93,43 @@ export class MapPlaylist {
const playerTeams =
mode === GameMode.Team ? this.getTeamCount() : undefined;
let { isCompact, isRandomSpawn } = config.getRandomPublicGameModifiers();
// Duos, Trios, and Quads should not get random spawn (as it defeats the purpose)
if (
playerTeams === Duos ||
playerTeams === Trios ||
playerTeams === Quads
) {
isRandomSpawn = false;
}
// Maps with smallest player count < 50 don't support compact map in team games
// The smallest player count is the 3rd number in numPlayersConfig
if (mode === GameMode.Team && !config.supportsCompactMapForTeams(map)) {
isCompact = false;
}
// Create the default public game config (from your GameManager)
return {
donateGold: mode === GameMode.Team,
donateTroops: mode === GameMode.Team,
gameMap: map,
maxPlayers: config.lobbyMaxPlayers(map, mode, playerTeams),
maxPlayers: config.lobbyMaxPlayers(map, mode, playerTeams, isCompact),
gameType: GameType.Public,
gameMapSize: GameMapSize.Normal,
gameMapSize: isCompact ? GameMapSize.Compact : GameMapSize.Normal,
publicGameModifiers: { isCompact, isRandomSpawn },
difficulty:
playerTeams === HumansVsNations ? Difficulty.Hard : Difficulty.Easy,
infiniteGold: false,
infiniteTroops: false,
maxTimerValue: undefined,
instantBuild: false,
randomSpawn: false,
randomSpawn: isRandomSpawn,
disableNations: mode === GameMode.Team && playerTeams !== HumansVsNations,
gameMode: mode,
playerTeams,
bots: 400,
bots: isCompact ? 100 : 400,
spawnImmunityDuration: 5 * 10,
disabledUnits: [],
} satisfies GameConfig;
-10
View File
@@ -1,10 +0,0 @@
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./src/**/*.{html,ts,js}"],
theme: {
extend: {},
},
plugins: [],
darkMode: "class",
};
+7 -1
View File
@@ -1,6 +1,6 @@
import { JWK } from "jose";
import { GameEnv, ServerConfig } from "../../src/core/configuration/Config";
import { GameMapType } from "../../src/core/game/Game";
import { GameMapType, PublicGameModifiers } from "../../src/core/game/Game";
import { GameID } from "../../src/core/Schemas";
export class TestServerConfig implements ServerConfig {
@@ -82,4 +82,10 @@ export class TestServerConfig implements ServerConfig {
gitCommit(): string {
throw new Error("Method not implemented.");
}
getRandomPublicGameModifiers(): PublicGameModifiers {
return { isCompact: false, isRandomSpawn: false };
}
supportsCompactMapForTeams(map: GameMapType): boolean {
throw new Error("Method not implemented.");
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
// Modules
"module": "ESNext",
"rootDir": ".",
"moduleResolution": "node",
"moduleResolution": "bundler",
"baseUrl": ".",
"paths": {
"resources/*": ["resources/*"]
+2
View File
@@ -1,3 +1,4 @@
import tailwindcss from "@tailwindcss/vite";
import { execSync } from "child_process";
import path from "path";
import { fileURLToPath } from "url";
@@ -67,6 +68,7 @@ export default defineConfig(({ mode }) => {
},
],
}),
tailwindcss(),
],
define: {