mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-23 14:19:09 +00:00
format
This commit is contained in:
+30
-23
@@ -1,12 +1,12 @@
|
||||
import eslintPluginLocal from "./eslint-plugin-local/plugin.js";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import globals from "globals";
|
||||
import { includeIgnoreFile } from "@eslint/compat";
|
||||
import jest from "eslint-plugin-jest";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { includeIgnoreFile } from "@eslint/compat";
|
||||
import pluginJs from "@eslint/js";
|
||||
import stylistic from "@stylistic/eslint-plugin";
|
||||
import jest from "eslint-plugin-jest";
|
||||
import globals from "globals";
|
||||
import tseslint from "typescript-eslint";
|
||||
import eslintPluginLocal from "./eslint-plugin-local/plugin.js";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
@@ -100,7 +100,10 @@ export default [
|
||||
"function-call-argument-newline": ["error", "consistent"],
|
||||
"max-depth": ["error", { max: 5 }],
|
||||
"max-len": ["error", { code: 120 }],
|
||||
"max-lines": ["error", { max: 676, skipBlankLines: true, skipComments: true }],
|
||||
"max-lines": [
|
||||
"error",
|
||||
{ max: 676, skipBlankLines: true, skipComments: true },
|
||||
],
|
||||
"max-lines-per-function": ["error", { max: 561 }],
|
||||
"no-loss-of-precision": "error",
|
||||
"no-multi-spaces": "error",
|
||||
@@ -108,21 +111,30 @@ export default [
|
||||
"no-trailing-spaces": "error",
|
||||
"object-curly-newline": ["error", { multiline: true, consistent: true }],
|
||||
"object-curly-spacing": ["error", "always"],
|
||||
"object-property-newline": ["error", { allowAllPropertiesOnSameLine: true }],
|
||||
"object-property-newline": [
|
||||
"error",
|
||||
{ allowAllPropertiesOnSameLine: true },
|
||||
],
|
||||
"object-shorthand": ["error", "always"],
|
||||
"no-undef": "error",
|
||||
"no-unused-vars": "off", // @typescript-eslint/no-unused-vars
|
||||
"prefer-destructuring": ["error", {
|
||||
array: false,
|
||||
object: true,
|
||||
}],
|
||||
"prefer-destructuring": [
|
||||
"error",
|
||||
{
|
||||
array: false,
|
||||
object: true,
|
||||
},
|
||||
],
|
||||
"quote-props": ["error", "consistent-as-needed"],
|
||||
"space-before-blocks": ["error", "always"],
|
||||
"space-before-function-paren": ["error", {
|
||||
anonymous: "always",
|
||||
named: "never",
|
||||
asyncArrow: "always",
|
||||
}],
|
||||
"space-before-function-paren": [
|
||||
"error",
|
||||
{
|
||||
anonymous: "always",
|
||||
named: "never",
|
||||
asyncArrow: "always",
|
||||
},
|
||||
],
|
||||
"space-infix-ops": "off",
|
||||
},
|
||||
},
|
||||
@@ -150,17 +162,12 @@ export default [
|
||||
...globals.jest,
|
||||
},
|
||||
},
|
||||
files: [
|
||||
"**/*.test.{js,ts,jsx,tsx}",
|
||||
"tests/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
files: ["**/*.test.{js,ts,jsx,tsx}", "tests/**/*.{js,ts,jsx,tsx}"],
|
||||
plugins: ["jest"],
|
||||
...jest.configs["flat/style"],
|
||||
},
|
||||
{
|
||||
files: [
|
||||
"src/client/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
files: ["src/client/**/*.{js,ts,jsx,tsx}"],
|
||||
rules: {
|
||||
// Disabled rules for frontend
|
||||
"sort-keys": "off",
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
import { translateText } from "../client/Utils";
|
||||
import { ServerConfig } from "../core/configuration/Config";
|
||||
import { getConfig } from "../core/configuration/ConfigLoader";
|
||||
import { EventBus } from "../core/EventBus";
|
||||
import { PlayerActions, UnitType } from "../core/game/Game";
|
||||
import { TileRef } from "../core/game/GameMap";
|
||||
import { GameMapLoader } from "../core/game/GameMapLoader";
|
||||
import {
|
||||
AutoUpgradeEvent,
|
||||
DoBoatAttackEvent,
|
||||
DoGroundAttackEvent,
|
||||
InputHandler,
|
||||
MouseMoveEvent,
|
||||
MouseUpEvent,
|
||||
} from "./InputHandler";
|
||||
ErrorUpdate,
|
||||
GameUpdateType,
|
||||
GameUpdateViewData,
|
||||
HashUpdate,
|
||||
WinUpdate,
|
||||
} from "../core/game/GameUpdates";
|
||||
import { GameView, PlayerView } from "../core/game/GameView";
|
||||
import { loadTerrainMap, TerrainMapData } from "../core/game/TerrainMapLoader";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import {
|
||||
ClientID,
|
||||
GameID,
|
||||
@@ -14,16 +23,20 @@ import {
|
||||
PlayerRecord,
|
||||
ServerMessage,
|
||||
} from "../core/Schemas";
|
||||
import { createGameRecord } from "../core/Util";
|
||||
import { WorkerClient } from "../core/worker/WorkerClient";
|
||||
import { createRenderer, GameRenderer } from "./graphics/GameRenderer";
|
||||
import {
|
||||
ErrorUpdate,
|
||||
GameUpdateType,
|
||||
GameUpdateViewData,
|
||||
HashUpdate,
|
||||
WinUpdate,
|
||||
} from "../core/game/GameUpdates";
|
||||
import { GameRenderer, createRenderer } from "./graphics/GameRenderer";
|
||||
import { GameView, PlayerView } from "../core/game/GameView";
|
||||
import { PlayerActions, UnitType } from "../core/game/Game";
|
||||
AutoUpgradeEvent,
|
||||
DoBoatAttackEvent,
|
||||
DoGroundAttackEvent,
|
||||
InputHandler,
|
||||
MouseMoveEvent,
|
||||
MouseUpEvent,
|
||||
} from "./InputHandler";
|
||||
import { endGame, startGame, startTime } from "./LocalPersistantStats";
|
||||
import { getPersistentID } from "./Main";
|
||||
import { terrainMapFileLoader } from "./TerrainMapFileLoader";
|
||||
import {
|
||||
SendAttackIntentEvent,
|
||||
SendBoatAttackIntentEvent,
|
||||
@@ -32,20 +45,7 @@ import {
|
||||
SendUpgradeStructureIntentEvent,
|
||||
Transport,
|
||||
} from "./Transport";
|
||||
import { TerrainMapData, loadTerrainMap } from "../core/game/TerrainMapLoader";
|
||||
import { endGame, startGame, startTime } from "./LocalPersistantStats";
|
||||
import { EventBus } from "../core/EventBus";
|
||||
import { GameMapLoader } from "../core/game/GameMapLoader";
|
||||
import { ServerConfig } from "../core/configuration/Config";
|
||||
import { TileRef } from "../core/game/GameMap";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import { WorkerClient } from "../core/worker/WorkerClient";
|
||||
import { createCanvas } from "./Utils";
|
||||
import { createGameRecord } from "../core/Util";
|
||||
import { getConfig } from "../core/configuration/ConfigLoader";
|
||||
import { getPersistentID } from "./Main";
|
||||
import { terrainMapFileLoader } from "./TerrainMapFileLoader";
|
||||
import { translateText } from "../client/Utils";
|
||||
|
||||
export type LobbyConfig = {
|
||||
serverConfig: ServerConfig;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Cosmetics, CosmeticsSchema, Pattern } from "../core/CosmeticSchemas";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
StripeCreateCheckoutSessionResponseSchema,
|
||||
UserMeResponse,
|
||||
} from "../core/ApiSchemas";
|
||||
import { Cosmetics, CosmeticsSchema, Pattern } from "../core/CosmeticSchemas";
|
||||
import { getApiBase, getAuthHeader } from "./jwt";
|
||||
import { z } from "zod";
|
||||
|
||||
export async function patterns(
|
||||
userMe: UserMeResponse | null,
|
||||
@@ -41,7 +41,7 @@ export async function handlePurchase(priceId: string) {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"authorization": getAuthHeader(),
|
||||
authorization: getAuthHeader(),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
priceId,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { FlagSchema } from "../core/Schemas";
|
||||
import { renderPlayerFlag } from "../core/CustomFlag";
|
||||
import { FlagSchema } from "../core/Schemas";
|
||||
|
||||
const flagKey = "flag";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query, state } from "lit/decorators.js";
|
||||
import Countries from "./data/countries.json";
|
||||
|
||||
@@ -33,10 +33,13 @@ export class FlagInputModal extends LitElement {
|
||||
<div
|
||||
class="flex flex-wrap justify-evenly gap-[1rem] overflow-y-auto overflow-x-hidden h-[90%]"
|
||||
>
|
||||
${this.isModalOpen ? Countries.filter(
|
||||
(country) => !country.restricted && this.includedInSearch(country),
|
||||
).map(
|
||||
(country) => html`
|
||||
${
|
||||
this.isModalOpen
|
||||
? Countries.filter(
|
||||
(country) =>
|
||||
!country.restricted && this.includedInSearch(country),
|
||||
).map(
|
||||
(country) => html`
|
||||
<button
|
||||
@click=${() => {
|
||||
this.setFlag(country.code);
|
||||
@@ -61,7 +64,9 @@ export class FlagInputModal extends LitElement {
|
||||
<span class="country-name">${country.name}</span>
|
||||
</button>
|
||||
`,
|
||||
) : html``}
|
||||
)
|
||||
: html``
|
||||
}
|
||||
</div>
|
||||
</o-modal>
|
||||
`;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { translateText } from "./Utils";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import "./components/Difficulties";
|
||||
import "./components/Maps";
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators.js";
|
||||
import { getAltKey, getModifierKey, translateText } from "../client/Utils";
|
||||
|
||||
|
||||
@@ -2,6 +2,22 @@
|
||||
import "./components/Difficulties";
|
||||
import "./components/Maps";
|
||||
import "./components/baseComponents/Modal";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query, state } from "lit/decorators.js";
|
||||
import randomMap from "../../resources/images/RandomMap.webp";
|
||||
import { translateText } from "../client/Utils";
|
||||
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
|
||||
import {
|
||||
Difficulty,
|
||||
Duos,
|
||||
GameMapType,
|
||||
GameMode,
|
||||
mapCategories,
|
||||
Quads,
|
||||
Trios,
|
||||
UnitType,
|
||||
} from "../core/game/Game";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import {
|
||||
ClientInfo,
|
||||
GameConfig,
|
||||
@@ -9,26 +25,10 @@ import {
|
||||
GameInfoSchema,
|
||||
TeamCountConfig,
|
||||
} from "../core/Schemas";
|
||||
import {
|
||||
Difficulty,
|
||||
Duos,
|
||||
GameMapType,
|
||||
GameMode,
|
||||
Quads,
|
||||
Trios,
|
||||
UnitType,
|
||||
mapCategories,
|
||||
} from "../core/game/Game";
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, query, state } from "lit/decorators.js";
|
||||
import { generateID } from "../core/Util";
|
||||
import { DifficultyDescription } from "./components/Difficulties";
|
||||
import { JoinLobbyEvent } from "./Main";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import { generateID } from "../core/Util";
|
||||
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
|
||||
import randomMap from "../../resources/images/RandomMap.webp";
|
||||
import { renderUnitTypeOptions } from "./utilities/RenderUnitTypeOptions";
|
||||
import { translateText } from "../client/Utils";
|
||||
|
||||
@customElement("host-lobby-modal")
|
||||
export class HostLobbyModal extends LitElement {
|
||||
@@ -206,8 +206,10 @@ export class HostLobbyModal extends LitElement {
|
||||
>
|
||||
<map-display
|
||||
.mapKey=${mapKey}
|
||||
.selected=${!this.useRandomMap &&
|
||||
this.selectedMap === mapValue}
|
||||
.selected=${
|
||||
!this.useRandomMap &&
|
||||
this.selectedMap === mapValue
|
||||
}
|
||||
.translation=${translateText(
|
||||
`map.${mapKey?.toLowerCase()}`,
|
||||
)}
|
||||
@@ -248,9 +250,9 @@ export class HostLobbyModal extends LitElement {
|
||||
.map(
|
||||
([key, value]) => html`
|
||||
<div
|
||||
class="option-card ${this.selectedDifficulty === value
|
||||
? "selected"
|
||||
: ""}"
|
||||
class="option-card ${
|
||||
this.selectedDifficulty === value ? "selected" : ""
|
||||
}"
|
||||
@click=${() => this.handleDifficultySelection(value)}
|
||||
>
|
||||
<difficulty-display
|
||||
@@ -303,17 +305,19 @@ export class HostLobbyModal extends LitElement {
|
||||
${[2, 3, 4, 5, 6, 7, Quads, Trios, Duos].map(
|
||||
(o) => html`
|
||||
<div
|
||||
class="option-card ${this.teamCount === o
|
||||
? "selected"
|
||||
: ""}"
|
||||
class="option-card ${
|
||||
this.teamCount === o ? "selected" : ""
|
||||
}"
|
||||
@click=${() => this.handleTeamCountSelection(o)}
|
||||
>
|
||||
<div class="option-card-title">
|
||||
${typeof o === "string"
|
||||
? translateText(`public_lobby.teams_${o}`)
|
||||
: translateText("public_lobby.teams", {
|
||||
num: o,
|
||||
})}
|
||||
${
|
||||
typeof o === "string"
|
||||
? translateText(`public_lobby.teams_${o}`)
|
||||
: translateText("public_lobby.teams", {
|
||||
num: o,
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
@@ -457,9 +461,9 @@ export class HostLobbyModal extends LitElement {
|
||||
style="display: flex; flex-wrap: wrap; justify-content: center; gap: 12px;"
|
||||
>
|
||||
${renderUnitTypeOptions({
|
||||
disabledUnits: this.disabledUnits,
|
||||
toggleUnit: this.toggleUnit.bind(this),
|
||||
})}
|
||||
disabledUnits: this.disabledUnits,
|
||||
toggleUnit: this.toggleUnit.bind(this),
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -482,11 +486,12 @@ export class HostLobbyModal extends LitElement {
|
||||
(client) => html`
|
||||
<span class="player-tag">
|
||||
${client.username}
|
||||
${client.clientID === this.lobbyCreatorClientID
|
||||
? html`<span class="host-badge"
|
||||
${
|
||||
client.clientID === this.lobbyCreatorClientID
|
||||
? html`<span class="host-badge"
|
||||
>(${translateText("host_modal.host_badge")})</span
|
||||
>`
|
||||
: html`
|
||||
: html`
|
||||
<button
|
||||
class="remove-player-btn"
|
||||
@click=${() => this.kickPlayer(client.clientID)}
|
||||
@@ -494,7 +499,8 @@ export class HostLobbyModal extends LitElement {
|
||||
>
|
||||
×
|
||||
</button>
|
||||
`}
|
||||
`
|
||||
}
|
||||
</span>
|
||||
`,
|
||||
)}
|
||||
@@ -695,8 +701,8 @@ export class HostLobbyModal extends LitElement {
|
||||
await this.putGameConfig();
|
||||
console.log(
|
||||
`Starting private game with map: ${
|
||||
GameMapType[this.selectedMap as keyof typeof GameMapType]} ${
|
||||
this.useRandomMap ? " (Randomly selected)" : ""}`,
|
||||
GameMapType[this.selectedMap as keyof typeof GameMapType]
|
||||
} ${this.useRandomMap ? " (Randomly selected)" : ""}`,
|
||||
);
|
||||
this.close();
|
||||
const config = await getServerConfigFromClient();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { EventBus, GameEvent } from "../core/EventBus";
|
||||
import { ReplaySpeedMultiplier } from "./utilities/ReplaySpeedMultiplier";
|
||||
import { UnitType } from "../core/game/Game";
|
||||
import { UnitView } from "../core/game/GameView";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import { ReplaySpeedMultiplier } from "./utilities/ReplaySpeedMultiplier";
|
||||
|
||||
export class MouseUpEvent implements GameEvent {
|
||||
constructor(
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import "./components/baseComponents/Button";
|
||||
import "./components/baseComponents/Modal";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query, state } from "lit/decorators.js";
|
||||
import { translateText } from "../client/Utils";
|
||||
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
|
||||
import { GameInfo, GameInfoSchema } from "../core/Schemas";
|
||||
import { LitElement, html } from "lit";
|
||||
import { getClientID } from "../core/Util";
|
||||
import {
|
||||
WorkerApiArchivedGameLobbySchema,
|
||||
WorkerApiGameIdExistsSchema,
|
||||
} from "../core/WorkerSchemas";
|
||||
import { customElement, query, state } from "lit/decorators.js";
|
||||
import { JoinLobbyEvent } from "./Main";
|
||||
import { getClientID } from "../core/Util";
|
||||
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
|
||||
import { translateText } from "../client/Utils";
|
||||
|
||||
@customElement("join-private-lobby-modal")
|
||||
export class JoinPrivateLobbyModal extends LitElement {
|
||||
@@ -82,13 +82,16 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
${this.message}
|
||||
</div>
|
||||
<div class="options-layout">
|
||||
${this.hasJoined && this.players.length > 0
|
||||
? html` <div class="options-section">
|
||||
${
|
||||
this.hasJoined && this.players.length > 0
|
||||
? html` <div class="options-section">
|
||||
<div class="option-title">
|
||||
${this.players.length}
|
||||
${this.players.length === 1
|
||||
? translateText("private_lobby.player")
|
||||
: translateText("private_lobby.players")}
|
||||
${
|
||||
this.players.length === 1
|
||||
? translateText("private_lobby.player")
|
||||
: translateText("private_lobby.players")
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="players-list">
|
||||
@@ -97,16 +100,19 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
)}
|
||||
</div>
|
||||
</div>`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
${!this.hasJoined
|
||||
? html` <o-button
|
||||
${
|
||||
!this.hasJoined
|
||||
? html` <o-button
|
||||
title=${translateText("private_lobby.join_lobby")}
|
||||
block
|
||||
@click=${this.joinLobby}
|
||||
></o-button>`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
</o-modal>
|
||||
`;
|
||||
|
||||
+12
-12
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
import "./LanguageModal";
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
import ar from "../../resources/lang/ar.json";
|
||||
@@ -289,16 +289,16 @@ export class LangSelector extends LitElement {
|
||||
this.languageList.find((l) => l.code === this.currentLang) ??
|
||||
(this.currentLang === "debug"
|
||||
? {
|
||||
code: "debug",
|
||||
native: "Debug",
|
||||
en: "Debug",
|
||||
svg: "xx",
|
||||
}
|
||||
code: "debug",
|
||||
native: "Debug",
|
||||
en: "Debug",
|
||||
svg: "xx",
|
||||
}
|
||||
: {
|
||||
native: "English",
|
||||
en: "English",
|
||||
svg: "uk_us_flag",
|
||||
});
|
||||
native: "English",
|
||||
en: "English",
|
||||
svg: "uk_us_flag",
|
||||
});
|
||||
|
||||
return html`
|
||||
<div class="container__row">
|
||||
@@ -327,7 +327,7 @@ export class LangSelector extends LitElement {
|
||||
.languageList=${this.languageList}
|
||||
.currentLang=${this.currentLang}
|
||||
@language-selected=${(e: CustomEvent) =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
this.changeLanguage(e.detail.lang)}
|
||||
@close-modal=${() => (this.showModal = false)}
|
||||
></language-modal>
|
||||
@@ -348,7 +348,7 @@ function flattenTranslations(
|
||||
if (typeof value === "string") {
|
||||
result[fullKey] = value;
|
||||
} else if (value && typeof value === "object" && !Array.isArray(value)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
flattenTranslations(value, fullKey, result);
|
||||
} else {
|
||||
console.warn("Unknown type", typeof value, value);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { translateText } from "../client/Utils";
|
||||
|
||||
@@ -111,7 +111,7 @@ export class LanguageModal extends LitElement {
|
||||
<button
|
||||
class="${buttonClasses}"
|
||||
@click=${() =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
this.selectLanguage(lang.code)}
|
||||
>
|
||||
<img
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { ID } from "../core/BaseSchemas";
|
||||
import {
|
||||
GameConfig,
|
||||
GameConfigSchema,
|
||||
@@ -5,9 +7,7 @@ import {
|
||||
GameRecord,
|
||||
GameRecordSchema,
|
||||
} from "../core/Schemas";
|
||||
import { ID } from "../core/BaseSchemas";
|
||||
import { replacer } from "../core/Util";
|
||||
import { z } from "zod";
|
||||
|
||||
const LocalStatsDataSchema = z.record(
|
||||
ID,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { EventBus } from "../core/EventBus";
|
||||
import {
|
||||
AllPlayersStats,
|
||||
ClientMessage,
|
||||
@@ -10,12 +12,10 @@ import {
|
||||
Turn,
|
||||
} from "../core/Schemas";
|
||||
import { createGameRecord, decompressGameRecord, replacer } from "../core/Util";
|
||||
import { EventBus } from "../core/EventBus";
|
||||
import { LobbyConfig } from "./ClientGameRunner";
|
||||
import { ReplaySpeedChangeEvent } from "./InputHandler";
|
||||
import { defaultReplaySpeedMultiplier } from "./utilities/ReplaySpeedMultiplier";
|
||||
import { getPersistentID } from "./Main";
|
||||
import { z } from "zod";
|
||||
import { defaultReplaySpeedMultiplier } from "./utilities/ReplaySpeedMultiplier";
|
||||
|
||||
export class LocalServer {
|
||||
// All turns from the game record on replay.
|
||||
@@ -117,10 +117,9 @@ export class LocalServer {
|
||||
}
|
||||
if (archivedHash !== clientMsg.hash) {
|
||||
console.error(
|
||||
`desync detected on turn ${
|
||||
clientMsg.turnNumber}, client hash: ${
|
||||
clientMsg.hash}, server hash: ${
|
||||
archivedHash}`,
|
||||
`desync detected on turn ${clientMsg.turnNumber}, client hash: ${
|
||||
clientMsg.hash
|
||||
}, server hash: ${archivedHash}`,
|
||||
);
|
||||
this.clientMessage({
|
||||
type: "desync",
|
||||
|
||||
+21
-17
@@ -8,37 +8,41 @@ import "./components/NewsButton";
|
||||
import "./components/baseComponents/Button";
|
||||
import "./components/baseComponents/Modal";
|
||||
import "./styles.css";
|
||||
import { GameRecord, GameStartInfo } from "../core/Schemas";
|
||||
import { discordLogin, getUserMe, isLoggedIn, logOut } from "./jwt";
|
||||
import { generateCryptoRandomUUID, incrementGamesPlayed, translateText } from "./Utils";
|
||||
import { DarkModeButton } from "./DarkModeButton";
|
||||
import version from "../../resources/version.txt";
|
||||
import { UserMeResponse } from "../core/ApiSchemas";
|
||||
import { ID } from "../core/BaseSchemas";
|
||||
import { ServerConfig } from "../core/configuration/Config";
|
||||
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
|
||||
import { EventBus } from "../core/EventBus";
|
||||
import { GameType } from "../core/game/Game";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import { GameRecord, GameStartInfo } from "../core/Schemas";
|
||||
import { getClientID } from "../core/Util";
|
||||
import { joinLobby } from "./ClientGameRunner";
|
||||
import { OButton } from "./components/baseComponents/Button";
|
||||
import { NewsButton } from "./components/NewsButton";
|
||||
import { DarkModeButton } from "./DarkModeButton";
|
||||
import { FlagInput } from "./FlagInput";
|
||||
import { FlagInputModal } from "./FlagInputModal";
|
||||
import { GameStartingModal } from "./GameStartingModal";
|
||||
import { GameType } from "../core/game/Game";
|
||||
import { HelpModal } from "./HelpModal";
|
||||
import { HostLobbyModal } from "./HostLobbyModal";
|
||||
import { ID } from "../core/BaseSchemas";
|
||||
import { JoinPrivateLobbyModal } from "./JoinPrivateLobbyModal";
|
||||
import { discordLogin, getUserMe, isLoggedIn, logOut } from "./jwt";
|
||||
import { LangSelector } from "./LangSelector";
|
||||
import { LanguageModal } from "./LanguageModal";
|
||||
import { NewsButton } from "./components/NewsButton";
|
||||
import { NewsModal } from "./NewsModal";
|
||||
import { OButton } from "./components/baseComponents/Button";
|
||||
import { PublicLobby } from "./PublicLobby";
|
||||
import { SendKickPlayerIntentEvent } from "./Transport";
|
||||
import { ServerConfig } from "../core/configuration/Config";
|
||||
import { SinglePlayerModal } from "./SinglePlayerModal";
|
||||
import { TerritoryPatternsModal } from "./TerritoryPatternsModal";
|
||||
import { UserMeResponse } from "../core/ApiSchemas";
|
||||
import { UserSettingModal } from "./UserSettingModal";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import { SendKickPlayerIntentEvent } from "./Transport";
|
||||
import { UsernameInput } from "./UsernameInput";
|
||||
import { getClientID } from "../core/Util";
|
||||
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
|
||||
import { joinLobby } from "./ClientGameRunner";
|
||||
import version from "../../resources/version.txt";
|
||||
import { UserSettingModal } from "./UserSettingModal";
|
||||
import {
|
||||
generateCryptoRandomUUID,
|
||||
incrementGamesPlayed,
|
||||
translateText,
|
||||
} from "./Utils";
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import "./components/baseComponents/Button";
|
||||
import "./components/baseComponents/Modal";
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators.js";
|
||||
import changelog from "../../resources/changelog.md";
|
||||
import { resolveMarkdown } from "lit-markdown";
|
||||
import changelog from "../../resources/changelog.md";
|
||||
import { translateText } from "../client/Utils";
|
||||
|
||||
@customElement("news-modal")
|
||||
|
||||
+27
-24
@@ -1,12 +1,12 @@
|
||||
import { GameID, GameInfo } from "../core/Schemas";
|
||||
import { GameMapType, GameMode } from "../core/game/Game";
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { ApiPublicLobbiesResponseSchema } from "../core/ExpressSchemas";
|
||||
import { JoinLobbyEvent } from "./Main";
|
||||
import { getClientID } from "../core/Util";
|
||||
import { terrainMapFileLoader } from "./TerrainMapFileLoader";
|
||||
import { translateText } from "../client/Utils";
|
||||
import { ApiPublicLobbiesResponseSchema } from "../core/ExpressSchemas";
|
||||
import { GameMapType, GameMode } from "../core/game/Game";
|
||||
import { GameID, GameInfo } from "../core/Schemas";
|
||||
import { getClientID } from "../core/Util";
|
||||
import { JoinLobbyEvent } from "./Main";
|
||||
import { terrainMapFileLoader } from "./TerrainMapFileLoader";
|
||||
|
||||
@customElement("public-lobby")
|
||||
export class PublicLobby extends LitElement {
|
||||
@@ -126,20 +126,21 @@ export class PublicLobby extends LitElement {
|
||||
? "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.isButtonDebounced
|
||||
? "opacity-70 cursor-not-allowed"
|
||||
: ""}"
|
||||
this.isButtonDebounced ? "opacity-70 cursor-not-allowed" : ""
|
||||
}"
|
||||
>
|
||||
${mapImageSrc
|
||||
? html`<img
|
||||
${
|
||||
mapImageSrc
|
||||
? 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)"
|
||||
/>`
|
||||
: html`<div
|
||||
: html`<div
|
||||
class="place-self-start col-span-full row-span-full h-full -z-10 bg-gray-300"
|
||||
></div>`}
|
||||
></div>`
|
||||
}
|
||||
<div
|
||||
class="flex flex-col justify-between h-full col-span-full row-span-full p-4 md:p-6 text-right z-0"
|
||||
>
|
||||
@@ -149,17 +150,19 @@ export class PublicLobby extends LitElement {
|
||||
</div>
|
||||
<div class="text-md font-medium text-blue-100">
|
||||
<span
|
||||
class="text-sm ${this.isLobbyHighlighted
|
||||
? "text-green-600"
|
||||
: "text-blue-600"} bg-white rounded-sm px-1"
|
||||
class="text-sm ${
|
||||
this.isLobbyHighlighted ? "text-green-600" : "text-blue-600"
|
||||
} bg-white rounded-sm px-1"
|
||||
>
|
||||
${lobby.gameConfig.gameMode === GameMode.Team
|
||||
? typeof teamCount === "string"
|
||||
? translateText(`public_lobby.teams_${teamCount}`)
|
||||
: translateText("public_lobby.teams", {
|
||||
num: teamCount ?? 0,
|
||||
})
|
||||
: translateText("game_mode.ffa")}</span
|
||||
${
|
||||
lobby.gameConfig.gameMode === GameMode.Team
|
||||
? typeof teamCount === "string"
|
||||
? translateText(`public_lobby.teams_${teamCount}`)
|
||||
: translateText("public_lobby.teams", {
|
||||
num: teamCount ?? 0,
|
||||
})
|
||||
: translateText("game_mode.ffa")
|
||||
}</span
|
||||
>
|
||||
<span
|
||||
>${translateText(
|
||||
|
||||
@@ -2,29 +2,29 @@ import "./components/Difficulties";
|
||||
import "./components/Maps";
|
||||
import "./components/baseComponents/Button";
|
||||
import "./components/baseComponents/Modal";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query, state } from "lit/decorators.js";
|
||||
import randomMap from "../../resources/images/RandomMap.webp";
|
||||
import { translateText } from "../client/Utils";
|
||||
import {
|
||||
Difficulty,
|
||||
Duos,
|
||||
GameMapType,
|
||||
GameMode,
|
||||
GameType,
|
||||
mapCategories,
|
||||
Quads,
|
||||
Trios,
|
||||
UnitType,
|
||||
mapCategories,
|
||||
} from "../core/game/Game";
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, query, state } from "lit/decorators.js";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import { TeamCountConfig } from "../core/Schemas";
|
||||
import { generateID, getClientID } from "../core/Util";
|
||||
import { DifficultyDescription } from "./components/Difficulties";
|
||||
import { FlagInput } from "./FlagInput";
|
||||
import { JoinLobbyEvent } from "./Main";
|
||||
import { TeamCountConfig } from "../core/Schemas";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import { UsernameInput } from "./UsernameInput";
|
||||
import randomMap from "../../resources/images/RandomMap.webp";
|
||||
import { renderUnitTypeOptions } from "./utilities/RenderUnitTypeOptions";
|
||||
import { translateText } from "../client/Utils";
|
||||
|
||||
@customElement("single-player-modal")
|
||||
export class SinglePlayerModal extends LitElement {
|
||||
@@ -96,8 +96,10 @@ export class SinglePlayerModal extends LitElement {
|
||||
>
|
||||
<map-display
|
||||
.mapKey=${mapKey}
|
||||
.selected=${!this.useRandomMap &&
|
||||
this.selectedMap === mapValue}
|
||||
.selected=${
|
||||
!this.useRandomMap &&
|
||||
this.selectedMap === mapValue
|
||||
}
|
||||
.translation=${translateText(
|
||||
`map.${mapKey?.toLowerCase()}`,
|
||||
)}
|
||||
@@ -110,9 +112,9 @@ export class SinglePlayerModal extends LitElement {
|
||||
`,
|
||||
)}
|
||||
<div
|
||||
class="option-card random-map ${this.useRandomMap
|
||||
? "selected"
|
||||
: ""}"
|
||||
class="option-card random-map ${
|
||||
this.useRandomMap ? "selected" : ""
|
||||
}"
|
||||
@click=${this.handleRandomMapToggle}
|
||||
>
|
||||
<div class="option-image">
|
||||
@@ -140,9 +142,9 @@ export class SinglePlayerModal extends LitElement {
|
||||
.map(
|
||||
([key, value]) => html`
|
||||
<div
|
||||
class="option-card ${this.selectedDifficulty === value
|
||||
? "selected"
|
||||
: ""}"
|
||||
class="option-card ${
|
||||
this.selectedDifficulty === value ? "selected" : ""
|
||||
}"
|
||||
@click=${() => this.handleDifficultySelection(value)}
|
||||
>
|
||||
<difficulty-display
|
||||
@@ -164,9 +166,9 @@ export class SinglePlayerModal extends LitElement {
|
||||
<div class="option-title">${translateText("host_modal.mode")}</div>
|
||||
<div class="option-cards">
|
||||
<div
|
||||
class="option-card ${this.gameMode === GameMode.FFA
|
||||
? "selected"
|
||||
: ""}"
|
||||
class="option-card ${
|
||||
this.gameMode === GameMode.FFA ? "selected" : ""
|
||||
}"
|
||||
@click=${() => this.handleGameModeSelection(GameMode.FFA)}
|
||||
>
|
||||
<div class="option-card-title">
|
||||
@@ -174,9 +176,9 @@ export class SinglePlayerModal extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="option-card ${this.gameMode === GameMode.Team
|
||||
? "selected"
|
||||
: ""}"
|
||||
class="option-card ${
|
||||
this.gameMode === GameMode.Team ? "selected" : ""
|
||||
}"
|
||||
@click=${() => this.handleGameModeSelection(GameMode.Team)}
|
||||
>
|
||||
<div class="option-card-title">
|
||||
@@ -186,9 +188,10 @@ export class SinglePlayerModal extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${this.gameMode === GameMode.FFA
|
||||
? ""
|
||||
: html`
|
||||
${
|
||||
this.gameMode === GameMode.FFA
|
||||
? ""
|
||||
: html`
|
||||
<!-- Team Count Selection -->
|
||||
<div class="options-section">
|
||||
<div class="option-title">
|
||||
@@ -198,22 +201,27 @@ export class SinglePlayerModal extends LitElement {
|
||||
${[2, 3, 4, 5, 6, 7, Quads, Trios, Duos].map(
|
||||
(o) => html`
|
||||
<div
|
||||
class="option-card ${this.teamCount === o
|
||||
? "selected"
|
||||
: ""}"
|
||||
class="option-card ${
|
||||
this.teamCount === o ? "selected" : ""
|
||||
}"
|
||||
@click=${() => this.handleTeamCountSelection(o)}
|
||||
>
|
||||
<div class="option-card-title">
|
||||
${typeof o === "string"
|
||||
? translateText(`public_lobby.teams_${o}`)
|
||||
: translateText("public_lobby.teams", { num: o })}
|
||||
${
|
||||
typeof o === "string"
|
||||
? translateText(`public_lobby.teams_${o}`)
|
||||
: translateText("public_lobby.teams", {
|
||||
num: o,
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
`}
|
||||
`
|
||||
}
|
||||
|
||||
<!-- Game Options -->
|
||||
<div class="options-section">
|
||||
@@ -233,10 +241,11 @@ export class SinglePlayerModal extends LitElement {
|
||||
.value="${String(this.bots)}"
|
||||
/>
|
||||
<div class="option-card-title">
|
||||
<span>${translateText("single_modal.bots")}</span>${this
|
||||
.bots === 0
|
||||
? translateText("single_modal.bots_disabled")
|
||||
: this.bots}
|
||||
<span>${translateText("single_modal.bots")}</span>${
|
||||
this.bots === 0
|
||||
? translateText("single_modal.bots_disabled")
|
||||
: this.bots
|
||||
}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
@@ -410,7 +419,8 @@ export class SinglePlayerModal extends LitElement {
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Starting single player game with map: ${GameMapType[this.selectedMap as keyof typeof GameMapType]
|
||||
`Starting single player game with map: ${
|
||||
GameMapType[this.selectedMap as keyof typeof GameMapType]
|
||||
}${this.useRandomMap ? " (Randomly selected)" : ""}`,
|
||||
);
|
||||
const gameID = generateID();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FetchGameMapLoader } from "../core/game/FetchGameMapLoader";
|
||||
import version from "../../resources/version.txt";
|
||||
import { FetchGameMapLoader } from "../core/game/FetchGameMapLoader";
|
||||
|
||||
export const terrainMapFileLoader = new FetchGameMapLoader("/maps", version);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import "./components/Difficulties";
|
||||
import "./components/Maps";
|
||||
import { LitElement, html, render } from "lit";
|
||||
import { customElement, query, state } from "lit/decorators.js";
|
||||
import { handlePurchase, patterns } from "./Cosmetics";
|
||||
import { Pattern } from "../core/CosmeticSchemas";
|
||||
import { PatternDecoder } from "../core/PatternDecoder";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { UserMeResponse } from "../core/ApiSchemas";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import { base64url } from "jose";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { html, LitElement, render } from "lit";
|
||||
import { customElement, query, state } from "lit/decorators.js";
|
||||
import { UserMeResponse } from "../core/ApiSchemas";
|
||||
import { Pattern } from "../core/CosmeticSchemas";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import { PatternDecoder } from "../core/PatternDecoder";
|
||||
import { handlePurchase, patterns } from "./Cosmetics";
|
||||
import { translateText } from "./Utils";
|
||||
|
||||
@customElement("territory-patterns-modal")
|
||||
@@ -112,8 +112,9 @@ export class TerritoryPatternsModal extends LitElement {
|
||||
return html`
|
||||
<div
|
||||
class="fixed z-[10000] px-3 py-2 rounded bg-black text-white text-sm pointer-events-none shadow-md"
|
||||
style="top: ${this.hoverPosition.y + 12}px; left: ${this.hoverPosition
|
||||
.x + 12}px;"
|
||||
style="top: ${this.hoverPosition.y + 12}px; left: ${
|
||||
this.hoverPosition.x + 12
|
||||
}px;"
|
||||
>
|
||||
${translateText("territory_patterns.blocked.purchase")}
|
||||
</div>
|
||||
@@ -129,9 +130,11 @@ export class TerritoryPatternsModal extends LitElement {
|
||||
<div style="flex: 0 1 calc(25% - 1rem); max-width: calc(25% - 1rem);">
|
||||
<button
|
||||
class="border p-2 rounded-lg shadow text-black dark:text-white text-left w-full
|
||||
${isSelected
|
||||
? "bg-blue-500 text-white"
|
||||
: "bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700"}
|
||||
${
|
||||
isSelected
|
||||
? "bg-blue-500 text-white"
|
||||
: "bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700"
|
||||
}
|
||||
${pattern.product !== null ? "opacity-50 cursor-not-allowed" : ""}"
|
||||
@click=${() =>
|
||||
pattern.product === null && this.selectPattern(pattern.pattern)}
|
||||
@@ -162,8 +165,9 @@ export class TerritoryPatternsModal extends LitElement {
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
${pattern.product !== null
|
||||
? html`
|
||||
${
|
||||
pattern.product !== null
|
||||
? html`
|
||||
<button
|
||||
class="w-full mt-2 px-3 py-1 bg-green-500 hover:bg-green-600
|
||||
text-white text-xs font-medium rounded transition-colors"
|
||||
@@ -177,7 +181,8 @@ export class TerritoryPatternsModal extends LitElement {
|
||||
(${pattern.product.price})
|
||||
</button>
|
||||
`
|
||||
: null}
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -198,9 +203,11 @@ export class TerritoryPatternsModal extends LitElement {
|
||||
>
|
||||
<button
|
||||
class="border p-2 rounded-lg shadow text-black dark:text-white text-left
|
||||
${this.selectedPattern === undefined
|
||||
? "bg-blue-500 text-white"
|
||||
: "bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700"}"
|
||||
${
|
||||
this.selectedPattern === undefined
|
||||
? "bg-blue-500 text-white"
|
||||
: "bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700"
|
||||
}"
|
||||
style="flex: 0 1 calc(25% - 1rem); max-width: calc(25% - 1rem);"
|
||||
@click=${() => this.selectPattern(undefined)}
|
||||
>
|
||||
@@ -290,24 +297,29 @@ export class TerritoryPatternsModal extends LitElement {
|
||||
"
|
||||
>
|
||||
<div
|
||||
style="display: grid; grid-template-columns: repeat(2, ${width /
|
||||
2}px); grid-template-rows: repeat(2, ${height / 2}px);"
|
||||
style="display: grid; grid-template-columns: repeat(2, ${
|
||||
width / 2
|
||||
}px); grid-template-rows: repeat(2, ${height / 2}px);"
|
||||
>
|
||||
<div
|
||||
style="background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.1); width: ${width /
|
||||
2}px; height: ${height / 2}px;"
|
||||
style="background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.1); width: ${
|
||||
width / 2
|
||||
}px; height: ${height / 2}px;"
|
||||
></div>
|
||||
<div
|
||||
style="background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.1); width: ${width /
|
||||
2}px; height: ${height / 2}px;"
|
||||
style="background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.1); width: ${
|
||||
width / 2
|
||||
}px; height: ${height / 2}px;"
|
||||
></div>
|
||||
<div
|
||||
style="background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.1); width: ${width /
|
||||
2}px; height: ${height / 2}px;"
|
||||
style="background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.1); width: ${
|
||||
width / 2
|
||||
}px; height: ${height / 2}px;"
|
||||
></div>
|
||||
<div
|
||||
style="background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.1); width: ${width /
|
||||
2}px; height: ${height / 2}px;"
|
||||
style="background-color: #fff; border: 1px solid rgba(0, 0, 0, 0.1); width: ${
|
||||
width / 2
|
||||
}px; height: ${height / 2}px;"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { z } from "zod";
|
||||
import { EventBus, GameEvent } from "../core/EventBus";
|
||||
import {
|
||||
AllPlayers,
|
||||
GameType,
|
||||
@@ -7,6 +9,8 @@ import {
|
||||
Tick,
|
||||
UnitType,
|
||||
} from "../core/game/Game";
|
||||
import { TileRef } from "../core/game/GameMap";
|
||||
import { PlayerView } from "../core/game/GameView";
|
||||
import {
|
||||
AllPlayersStats,
|
||||
ClientHashMessage,
|
||||
@@ -20,13 +24,9 @@ import {
|
||||
ServerMessageSchema,
|
||||
Winner,
|
||||
} from "../core/Schemas";
|
||||
import { EventBus, GameEvent } from "../core/EventBus";
|
||||
import { replacer } from "../core/Util";
|
||||
import { LobbyConfig } from "./ClientGameRunner";
|
||||
import { LocalServer } from "./LocalServer";
|
||||
import { PlayerView } from "../core/game/GameView";
|
||||
import { TileRef } from "../core/game/GameMap";
|
||||
import { replacer } from "../core/Util";
|
||||
import { z } from "zod";
|
||||
|
||||
export class PauseGameEvent implements GameEvent {
|
||||
constructor(public readonly paused: boolean) {}
|
||||
|
||||
@@ -2,12 +2,12 @@ import "./components/baseComponents/setting/SettingKeybind";
|
||||
import "./components/baseComponents/setting/SettingNumber";
|
||||
import "./components/baseComponents/setting/SettingSlider";
|
||||
import "./components/baseComponents/setting/SettingToggle";
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query, state } from "lit/decorators.js";
|
||||
import { SettingKeybind } from "./components/baseComponents/setting/SettingKeybind";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import { translateText } from "../client/Utils";
|
||||
import { z } from "zod";
|
||||
import { translateText } from "../client/Utils";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import { SettingKeybind } from "./components/baseComponents/setting/SettingKeybind";
|
||||
|
||||
const KeybindSchema = z.record(z.string(), z.string());
|
||||
|
||||
@@ -233,18 +233,22 @@ export class UserSettingModal extends LitElement {
|
||||
<div class="flex mb-4 w-full justify-center">
|
||||
<button
|
||||
class="w-1/2 text-center px-3 py-1 rounded-l
|
||||
${this.settingsMode === "basic"
|
||||
? "bg-white/10 text-white"
|
||||
: "bg-transparent text-gray-400"}"
|
||||
${
|
||||
this.settingsMode === "basic"
|
||||
? "bg-white/10 text-white"
|
||||
: "bg-transparent text-gray-400"
|
||||
}"
|
||||
@click=${() => (this.settingsMode = "basic")}
|
||||
>
|
||||
${translateText("user_setting.tab_basic")}
|
||||
</button>
|
||||
<button
|
||||
class="w-1/2 text-center px-3 py-1 rounded-r
|
||||
${this.settingsMode === "keybinds"
|
||||
? "bg-white/10 text-white"
|
||||
: "bg-transparent text-gray-400"}"
|
||||
${
|
||||
this.settingsMode === "keybinds"
|
||||
? "bg-white/10 text-white"
|
||||
: "bg-transparent text-gray-400"
|
||||
}"
|
||||
@click=${() => (this.settingsMode = "keybinds")}
|
||||
>
|
||||
${translateText("user_setting.tab_keybinds")}
|
||||
@@ -252,9 +256,11 @@ export class UserSettingModal extends LitElement {
|
||||
</div>
|
||||
|
||||
<div class="settings-list">
|
||||
${this.settingsMode === "basic"
|
||||
? this.renderBasicSettings()
|
||||
: this.renderKeybindSettings()}
|
||||
${
|
||||
this.settingsMode === "basic"
|
||||
? this.renderBasicSettings()
|
||||
: this.renderKeybindSettings()
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -361,13 +367,15 @@ export class UserSettingModal extends LitElement {
|
||||
description="${translateText("user_setting.attack_ratio_desc")}"
|
||||
min="1"
|
||||
max="100"
|
||||
.value=${Number(localStorage.getItem("settings.attackRatio") ?? "0.2") *
|
||||
100}
|
||||
.value=${
|
||||
Number(localStorage.getItem("settings.attackRatio") ?? "0.2") * 100
|
||||
}
|
||||
@change=${this.sliderAttackRatio}
|
||||
></setting-slider>
|
||||
|
||||
${this.showEasterEggSettings
|
||||
? html`
|
||||
${
|
||||
this.showEasterEggSettings
|
||||
? html`
|
||||
<setting-slider
|
||||
label="${translateText(
|
||||
"user_setting.easter_writing_speed_label",
|
||||
@@ -408,7 +416,8 @@ export class UserSettingModal extends LitElement {
|
||||
}}
|
||||
></setting-number>
|
||||
`
|
||||
: null}
|
||||
: null
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { translateText } from "../client/Utils";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import {
|
||||
MAX_USERNAME_LENGTH,
|
||||
validateUsername,
|
||||
} from "../core/validations/username";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import { translateText } from "../client/Utils";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
const usernameKey = "username";
|
||||
|
||||
@@ -48,8 +48,9 @@ export class UsernameInput extends LitElement {
|
||||
focus:ring-blue-500 focus:border-blue-500 dark:border-gray-300/60
|
||||
dark:bg-gray-700 dark:text-white"
|
||||
/>
|
||||
${this.validationError
|
||||
? html`<div
|
||||
${
|
||||
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
|
||||
@@ -57,7 +58,8 @@ export class UsernameInput extends LitElement {
|
||||
>
|
||||
${this.validationError}
|
||||
</div>`
|
||||
: null}
|
||||
: null
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import IntlMessageFormat from "intl-messageformat";
|
||||
import { LangSelector } from "./LangSelector";
|
||||
import { MessageType } from "../core/game/Game";
|
||||
import { LangSelector } from "./LangSelector";
|
||||
|
||||
export function renderTroops(troops: number): string {
|
||||
return renderNumber(troops / 10);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
export enum DifficultyDescription {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { GameMapType } from "../../core/game/Game";
|
||||
import { terrainMapFileLoader } from "../TerrainMapFileLoader";
|
||||
@@ -119,17 +119,19 @@ export class MapDisplay extends LitElement {
|
||||
render() {
|
||||
return html`
|
||||
<div class="option-card ${this.selected ? "selected" : ""}">
|
||||
${this.isLoading
|
||||
? html`<div class="option-image">
|
||||
${
|
||||
this.isLoading
|
||||
? html`<div class="option-image">
|
||||
${translateText("map_component.loading")}
|
||||
</div>`
|
||||
: this.mapWebpPath
|
||||
? html`<img
|
||||
: this.mapWebpPath
|
||||
? html`<img
|
||||
src="${this.mapWebpPath}"
|
||||
alt="${this.mapKey}"
|
||||
class="option-image"
|
||||
/>`
|
||||
: html`<div class="option-image">Error</div>`}
|
||||
: html`<div class="option-image">Error</div>`
|
||||
}
|
||||
<div class="option-card-title">${this.translation || this.mapName}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
@customElement("modal-overlay")
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { NewsModal } from "../NewsModal";
|
||||
import megaphone from "../../../resources/images/Megaphone.svg";
|
||||
import { translateText } from "../Utils";
|
||||
import version from "../../../resources/version.txt";
|
||||
import { NewsModal } from "../NewsModal";
|
||||
import { translateText } from "../Utils";
|
||||
|
||||
@customElement("news-button")
|
||||
export class NewsButton extends LitElement {
|
||||
@@ -38,10 +38,9 @@ export class NewsButton extends LitElement {
|
||||
render() {
|
||||
return html`
|
||||
<div
|
||||
class="flex relative ${this.hidden ? "parent-hidden" : ""} ${this
|
||||
.isActive
|
||||
? "active"
|
||||
: ""}"
|
||||
class="flex relative ${this.hidden ? "parent-hidden" : ""} ${
|
||||
this.isActive ? "active" : ""
|
||||
}"
|
||||
>
|
||||
<button
|
||||
class="border p-[4px] rounded-lg flex cursor-pointer border-black/30
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { classMap } from "lit/directives/class-map.js";
|
||||
import { translateText } from "../../Utils";
|
||||
@@ -28,9 +28,11 @@ export class OButton extends LitElement {
|
||||
})}
|
||||
?disabled=${this.disable}
|
||||
>
|
||||
${`${this.translationKey}` === ""
|
||||
? `${this.title}`
|
||||
: `${translateText(this.translationKey)}`}
|
||||
${
|
||||
`${this.translationKey}` === ""
|
||||
? `${this.title}`
|
||||
: `${translateText(this.translationKey)}`
|
||||
}
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { translateText } from "../../Utils";
|
||||
|
||||
@@ -83,18 +83,21 @@ export class OModal extends LitElement {
|
||||
|
||||
render() {
|
||||
return html`
|
||||
${this.isModalOpen
|
||||
? html`
|
||||
${
|
||||
this.isModalOpen
|
||||
? html`
|
||||
<aside class="c-modal">
|
||||
<div
|
||||
class="c-modal__wrapper ${this.alwaysMaximized
|
||||
? "always-maximized"
|
||||
: ""}"
|
||||
class="c-modal__wrapper ${
|
||||
this.alwaysMaximized ? "always-maximized" : ""
|
||||
}"
|
||||
>
|
||||
<header class="c-modal__header">
|
||||
${`${this.translationKey}` === ""
|
||||
? `${this.title}`
|
||||
: `${translateText(this.translationKey)}`}
|
||||
${
|
||||
`${this.translationKey}` === ""
|
||||
? `${this.title}`
|
||||
: `${translateText(this.translationKey)}`
|
||||
}
|
||||
<div class="c-modal__close" @click=${this.close}>✕</div>
|
||||
</header>
|
||||
<section class="c-modal__content">
|
||||
@@ -103,7 +106,8 @@ export class OModal extends LitElement {
|
||||
</div>
|
||||
</aside>
|
||||
`
|
||||
: html``}
|
||||
: html``
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { translateText } from "../../../../client/Utils";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("setting-number")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("setting-slider")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("setting-toggle")
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { AnimatedSprite } from "./AnimatedSprite";
|
||||
import { FxType } from "./fx/Fx";
|
||||
import { PlayerView } from "../../core/game/GameView";
|
||||
import SAMExplosion from "../../../resources/sprites/samExplosion.png";
|
||||
import { Theme } from "../../core/configuration/Config";
|
||||
import { colorizeCanvas } from "./SpriteLoader";
|
||||
import miniBigSmoke from "../../../resources/sprites/bigsmoke.png";
|
||||
import conquestSword from "../../../resources/sprites/conquestSword.png";
|
||||
import dust from "../../../resources/sprites/dust.png";
|
||||
import miniBigSmoke from "../../../resources/sprites/bigsmoke.png";
|
||||
import miniExplosion from "../../../resources/sprites/miniExplosion.png";
|
||||
import miniFire from "../../../resources/sprites/minifire.png";
|
||||
import nuke from "../../../resources/sprites/nukeExplosion.png";
|
||||
import SAMExplosion from "../../../resources/sprites/samExplosion.png";
|
||||
import sinkingShip from "../../../resources/sprites/sinkingShip.png";
|
||||
import miniSmoke from "../../../resources/sprites/smoke.png";
|
||||
import miniSmokeAndFire from "../../../resources/sprites/smokeAndFire.png";
|
||||
import nuke from "../../../resources/sprites/nukeExplosion.png";
|
||||
import sinkingShip from "../../../resources/sprites/sinkingShip.png";
|
||||
import unitExplosion from "../../../resources/sprites/unitExplosion.png";
|
||||
import { Theme } from "../../core/configuration/Config";
|
||||
import { PlayerView } from "../../core/game/GameView";
|
||||
import { AnimatedSprite } from "./AnimatedSprite";
|
||||
import { FxType } from "./fx/Fx";
|
||||
import { colorizeCanvas } from "./SpriteLoader";
|
||||
|
||||
type AnimatedSpriteConfig = {
|
||||
url: string;
|
||||
@@ -128,7 +128,8 @@ const ANIMATED_SPRITE_CONFIG: Partial<Record<FxType, AnimatedSpriteConfig>> = {
|
||||
};
|
||||
|
||||
export class AnimatedSpriteLoader {
|
||||
private readonly animatedSpriteImageMap: Map<FxType, HTMLCanvasElement> = new Map();
|
||||
private readonly animatedSpriteImageMap: Map<FxType, HTMLCanvasElement> =
|
||||
new Map();
|
||||
// Do not color the same sprite twice
|
||||
private readonly coloredAnimatedSpriteCache: Map<string, HTMLCanvasElement> =
|
||||
new Map();
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import { EventBus } from "../../core/EventBus";
|
||||
import { GameView } from "../../core/game/GameView";
|
||||
import { UserSettings } from "../../core/game/UserSettings";
|
||||
import { GameStartingModal } from "../GameStartingModal";
|
||||
import { RedrawGraphicsEvent } from "../InputHandler";
|
||||
import { AlertFrame } from "./layers/AlertFrame";
|
||||
import { BuildMenu } from "./layers/BuildMenu";
|
||||
import { ChatDisplay } from "./layers/ChatDisplay";
|
||||
import { ChatModal } from "./layers/ChatModal";
|
||||
import { ControlPanel } from "./layers/ControlPanel";
|
||||
import { EmojiTable } from "./layers/EmojiTable";
|
||||
import { EventBus } from "../../core/EventBus";
|
||||
import { EventsDisplay } from "./layers/EventsDisplay";
|
||||
import { FPSDisplay } from "./layers/FPSDisplay";
|
||||
import { FxLayer } from "./layers/FxLayer";
|
||||
import { GameLeftSidebar } from "./layers/GameLeftSidebar";
|
||||
import { GameRightSidebar } from "./layers/GameRightSidebar";
|
||||
import { GameStartingModal } from "../GameStartingModal";
|
||||
import { GameView } from "../../core/game/GameView";
|
||||
import { GutterAdModal } from "./layers/GutterAdModal";
|
||||
import { HeadsUpMessage } from "./layers/HeadsUpMessage";
|
||||
import { Layer } from "./layers/Layer";
|
||||
@@ -22,7 +24,6 @@ import { NameLayer } from "./layers/NameLayer";
|
||||
import { PlayerInfoOverlay } from "./layers/PlayerInfoOverlay";
|
||||
import { PlayerPanel } from "./layers/PlayerPanel";
|
||||
import { RailroadLayer } from "./layers/RailroadLayer";
|
||||
import { RedrawGraphicsEvent } from "../InputHandler";
|
||||
import { ReplayPanel } from "./layers/ReplayPanel";
|
||||
import { SettingsModal } from "./layers/SettingsModal";
|
||||
import { SpawnAd } from "./layers/SpawnAd";
|
||||
@@ -32,13 +33,12 @@ import { StructureLayer } from "./layers/StructureLayer";
|
||||
import { TeamStats } from "./layers/TeamStats";
|
||||
import { TerrainLayer } from "./layers/TerrainLayer";
|
||||
import { TerritoryLayer } from "./layers/TerritoryLayer";
|
||||
import { TransformHandler } from "./TransformHandler";
|
||||
import { UILayer } from "./layers/UILayer";
|
||||
import { UIState } from "./UIState";
|
||||
import { UnitDisplay } from "./layers/UnitDisplay";
|
||||
import { UnitLayer } from "./layers/UnitLayer";
|
||||
import { UserSettings } from "../../core/game/UserSettings";
|
||||
import { WinModal } from "./layers/WinModal";
|
||||
import { TransformHandler } from "./TransformHandler";
|
||||
import { UIState } from "./UIState";
|
||||
|
||||
export function createRenderer(
|
||||
canvas: HTMLCanvasElement,
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { TrainType, UnitType } from "../../core/game/Game";
|
||||
import { Colord } from "colord";
|
||||
import { Theme } from "../../core/configuration/Config";
|
||||
import { UnitView } from "../../core/game/GameView";
|
||||
import atomBombSprite from "../../../resources/sprites/atombomb.png";
|
||||
import hydrogenBombSprite from "../../../resources/sprites/hydrogenbomb.png";
|
||||
import mirvSprite from "../../../resources/sprites/mirv2.png";
|
||||
import samMissileSprite from "../../../resources/sprites/samMissile.png";
|
||||
import tradeShipSprite from "../../../resources/sprites/tradeship.png";
|
||||
import trainCarriageSprite from "../../../resources/sprites/trainCarriage.png";
|
||||
import trainEngineSprite from "../../../resources/sprites/trainEngine.png";
|
||||
import trainLoadedCarriageSprite from "../../../resources/sprites/trainCarriageLoaded.png";
|
||||
import trainEngineSprite from "../../../resources/sprites/trainEngine.png";
|
||||
import transportShipSprite from "../../../resources/sprites/transportship.png";
|
||||
import warshipSprite from "../../../resources/sprites/warship.png";
|
||||
import { Theme } from "../../core/configuration/Config";
|
||||
import { TrainType, UnitType } from "../../core/game/Game";
|
||||
import { UnitView } from "../../core/game/GameView";
|
||||
|
||||
// Can't reuse TrainType because "loaded" is not a type, just an attribute
|
||||
const TrainTypeSprite = {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { EventBus } from "../../core/EventBus";
|
||||
import { Cell } from "../../core/game/Game";
|
||||
import { GameView } from "../../core/game/GameView";
|
||||
import { CenterCameraEvent, DragEvent, ZoomEvent } from "../InputHandler";
|
||||
import {
|
||||
GoToPlayerEvent,
|
||||
GoToPositionEvent,
|
||||
GoToUnitEvent,
|
||||
} from "./layers/Leaderboard";
|
||||
import { Cell } from "../../core/game/Game";
|
||||
import { EventBus } from "../../core/EventBus";
|
||||
import { GameView } from "../../core/game/GameView";
|
||||
|
||||
export const GOTO_INTERVAL_MS = 16;
|
||||
export const CAMERA_MAX_SPEED = 15;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { FadeFx, SpriteFx } from "./SpriteFx";
|
||||
import { Fx, FxType } from "./Fx";
|
||||
import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader";
|
||||
import { ConquestUpdate } from "../../../core/game/GameUpdates";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { TextFx } from "./TextFx";
|
||||
import { renderNumber } from "../../Utils";
|
||||
import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader";
|
||||
import { Fx, FxType } from "./Fx";
|
||||
import { FadeFx, SpriteFx } from "./SpriteFx";
|
||||
import { TextFx } from "./TextFx";
|
||||
|
||||
/**
|
||||
* Conquest FX:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { FadeFx, SpriteFx } from "./SpriteFx";
|
||||
import { Fx, FxType } from "./Fx";
|
||||
import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader";
|
||||
import { Fx, FxType } from "./Fx";
|
||||
import { FadeFx, SpriteFx } from "./SpriteFx";
|
||||
|
||||
/**
|
||||
* Shockwave effect: draw a growing 1px white circle
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Fx, FxType } from "./Fx";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { PlayerView } from "../../../core/game/GameView";
|
||||
import { AnimatedSprite } from "../AnimatedSprite";
|
||||
import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader";
|
||||
import { PlayerView } from "../../../core/game/GameView";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { Fx, FxType } from "./Fx";
|
||||
|
||||
function fadeInOut(t: number, fadeIn = 0.3, fadeOut = 0.7): number {
|
||||
if (t < fadeIn) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Fx, FxType } from "./Fx";
|
||||
import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader";
|
||||
import { Fx, FxType } from "./Fx";
|
||||
import { SpriteFx } from "./SpriteFx";
|
||||
import { Timeline } from "./Timeline";
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import {
|
||||
BrokeAllianceUpdate,
|
||||
GameUpdateType,
|
||||
} from "../../../core/game/GameUpdates";
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { Layer } from "./Layer";
|
||||
import { UserSettings } from "../../../core/game/UserSettings";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
// Parameters for the alert animation
|
||||
const ALERT_SPEED = 1.6;
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
import {
|
||||
BuildUnitIntentEvent,
|
||||
SendUpgradeStructureIntentEvent,
|
||||
} from "../../Transport";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import warshipIcon from "../../../../resources/images/BattleshipIconWhite.svg";
|
||||
import cityIcon from "../../../../resources/images/CityIconWhite.svg";
|
||||
import factoryIcon from "../../../../resources/images/FactoryIconWhite.svg";
|
||||
import goldCoinIcon from "../../../../resources/images/GoldCoinIcon.svg";
|
||||
import mirvIcon from "../../../../resources/images/MIRVIcon.svg";
|
||||
import hydrogenBombIcon from "../../../../resources/images/MushroomCloudIconWhite.svg";
|
||||
import atomBombIcon from "../../../../resources/images/NukeIconWhite.svg";
|
||||
import portIcon from "../../../../resources/images/PortIcon.svg";
|
||||
import shieldIcon from "../../../../resources/images/ShieldIconWhite.svg";
|
||||
import missileSiloIcon from "../../../../resources/non-commercial/svg/MissileSiloIconWhite.svg";
|
||||
import samlauncherIcon from "../../../../resources/non-commercial/svg/SamLauncherIconWhite.svg";
|
||||
import { translateText } from "../../../client/Utils";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import {
|
||||
BuildableUnit,
|
||||
Gold,
|
||||
PlayerActions,
|
||||
UnitType,
|
||||
} from "../../../core/game/Game";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import {
|
||||
CloseViewEvent,
|
||||
MouseDownEvent,
|
||||
ShowBuildMenuEvent,
|
||||
ShowEmojiMenuEvent,
|
||||
} from "../../InputHandler";
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { Layer } from "./Layer";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import atomBombIcon from "../../../../resources/images/NukeIconWhite.svg";
|
||||
import cityIcon from "../../../../resources/images/CityIconWhite.svg";
|
||||
import factoryIcon from "../../../../resources/images/FactoryIconWhite.svg";
|
||||
import goldCoinIcon from "../../../../resources/images/GoldCoinIcon.svg";
|
||||
import hydrogenBombIcon from "../../../../resources/images/MushroomCloudIconWhite.svg";
|
||||
import mirvIcon from "../../../../resources/images/MIRVIcon.svg";
|
||||
import missileSiloIcon from "../../../../resources/non-commercial/svg/MissileSiloIconWhite.svg";
|
||||
import portIcon from "../../../../resources/images/PortIcon.svg";
|
||||
import {
|
||||
BuildUnitIntentEvent,
|
||||
SendUpgradeStructureIntentEvent,
|
||||
} from "../../Transport";
|
||||
import { renderNumber } from "../../Utils";
|
||||
import samlauncherIcon from "../../../../resources/non-commercial/svg/SamLauncherIconWhite.svg";
|
||||
import shieldIcon from "../../../../resources/images/ShieldIconWhite.svg";
|
||||
import { translateText } from "../../../client/Utils";
|
||||
import warshipIcon from "../../../../resources/images/BattleshipIconWhite.svg";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
export type BuildItemDisplay = {
|
||||
unitType: UnitType;
|
||||
@@ -389,7 +389,10 @@ export class BuildMenu extends LitElement implements Layer {
|
||||
return player.totalUnitLevels(item.unitType).toString();
|
||||
}
|
||||
|
||||
public sendBuildOrUpgrade(buildableUnit: BuildableUnit, tile?: TileRef): void {
|
||||
public sendBuildOrUpgrade(
|
||||
buildableUnit: BuildableUnit,
|
||||
tile?: TileRef,
|
||||
): void {
|
||||
if (tile === undefined) throw new Error("Missing tile");
|
||||
if (this.eventBus === undefined) throw new Error("Not initialized");
|
||||
if (buildableUnit.canUpgrade !== false) {
|
||||
@@ -430,9 +433,11 @@ export class BuildMenu extends LitElement implements Layer {
|
||||
@click=${() =>
|
||||
this.sendBuildOrUpgrade(buildableUnit, this.clickedTile)}
|
||||
?disabled=${!enabled}
|
||||
title=${!enabled
|
||||
? translateText("build_menu.not_enough_money")
|
||||
: ""}
|
||||
title=${
|
||||
!enabled
|
||||
? translateText("build_menu.not_enough_money")
|
||||
: ""
|
||||
}
|
||||
>
|
||||
<img
|
||||
src=${item.icon}
|
||||
@@ -444,8 +449,9 @@ export class BuildMenu extends LitElement implements Layer {
|
||||
>${item.key && translateText(item.key)}</span
|
||||
>
|
||||
<span class="build-description"
|
||||
>${item.description &&
|
||||
translateText(item.description)}</span
|
||||
>${
|
||||
item.description && translateText(item.description)
|
||||
}</span
|
||||
>
|
||||
<span class="build-cost" translate="no">
|
||||
${renderNumber(
|
||||
@@ -459,11 +465,13 @@ export class BuildMenu extends LitElement implements Layer {
|
||||
style="vertical-align: middle;"
|
||||
/>
|
||||
</span>
|
||||
${item.countable
|
||||
? html`<div class="build-count-chip">
|
||||
${
|
||||
item.countable
|
||||
? html`<div class="build-count-chip">
|
||||
<span class="build-count">${this.count(item)}</span>
|
||||
</div>`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
</button>
|
||||
`;
|
||||
})}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { DirectiveResult } from "lit/directive.js";
|
||||
import { UnsafeHTMLDirective, unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { MessageType } from "../../../core/game/Game";
|
||||
import {
|
||||
DisplayMessageUpdate,
|
||||
GameUpdateType,
|
||||
} from "../../../core/game/GameUpdates";
|
||||
import { LitElement, html } from "lit";
|
||||
import { UnsafeHTMLDirective, unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { DirectiveResult } from "lit/directive.js";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { Layer } from "./Layer";
|
||||
import { MessageType } from "../../../core/game/Game";
|
||||
import { onlyImages } from "../../../core/Util";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
type ChatEvent = {
|
||||
description: string;
|
||||
@@ -136,10 +136,9 @@ export class ChatDisplay extends LitElement implements Layer {
|
||||
<div>
|
||||
<div class="w-full bg-black/80 sticky top-0 px-[10px]">
|
||||
<button
|
||||
class="text-white cursor-pointer pointer-events-auto ${this
|
||||
._hidden
|
||||
? "hidden"
|
||||
: ""}"
|
||||
class="text-white cursor-pointer pointer-events-auto ${
|
||||
this._hidden ? "hidden" : ""
|
||||
}"
|
||||
@click=${this.toggleHidden}
|
||||
>
|
||||
Hide
|
||||
@@ -147,25 +146,24 @@ export class ChatDisplay extends LitElement implements Layer {
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="text-white cursor-pointer pointer-events-auto ${this._hidden
|
||||
? ""
|
||||
: "hidden"}"
|
||||
class="text-white cursor-pointer pointer-events-auto ${
|
||||
this._hidden ? "" : "hidden"
|
||||
}"
|
||||
@click=${this.toggleHidden}
|
||||
>
|
||||
Chat
|
||||
<span
|
||||
class="${this.newEvents
|
||||
? ""
|
||||
: "hidden"} inline-block px-2 bg-red-500 rounded-sm"
|
||||
class="${
|
||||
this.newEvents ? "" : "hidden"
|
||||
} inline-block px-2 bg-red-500 rounded-sm"
|
||||
>${this.newEvents}</span
|
||||
>
|
||||
</button>
|
||||
|
||||
<table
|
||||
class="w-full border-collapse text-white shadow-lg lg:text-xl text-xs ${this
|
||||
._hidden
|
||||
? "hidden"
|
||||
: ""}"
|
||||
class="w-full border-collapse text-white shadow-lg lg:text-xl text-xs ${
|
||||
this._hidden ? "hidden" : ""
|
||||
}"
|
||||
style="pointer-events: auto;"
|
||||
>
|
||||
<tbody>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { COLORS, MenuElement, MenuElementParams } from "./RadialMenuElements";
|
||||
import { ChatModal, QuickChatPhrase, quickChatPhrases } from "./ChatModal";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { SendQuickChatEvent } from "../../Transport";
|
||||
import { translateText } from "../../Utils";
|
||||
import { ChatModal, QuickChatPhrase, quickChatPhrases } from "./ChatModal";
|
||||
import { COLORS, MenuElement, MenuElementParams } from "./RadialMenuElements";
|
||||
|
||||
export class ChatIntegration {
|
||||
private readonly ctModal: ChatModal;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, query } from "lit/decorators.js";
|
||||
import { CloseViewEvent } from "../../InputHandler";
|
||||
import quickChatData from "../../../../resources/QuickChat.json";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { PlayerType } from "../../../core/game/Game";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { CloseViewEvent } from "../../InputHandler";
|
||||
import { SendQuickChatEvent } from "../../Transport";
|
||||
import quickChatData from "../../../../resources/QuickChat.json";
|
||||
import { translateText } from "../../Utils";
|
||||
|
||||
export type QuickChatPhrase = {
|
||||
@@ -78,10 +78,9 @@ export class ChatModal extends LitElement {
|
||||
${this.categories.map(
|
||||
(category) => html`
|
||||
<button
|
||||
class="chat-option-button ${this.selectedCategory ===
|
||||
category.id
|
||||
? "selected"
|
||||
: ""}"
|
||||
class="chat-option-button ${
|
||||
this.selectedCategory === category.id ? "selected" : ""
|
||||
}"
|
||||
@click=${() => this.selectCategory(category.id)}
|
||||
>
|
||||
${translateText(`chat.cat.${category.id}`)}
|
||||
@@ -90,8 +89,9 @@ export class ChatModal extends LitElement {
|
||||
)}
|
||||
</div>
|
||||
|
||||
${this.selectedCategory
|
||||
? html`
|
||||
${
|
||||
this.selectedCategory
|
||||
? html`
|
||||
<div class="chat-column">
|
||||
<div class="column-title">
|
||||
${translateText("chat.phrase")}
|
||||
@@ -100,13 +100,14 @@ export class ChatModal extends LitElement {
|
||||
${this.getPhrasesForCategory(this.selectedCategory).map(
|
||||
(phrase) => html`
|
||||
<button
|
||||
class="chat-option-button ${this
|
||||
.selectedPhraseText ===
|
||||
translateText(
|
||||
`chat.${this.selectedCategory}.${phrase.key}`,
|
||||
)
|
||||
? "selected"
|
||||
: ""}"
|
||||
class="chat-option-button ${
|
||||
this.selectedPhraseText ===
|
||||
translateText(
|
||||
`chat.${this.selectedCategory}.${phrase.key}`,
|
||||
)
|
||||
? "selected"
|
||||
: ""
|
||||
}"
|
||||
@click=${() => this.selectPhrase(phrase)}
|
||||
>
|
||||
${this.renderPhrasePreview(phrase)}
|
||||
@@ -116,9 +117,11 @@ export class ChatModal extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: null}
|
||||
${this.requiresPlayerSelection || this.selectedPlayer
|
||||
? html`
|
||||
: null
|
||||
}
|
||||
${
|
||||
this.requiresPlayerSelection || this.selectedPlayer
|
||||
? html`
|
||||
<div class="chat-column">
|
||||
<div class="column-title">
|
||||
${translateText("chat.player")}
|
||||
@@ -136,10 +139,9 @@ export class ChatModal extends LitElement {
|
||||
${this.getSortedFilteredPlayers().map(
|
||||
(player) => html`
|
||||
<button
|
||||
class="chat-option-button ${this.selectedPlayer ===
|
||||
player
|
||||
? "selected"
|
||||
: ""}"
|
||||
class="chat-option-button ${
|
||||
this.selectedPlayer === player ? "selected" : ""
|
||||
}"
|
||||
@click=${() => this.selectPlayer(player)}
|
||||
>
|
||||
${player.name()}
|
||||
@@ -149,20 +151,25 @@ export class ChatModal extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: null}
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="chat-preview">
|
||||
${this.previewText
|
||||
? translateText(this.previewText)
|
||||
: translateText("chat.build")}
|
||||
${
|
||||
this.previewText
|
||||
? translateText(this.previewText)
|
||||
: translateText("chat.build")
|
||||
}
|
||||
</div>
|
||||
<div class="chat-send">
|
||||
<button
|
||||
class="chat-send-button"
|
||||
@click=${this.sendChatMessage}
|
||||
?disabled=${!this.previewText ||
|
||||
(this.requiresPlayerSelection && !this.selectedPlayer)}
|
||||
?disabled=${
|
||||
!this.previewText ||
|
||||
(this.requiresPlayerSelection && !this.selectedPlayer)
|
||||
}
|
||||
>
|
||||
${translateText("chat.send")}
|
||||
</button>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { renderNumber, renderTroops } from "../../Utils";
|
||||
import { AttackRatioEvent } from "../../InputHandler";
|
||||
import { ClientID } from "../../../core/Schemas";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { Gold } from "../../../core/game/Game";
|
||||
import { Layer } from "./Layer";
|
||||
import { UIState } from "../UIState";
|
||||
import { translateText } from "../../../client/Utils";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { Gold } from "../../../core/game/Game";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { ClientID } from "../../../core/Schemas";
|
||||
import { AttackRatioEvent } from "../../InputHandler";
|
||||
import { renderNumber, renderTroops } from "../../Utils";
|
||||
import { UIState } from "../UIState";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
@customElement("control-panel")
|
||||
export class ControlPanel extends LitElement implements Layer {
|
||||
@@ -166,10 +166,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"
|
||||
: "hidden"}"
|
||||
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"
|
||||
: "hidden"
|
||||
}"
|
||||
@contextmenu=${(e: MouseEvent) => e.preventDefault()}
|
||||
>
|
||||
<div class="block bg-black/30 text-white mb-4 p-2 rounded">
|
||||
@@ -180,9 +182,11 @@ export class ControlPanel extends LitElement implements Layer {
|
||||
<span translate="no"
|
||||
>${renderTroops(this._troops)} / ${renderTroops(this._maxTroops)}
|
||||
<span
|
||||
class="${this._troopRateIsIncreasing
|
||||
? "text-green-500"
|
||||
: "text-yellow-500"}"
|
||||
class="${
|
||||
this._troopRateIsIncreasing
|
||||
? "text-green-500"
|
||||
: "text-yellow-500"
|
||||
}"
|
||||
translate="no"
|
||||
>(+${renderTroops(this.troopRate)})</span
|
||||
></span
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
import { CloseViewEvent, ShowEmojiMenuEvent } from "../../InputHandler";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { emojiTable, flattenedEmojiTable } from "../../../core/Util";
|
||||
import { AllPlayers } from "../../../core/game/Game";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { SendEmojiIntentEvent } from "../../Transport";
|
||||
import { AllPlayers } from "../../../core/game/Game";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { TerraNulliusImpl } from "../../../core/game/TerraNulliusImpl";
|
||||
import { emojiTable, flattenedEmojiTable } from "../../../core/Util";
|
||||
import { CloseViewEvent, ShowEmojiMenuEvent } from "../../InputHandler";
|
||||
import { SendEmojiIntentEvent } from "../../Transport";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
|
||||
@customElement("emoji-table")
|
||||
export class EmojiTable extends LitElement {
|
||||
@state() public isVisible = false;
|
||||
|
||||
init(
|
||||
transformHandler: TransformHandler,
|
||||
game: GameView,
|
||||
eventBus: EventBus,
|
||||
) {
|
||||
init(transformHandler: TransformHandler, game: GameView, eventBus: EventBus) {
|
||||
eventBus.on(ShowEmojiMenuEvent, (e) => {
|
||||
this.isVisible = true;
|
||||
const cell = transformHandler.screenToWorldCoordinates(e.x, e.y);
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
/* eslint-disable max-lines */
|
||||
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { DirectiveResult } from "lit/directive.js";
|
||||
import { UnsafeHTMLDirective, unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||
import allianceIcon from "../../../../resources/images/AllianceIconWhite.svg";
|
||||
import chatIcon from "../../../../resources/images/ChatIconWhite.svg";
|
||||
import donateGoldIcon from "../../../../resources/images/DonateGoldIconWhite.svg";
|
||||
import swordIcon from "../../../../resources/images/SwordIconWhite.svg";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import {
|
||||
AllPlayers,
|
||||
getMessageCategory,
|
||||
MessageCategory,
|
||||
MessageType,
|
||||
PlayerType,
|
||||
Tick,
|
||||
UnitType,
|
||||
getMessageCategory,
|
||||
} from "../../../core/game/Game";
|
||||
import {
|
||||
AllianceExpiredUpdate,
|
||||
@@ -21,31 +31,26 @@ import {
|
||||
TargetPlayerUpdate,
|
||||
UnitIncomingUpdate,
|
||||
} from "../../../core/game/GameUpdates";
|
||||
import { GameView, PlayerView, UnitView } from "../../../core/game/GameView";
|
||||
import { onlyImages } from "../../../core/Util";
|
||||
import {
|
||||
CancelAttackIntentEvent,
|
||||
CancelBoatIntentEvent,
|
||||
SendAllianceExtensionIntentEvent,
|
||||
SendAllianceReplyIntentEvent,
|
||||
} from "../../Transport";
|
||||
import { GameView, PlayerView, UnitView } from "../../../core/game/GameView";
|
||||
import {
|
||||
getMessageTypeClasses,
|
||||
renderNumber,
|
||||
renderTroops,
|
||||
translateText,
|
||||
} from "../../Utils";
|
||||
import { Layer } from "./Layer";
|
||||
import {
|
||||
GoToPlayerEvent,
|
||||
GoToPositionEvent,
|
||||
GoToUnitEvent,
|
||||
} from "./Leaderboard";
|
||||
import { LitElement, TemplateResult, html } from "lit";
|
||||
import { UnsafeHTMLDirective, unsafeHTML } from "lit/directives/unsafe-html.js";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { getMessageTypeClasses, translateText } from "../../Utils";
|
||||
import { renderNumber, renderTroops } from "../../Utils";
|
||||
import { DirectiveResult } from "lit/directive.js";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { Layer } from "./Layer";
|
||||
import allianceIcon from "../../../../resources/images/AllianceIconWhite.svg";
|
||||
import chatIcon from "../../../../resources/images/ChatIconWhite.svg";
|
||||
import donateGoldIcon from "../../../../resources/images/DonateGoldIconWhite.svg";
|
||||
import { onlyImages } from "../../../core/Util";
|
||||
import swordIcon from "../../../../resources/images/SwordIconWhite.svg";
|
||||
|
||||
type GameEvent = {
|
||||
description: string;
|
||||
@@ -87,15 +92,19 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
@state() private latestGoldAmount: bigint | null = null;
|
||||
@state() private goldAmountAnimating = false;
|
||||
private goldAmountTimeoutId: ReturnType<typeof setTimeout> | null = null;
|
||||
@state() private readonly eventsFilters: Map<MessageCategory, boolean> = new Map([
|
||||
[MessageCategory.ATTACK, false],
|
||||
[MessageCategory.TRADE, false],
|
||||
[MessageCategory.ALLIANCE, false],
|
||||
[MessageCategory.CHAT, false],
|
||||
]);
|
||||
@state() private readonly eventsFilters: Map<MessageCategory, boolean> =
|
||||
new Map([
|
||||
[MessageCategory.ATTACK, false],
|
||||
[MessageCategory.TRADE, false],
|
||||
[MessageCategory.ALLIANCE, false],
|
||||
[MessageCategory.CHAT, false],
|
||||
]);
|
||||
|
||||
private renderButton(options: {
|
||||
content: string | TemplateResult | DirectiveResult<typeof UnsafeHTMLDirective>;
|
||||
content:
|
||||
| string
|
||||
| TemplateResult
|
||||
| DirectiveResult<typeof UnsafeHTMLDirective>;
|
||||
onClick?: () => void;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
@@ -539,8 +548,8 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
traitorDuration === 1
|
||||
? translateText("events_display.duration_second")
|
||||
: translateText("events_display.duration_seconds_plural", {
|
||||
seconds: traitorDuration,
|
||||
});
|
||||
seconds: traitorDuration,
|
||||
});
|
||||
|
||||
this.addEvent({
|
||||
description: translateText("events_display.betrayal_description", {
|
||||
@@ -738,41 +747,43 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
|
||||
private renderIncomingAttacks() {
|
||||
return html`
|
||||
${this.incomingAttacks.length > 0
|
||||
? html`
|
||||
${this.incomingAttacks.map(
|
||||
(attack) => {
|
||||
const attacker = this.game?.playerBySmallID(attack.attackerID);
|
||||
return html`
|
||||
${
|
||||
this.incomingAttacks.length > 0
|
||||
? html`
|
||||
${this.incomingAttacks.map((attack) => {
|
||||
const attacker = this.game?.playerBySmallID(attack.attackerID);
|
||||
return html`
|
||||
${this.renderButton({
|
||||
content: html`
|
||||
${renderTroops(attack.troops)}
|
||||
${attacker?.isPlayer() ? attacker.name() : "unknown"}
|
||||
${attack.retreating
|
||||
? `(${translateText("events_display.retreating")}...)`
|
||||
: ""}
|
||||
${
|
||||
attack.retreating
|
||||
? `(${translateText("events_display.retreating")}...)`
|
||||
: ""
|
||||
}
|
||||
`,
|
||||
onClick: () => this.attackWarningOnClick(attack),
|
||||
className: "text-left text-red-400",
|
||||
translate: false,
|
||||
})}
|
||||
`;
|
||||
},
|
||||
)}
|
||||
})}
|
||||
`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
private renderOutgoingAttacks() {
|
||||
return html`
|
||||
${this.outgoingAttacks.length > 0
|
||||
? html`
|
||||
${
|
||||
this.outgoingAttacks.length > 0
|
||||
? html`
|
||||
<div class="flex flex-wrap gap-y-1 gap-x-2">
|
||||
${this.outgoingAttacks.map(
|
||||
(attack) => {
|
||||
const target = this.game?.playerBySmallID(attack.targetID);
|
||||
return html`
|
||||
${this.outgoingAttacks.map((attack) => {
|
||||
const target = this.game?.playerBySmallID(attack.targetID);
|
||||
return html`
|
||||
<div class="inline-flex items-center gap-1">
|
||||
${this.renderButton({
|
||||
content: html`
|
||||
@@ -783,32 +794,36 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
className: "text-left text-blue-400",
|
||||
translate: false,
|
||||
})}
|
||||
${!attack.retreating
|
||||
? this.renderButton({
|
||||
content: "❌",
|
||||
onClick: () => this.emitCancelAttackIntent(attack.id),
|
||||
className: "text-left flex-shrink-0",
|
||||
disabled: attack.retreating,
|
||||
})
|
||||
: html`<span class="flex-shrink-0 text-blue-400"
|
||||
${
|
||||
!attack.retreating
|
||||
? this.renderButton({
|
||||
content: "❌",
|
||||
onClick: () =>
|
||||
this.emitCancelAttackIntent(attack.id),
|
||||
className: "text-left flex-shrink-0",
|
||||
disabled: attack.retreating,
|
||||
})
|
||||
: html`<span class="flex-shrink-0 text-blue-400"
|
||||
>(${translateText(
|
||||
"events_display.retreating",
|
||||
)}...)</span
|
||||
>`}
|
||||
>`
|
||||
}
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
)}
|
||||
})}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
private renderOutgoingLandAttacks() {
|
||||
return html`
|
||||
${this.outgoingLandAttacks.length > 0
|
||||
? html`
|
||||
${
|
||||
this.outgoingLandAttacks.length > 0
|
||||
? html`
|
||||
<div class="flex flex-wrap gap-y-1 gap-x-2">
|
||||
${this.outgoingLandAttacks.map(
|
||||
(landAttack) => html`
|
||||
@@ -819,32 +834,36 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
className: "text-left text-gray-400",
|
||||
translate: false,
|
||||
})}
|
||||
${!landAttack.retreating
|
||||
? this.renderButton({
|
||||
content: "❌",
|
||||
onClick: () =>
|
||||
this.emitCancelAttackIntent(landAttack.id),
|
||||
className: "text-left flex-shrink-0",
|
||||
disabled: landAttack.retreating,
|
||||
})
|
||||
: html`<span class="flex-shrink-0 text-blue-400"
|
||||
${
|
||||
!landAttack.retreating
|
||||
? this.renderButton({
|
||||
content: "❌",
|
||||
onClick: () =>
|
||||
this.emitCancelAttackIntent(landAttack.id),
|
||||
className: "text-left flex-shrink-0",
|
||||
disabled: landAttack.retreating,
|
||||
})
|
||||
: html`<span class="flex-shrink-0 text-blue-400"
|
||||
>(${translateText(
|
||||
"events_display.retreating",
|
||||
)}...)</span
|
||||
>`}
|
||||
>`
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
private renderBoats() {
|
||||
return html`
|
||||
${this.outgoingBoats.length > 0
|
||||
? html`
|
||||
${
|
||||
this.outgoingBoats.length > 0
|
||||
? html`
|
||||
<div class="flex flex-wrap gap-y-1 gap-x-2">
|
||||
${this.outgoingBoats.map(
|
||||
(boat) => html`
|
||||
@@ -856,24 +875,27 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
className: "text-left text-blue-400",
|
||||
translate: false,
|
||||
})}
|
||||
${!boat.retreating()
|
||||
? this.renderButton({
|
||||
content: "❌",
|
||||
onClick: () => this.emitBoatCancelIntent(boat.id()),
|
||||
className: "text-left flex-shrink-0",
|
||||
disabled: boat.retreating(),
|
||||
})
|
||||
: html`<span class="flex-shrink-0 text-blue-400"
|
||||
${
|
||||
!boat.retreating()
|
||||
? this.renderButton({
|
||||
content: "❌",
|
||||
onClick: () => this.emitBoatCancelIntent(boat.id()),
|
||||
className: "text-left flex-shrink-0",
|
||||
disabled: boat.retreating(),
|
||||
})
|
||||
: html`<span class="flex-shrink-0 text-blue-400"
|
||||
>(${translateText(
|
||||
"events_display.retreating",
|
||||
)}...)</span
|
||||
>`}
|
||||
>`
|
||||
}
|
||||
</div>
|
||||
`,
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -921,16 +943,17 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
return html`
|
||||
${styles}
|
||||
<!-- Events Toggle (when hidden) -->
|
||||
${this._hidden
|
||||
? html`
|
||||
${
|
||||
this._hidden
|
||||
? html`
|
||||
<div class="relative w-fit lg:bottom-2.5 lg:right-2.5 z-50">
|
||||
${this.renderButton({
|
||||
content: html`
|
||||
Events
|
||||
<span
|
||||
class="${this.newEvents
|
||||
? ""
|
||||
: "hidden"} inline-block px-2 bg-red-500 rounded-xl text-sm"
|
||||
class="${
|
||||
this.newEvents ? "" : "hidden"
|
||||
} inline-block px-2 bg-red-500 rounded-xl text-sm"
|
||||
>${this.newEvents}</span
|
||||
>
|
||||
`,
|
||||
@@ -941,7 +964,7 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
})}
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
: html`
|
||||
<!-- Main Events Display -->
|
||||
<div
|
||||
class="relative w-full sm:bottom-2.5 sm:right-2.5 z-50 sm:w-96 backdrop-blur"
|
||||
@@ -956,11 +979,11 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
content: html`<img
|
||||
src="${swordIcon}"
|
||||
class="w-5 h-5"
|
||||
style="filter: ${this.eventsFilters.get(
|
||||
MessageCategory.ATTACK,
|
||||
)
|
||||
? "grayscale(1) opacity(0.5)"
|
||||
: "none"}"
|
||||
style="filter: ${
|
||||
this.eventsFilters.get(MessageCategory.ATTACK)
|
||||
? "grayscale(1) opacity(0.5)"
|
||||
: "none"
|
||||
}"
|
||||
/>`,
|
||||
onClick: () =>
|
||||
this.toggleEventFilter(MessageCategory.ATTACK),
|
||||
@@ -970,11 +993,11 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
content: html`<img
|
||||
src="${donateGoldIcon}"
|
||||
class="w-5 h-5"
|
||||
style="filter: ${this.eventsFilters.get(
|
||||
MessageCategory.TRADE,
|
||||
)
|
||||
? "grayscale(1) opacity(0.5)"
|
||||
: "none"}"
|
||||
style="filter: ${
|
||||
this.eventsFilters.get(MessageCategory.TRADE)
|
||||
? "grayscale(1) opacity(0.5)"
|
||||
: "none"
|
||||
}"
|
||||
/>`,
|
||||
onClick: () =>
|
||||
this.toggleEventFilter(MessageCategory.TRADE),
|
||||
@@ -984,11 +1007,11 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
content: html`<img
|
||||
src="${allianceIcon}"
|
||||
class="w-5 h-5"
|
||||
style="filter: ${this.eventsFilters.get(
|
||||
MessageCategory.ALLIANCE,
|
||||
)
|
||||
? "grayscale(1) opacity(0.5)"
|
||||
: "none"}"
|
||||
style="filter: ${
|
||||
this.eventsFilters.get(MessageCategory.ALLIANCE)
|
||||
? "grayscale(1) opacity(0.5)"
|
||||
: "none"
|
||||
}"
|
||||
/>`,
|
||||
onClick: () =>
|
||||
this.toggleEventFilter(MessageCategory.ALLIANCE),
|
||||
@@ -998,11 +1021,11 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
content: html`<img
|
||||
src="${chatIcon}"
|
||||
class="w-5 h-5"
|
||||
style="filter: ${this.eventsFilters.get(
|
||||
MessageCategory.CHAT,
|
||||
)
|
||||
? "grayscale(1) opacity(0.5)"
|
||||
: "none"}"
|
||||
style="filter: ${
|
||||
this.eventsFilters.get(MessageCategory.CHAT)
|
||||
? "grayscale(1) opacity(0.5)"
|
||||
: "none"
|
||||
}"
|
||||
/>`,
|
||||
onClick: () =>
|
||||
this.toggleEventFilter(MessageCategory.CHAT),
|
||||
@@ -1010,18 +1033,23 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
})}
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
${this.latestGoldAmount !== null
|
||||
? html`<span
|
||||
class="text-green-400 font-semibold transition-all duration-300 ${this
|
||||
.goldAmountAnimating
|
||||
? "animate-pulse scale-110"
|
||||
: "scale-100"}"
|
||||
style="animation: ${this.goldAmountAnimating
|
||||
? "goldBounce 0.6s ease-out"
|
||||
: "none"}"
|
||||
${
|
||||
this.latestGoldAmount !== null
|
||||
? html`<span
|
||||
class="text-green-400 font-semibold transition-all duration-300 ${
|
||||
this.goldAmountAnimating
|
||||
? "animate-pulse scale-110"
|
||||
: "scale-100"
|
||||
}"
|
||||
style="animation: ${
|
||||
this.goldAmountAnimating
|
||||
? "goldBounce 0.6s ease-out"
|
||||
: "none"
|
||||
}"
|
||||
>+${renderNumber(this.latestGoldAmount)}</span
|
||||
>`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
${this.renderButton({
|
||||
content: translateText("leaderboard.hide"),
|
||||
onClick: this.toggleHidden,
|
||||
@@ -1052,30 +1080,36 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
event.type,
|
||||
)}"
|
||||
>
|
||||
${event.focusID
|
||||
? this.renderButton({
|
||||
content: this.getEventDescription(event),
|
||||
onClick: () => {
|
||||
event.focusID &&
|
||||
this.emitGoToPlayerEvent(event.focusID);
|
||||
},
|
||||
className: "text-left",
|
||||
})
|
||||
: event.unitView
|
||||
${
|
||||
event.focusID
|
||||
? this.renderButton({
|
||||
content: this.getEventDescription(event),
|
||||
onClick: () => {
|
||||
event.unitView &&
|
||||
this.emitGoToUnitEvent(
|
||||
event.unitView,
|
||||
content: this.getEventDescription(event),
|
||||
onClick: () => {
|
||||
event.focusID &&
|
||||
this.emitGoToPlayerEvent(
|
||||
event.focusID,
|
||||
);
|
||||
},
|
||||
className: "text-left",
|
||||
})
|
||||
: this.getEventDescription(event)}
|
||||
},
|
||||
className: "text-left",
|
||||
})
|
||||
: event.unitView
|
||||
? this.renderButton({
|
||||
content:
|
||||
this.getEventDescription(event),
|
||||
onClick: () => {
|
||||
event.unitView &&
|
||||
this.emitGoToUnitEvent(
|
||||
event.unitView,
|
||||
);
|
||||
},
|
||||
className: "text-left",
|
||||
})
|
||||
: this.getEventDescription(event)
|
||||
}
|
||||
<!-- Events with buttons (Alliance requests) -->
|
||||
${event.buttons
|
||||
? html`
|
||||
${
|
||||
event.buttons
|
||||
? html`
|
||||
<div class="flex flex-wrap gap-1.5 mt-1">
|
||||
${event.buttons.map(
|
||||
(btn) => html`
|
||||
@@ -1084,13 +1118,13 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
text-white rounded text-md
|
||||
md:text-sm cursor-pointer
|
||||
transition-colors duration-300
|
||||
${btn.className.includes("btn-info")
|
||||
? "bg-blue-500 hover:bg-blue-600"
|
||||
: btn.className.includes(
|
||||
"btn-gray",
|
||||
)
|
||||
? "bg-gray-500 hover:bg-gray-600"
|
||||
: "bg-green-600 hover:bg-green-700"}"
|
||||
${
|
||||
btn.className.includes("btn-info")
|
||||
? "bg-blue-500 hover:bg-blue-600"
|
||||
: btn.className.includes("btn-gray")
|
||||
? "bg-gray-500 hover:bg-gray-600"
|
||||
: "bg-green-600 hover:bg-green-700"
|
||||
}"
|
||||
@click=${() => {
|
||||
btn.action();
|
||||
if (!btn.preventClose) {
|
||||
@@ -1113,62 +1147,72 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
`,
|
||||
)}
|
||||
<!--- Incoming attacks row -->
|
||||
${this.incomingAttacks.length > 0
|
||||
? html`
|
||||
${
|
||||
this.incomingAttacks.length > 0
|
||||
? html`
|
||||
<tr class="lg:px-2 lg:py-1 p-1">
|
||||
<td class="lg:px-2 lg:py-1 p-1 text-left">
|
||||
${this.renderIncomingAttacks()}
|
||||
</td>
|
||||
</tr>
|
||||
`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
|
||||
<!--- Outgoing attacks row -->
|
||||
${this.outgoingAttacks.length > 0
|
||||
? html`
|
||||
${
|
||||
this.outgoingAttacks.length > 0
|
||||
? html`
|
||||
<tr class="lg:px-2 lg:py-1 p-1">
|
||||
<td class="lg:px-2 lg:py-1 p-1 text-left">
|
||||
${this.renderOutgoingAttacks()}
|
||||
</td>
|
||||
</tr>
|
||||
`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
|
||||
<!--- Outgoing land attacks row -->
|
||||
${this.outgoingLandAttacks.length > 0
|
||||
? html`
|
||||
${
|
||||
this.outgoingLandAttacks.length > 0
|
||||
? html`
|
||||
<tr class="lg:px-2 lg:py-1 p-1">
|
||||
<td class="lg:px-2 lg:py-1 p-1 text-left">
|
||||
${this.renderOutgoingLandAttacks()}
|
||||
</td>
|
||||
</tr>
|
||||
`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
|
||||
<!--- Boats row -->
|
||||
${this.outgoingBoats.length > 0
|
||||
? html`
|
||||
${
|
||||
this.outgoingBoats.length > 0
|
||||
? html`
|
||||
<tr class="lg:px-2 lg:py-1 p-1">
|
||||
<td class="lg:px-2 lg:py-1 p-1 text-left">
|
||||
${this.renderBoats()}
|
||||
</td>
|
||||
</tr>
|
||||
`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
|
||||
<!--- Empty row when no events or attacks -->
|
||||
${filteredEvents.length === 0 &&
|
||||
this.incomingAttacks.length === 0 &&
|
||||
this.outgoingAttacks.length === 0 &&
|
||||
this.outgoingLandAttacks.length === 0 &&
|
||||
this.outgoingBoats.length === 0
|
||||
? html`
|
||||
${
|
||||
filteredEvents.length === 0 &&
|
||||
this.incomingAttacks.length === 0 &&
|
||||
this.outgoingAttacks.length === 0 &&
|
||||
this.outgoingLandAttacks.length === 0 &&
|
||||
this.outgoingBoats.length === 0
|
||||
? html`
|
||||
<tr>
|
||||
<td
|
||||
class="lg:px-2 lg:py-1 p-1 min-w-72 text-left"
|
||||
@@ -1177,13 +1221,15 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
</td>
|
||||
</tr>
|
||||
`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`}
|
||||
`
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { Layer } from "./Layer";
|
||||
import { TogglePerformanceOverlayEvent } from "../../InputHandler";
|
||||
import { UserSettings } from "../../../core/game/UserSettings";
|
||||
import { TogglePerformanceOverlayEvent } from "../../InputHandler";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
@customElement("fps-display")
|
||||
export class FPSDisplay extends LitElement implements Layer {
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { UnitType } from "../../../core/game/Game";
|
||||
import {
|
||||
BonusEventUpdate,
|
||||
ConquestUpdate,
|
||||
GameUpdateType,
|
||||
RailroadUpdate,
|
||||
} from "../../../core/game/GameUpdates";
|
||||
import { Fx, FxType } from "../fx/Fx";
|
||||
import { GameView, UnitView } from "../../../core/game/GameView";
|
||||
import { ShockwaveFx, nukeFxFactory } from "../fx/NukeFx";
|
||||
import { renderNumber } from "../../Utils";
|
||||
import { AnimatedSpriteLoader } from "../AnimatedSpriteLoader";
|
||||
import { Layer } from "./Layer";
|
||||
import { conquestFxFactory } from "../fx/ConquestFx";
|
||||
import { Fx, FxType } from "../fx/Fx";
|
||||
import { nukeFxFactory, ShockwaveFx } from "../fx/NukeFx";
|
||||
import { SpriteFx } from "../fx/SpriteFx";
|
||||
import { TextFx } from "../fx/TextFx";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { UnitExplosionFx } from "../fx/UnitExplosionFx";
|
||||
import { UnitType } from "../../../core/game/Game";
|
||||
import { conquestFxFactory } from "../fx/ConquestFx";
|
||||
import { renderNumber } from "../../Utils";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
export class FxLayer implements Layer {
|
||||
private canvas: HTMLCanvasElement | undefined;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { Colord } from "colord";
|
||||
import { GameMode } from "../../../core/game/Game";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { Layer } from "./Layer";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import leaderboardRegularIcon from "../../../../resources/images/LeaderboardIconRegularWhite.svg";
|
||||
import leaderboardSolidIcon from "../../../../resources/images/LeaderboardIconSolidWhite.svg";
|
||||
import teamRegularIcon from "../../../../resources/images/TeamIconRegularWhite.svg";
|
||||
import teamSolidIcon from "../../../../resources/images/TeamIconSolidWhite.svg";
|
||||
import { GameMode } from "../../../core/game/Game";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { translateText } from "../../Utils";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
@customElement("game-left-sidebar")
|
||||
export class GameLeftSidebar extends LitElement implements Layer {
|
||||
@@ -97,8 +97,9 @@ export class GameLeftSidebar extends LitElement implements Layer {
|
||||
transition-transform duration-300 ease-out transform
|
||||
${this.isVisible ? "translate-x-0" : "-translate-x-full"}`}
|
||||
>
|
||||
${this.isPlayerTeamLabelVisible
|
||||
? html`
|
||||
${
|
||||
this.isPlayerTeamLabelVisible
|
||||
? html`
|
||||
<div
|
||||
class="flex items-center w-full h-8 lg:h-10 text-white py-1 lg:p-2"
|
||||
@contextmenu=${(e: Event) => e.preventDefault()}
|
||||
@@ -109,7 +110,8 @@ export class GameLeftSidebar extends LitElement implements Layer {
|
||||
</span>
|
||||
</div>
|
||||
`
|
||||
: null}
|
||||
: null
|
||||
}
|
||||
<div
|
||||
class=${`flex items-center gap-2 space-x-2 text-white ${
|
||||
this.isLeaderboardShow || this.isTeamLeaderboardShow ? "mb-2" : ""
|
||||
@@ -117,31 +119,37 @@ export class GameLeftSidebar extends LitElement implements Layer {
|
||||
>
|
||||
<div class="w-6 h-6 cursor-pointer" @click=${this.toggleLeaderboard}>
|
||||
<img
|
||||
src=${this.isLeaderboardShow
|
||||
? leaderboardSolidIcon
|
||||
: leaderboardRegularIcon}
|
||||
src=${
|
||||
this.isLeaderboardShow
|
||||
? leaderboardSolidIcon
|
||||
: leaderboardRegularIcon
|
||||
}
|
||||
alt="treeIcon"
|
||||
width="20"
|
||||
height="20"
|
||||
/>
|
||||
</div>
|
||||
${this.isTeamGame
|
||||
? html`
|
||||
${
|
||||
this.isTeamGame
|
||||
? html`
|
||||
<div
|
||||
class="w-6 h-6 cursor-pointer"
|
||||
@click=${this.toggleTeamLeaderboard}
|
||||
>
|
||||
<img
|
||||
src=${this.isTeamLeaderboardShow
|
||||
? teamSolidIcon
|
||||
: teamRegularIcon}
|
||||
src=${
|
||||
this.isTeamLeaderboardShow
|
||||
? teamSolidIcon
|
||||
: teamRegularIcon
|
||||
}
|
||||
alt="treeIcon"
|
||||
width="20"
|
||||
height="20"
|
||||
/>
|
||||
</div>
|
||||
`
|
||||
: null}
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
<div class="block lg:flex flex-wrap gap-2">
|
||||
<leader-board .visible=${this.isLeaderboardShow}></leader-board>
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { GameType } from "../../../core/game/Game";
|
||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { Layer } from "./Layer";
|
||||
import { PauseGameEvent } from "../../Transport";
|
||||
import { ShowReplayPanelEvent } from "./ReplayPanel";
|
||||
import { ShowSettingsModalEvent } from "./SettingsModal";
|
||||
import exitIcon from "../../../../resources/images/ExitIconWhite.svg";
|
||||
import pauseIcon from "../../../../resources/images/PauseIconWhite.svg";
|
||||
import playIcon from "../../../../resources/images/PlayIconWhite.svg";
|
||||
import replayRegularIcon from "../../../../resources/images/ReplayRegularIconWhite.svg";
|
||||
import replaySolidIcon from "../../../../resources/images/ReplaySolidIconWhite.svg";
|
||||
import settingsIcon from "../../../../resources/images/SettingIconWhite.svg";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { GameType } from "../../../core/game/Game";
|
||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { PauseGameEvent } from "../../Transport";
|
||||
import { translateText } from "../../Utils";
|
||||
import { Layer } from "./Layer";
|
||||
import { ShowReplayPanelEvent } from "./ReplayPanel";
|
||||
import { ShowSettingsModalEvent } from "./SettingsModal";
|
||||
|
||||
@customElement("game-right-sidebar")
|
||||
export class GameRightSidebar extends LitElement implements Layer {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { EventBus, GameEvent } from "../../../core/EventBus";
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { Layer } from "./Layer";
|
||||
import { EventBus, GameEvent } from "../../../core/EventBus";
|
||||
import { getGamesPlayed } from "../../Utils";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
export class GutterAdModalEvent implements GameEvent {
|
||||
constructor(public readonly isVisible: boolean) {}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { Layer } from "./Layer";
|
||||
import { translateText } from "../../Utils";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
@customElement("heads-up-message")
|
||||
export class HeadsUpMessage extends LitElement implements Layer {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { EventBus, GameEvent } from "../../../core/EventBus";
|
||||
import { GameView, PlayerView, UnitView } from "../../../core/game/GameView";
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { Layer } from "./Layer";
|
||||
import { renderNumber } from "../../Utils";
|
||||
import { repeat } from "lit/directives/repeat.js";
|
||||
import { translateText } from "../../../client/Utils";
|
||||
import { EventBus, GameEvent } from "../../../core/EventBus";
|
||||
import { GameView, PlayerView, UnitView } from "../../../core/game/GameView";
|
||||
import { renderNumber } from "../../Utils";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
type Entry = {
|
||||
name: string;
|
||||
@@ -169,10 +169,9 @@ export class Leaderboard extends LitElement implements Layer {
|
||||
}
|
||||
return html`
|
||||
<div
|
||||
class="max-h-[35vh] overflow-y-auto text-white text-xs md:text-xs lg:text-sm md:max-h-[50vh] ${this
|
||||
.visible
|
||||
? ""
|
||||
: "hidden"}"
|
||||
class="max-h-[35vh] overflow-y-auto text-white text-xs md:text-xs lg:text-sm md:max-h-[50vh] ${
|
||||
this.visible ? "" : "hidden"
|
||||
}"
|
||||
@contextmenu=${(e: Event) => e.preventDefault()}
|
||||
>
|
||||
<div
|
||||
@@ -191,33 +190,39 @@ export class Leaderboard extends LitElement implements Layer {
|
||||
@click=${() => this.setSort("tiles")}
|
||||
>
|
||||
${translateText("leaderboard.owned")}
|
||||
${this._sortKey === "tiles"
|
||||
? this._sortOrder === "asc"
|
||||
? "⬆️"
|
||||
: "⬇️"
|
||||
: ""}
|
||||
${
|
||||
this._sortKey === "tiles"
|
||||
? this._sortOrder === "asc"
|
||||
? "⬆️"
|
||||
: "⬇️"
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
<div
|
||||
class="py-1 md:py-2 text-center border-b border-slate-500 cursor-pointer whitespace-nowrap"
|
||||
@click=${() => this.setSort("gold")}
|
||||
>
|
||||
${translateText("leaderboard.gold")}
|
||||
${this._sortKey === "gold"
|
||||
? this._sortOrder === "asc"
|
||||
? "⬆️"
|
||||
: "⬇️"
|
||||
: ""}
|
||||
${
|
||||
this._sortKey === "gold"
|
||||
? this._sortOrder === "asc"
|
||||
? "⬆️"
|
||||
: "⬇️"
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
<div
|
||||
class="py-1 md:py-2 text-center border-b border-slate-500 cursor-pointer whitespace-nowrap"
|
||||
@click=${() => this.setSort("troops")}
|
||||
>
|
||||
${translateText("leaderboard.troops")}
|
||||
${this._sortKey === "troops"
|
||||
? this._sortOrder === "asc"
|
||||
? "⬆️"
|
||||
: "⬇️"
|
||||
: ""}
|
||||
${
|
||||
this._sortKey === "troops"
|
||||
? this._sortOrder === "asc"
|
||||
? "⬆️"
|
||||
: "⬇️"
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -226,9 +231,9 @@ export class Leaderboard extends LitElement implements Layer {
|
||||
(p) => p.player.id(),
|
||||
(player) => html`
|
||||
<div
|
||||
class="contents hover:bg-slate-600/60 ${player.isMyPlayer
|
||||
? "font-bold"
|
||||
: ""} cursor-pointer"
|
||||
class="contents hover:bg-slate-600/60 ${
|
||||
player.isMyPlayer ? "font-bold" : ""
|
||||
} cursor-pointer"
|
||||
@click=${() => this.handleRowClickPlayer(player.player)}
|
||||
>
|
||||
<div class="py-1 md:py-2 text-center border-b border-slate-500">
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import {
|
||||
COLORS,
|
||||
MenuElementParams,
|
||||
centerButtonElement,
|
||||
rootMenuElement,
|
||||
} from "./RadialMenuElements";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { RadialMenu, RadialMenuConfig } from "./RadialMenu";
|
||||
import { BuildMenu } from "./BuildMenu";
|
||||
import { ChatIntegration } from "./ChatIntegration";
|
||||
import { ContextMenuEvent } from "../../InputHandler";
|
||||
import { EmojiTable } from "./EmojiTable";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { Layer } from "./Layer";
|
||||
import { LitElement } from "lit";
|
||||
import { PlayerActionHandler } from "./PlayerActionHandler";
|
||||
import { PlayerActions } from "../../../core/game/Game";
|
||||
import { PlayerPanel } from "./PlayerPanel";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { UIState } from "../UIState";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
import swordIcon from "../../../../resources/images/SwordIconWhite.svg";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { PlayerActions } from "../../../core/game/Game";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { ContextMenuEvent } from "../../InputHandler";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { UIState } from "../UIState";
|
||||
import { BuildMenu } from "./BuildMenu";
|
||||
import { ChatIntegration } from "./ChatIntegration";
|
||||
import { EmojiTable } from "./EmojiTable";
|
||||
import { Layer } from "./Layer";
|
||||
import { PlayerActionHandler } from "./PlayerActionHandler";
|
||||
import { PlayerPanel } from "./PlayerPanel";
|
||||
import { RadialMenu, RadialMenuConfig } from "./RadialMenu";
|
||||
import {
|
||||
COLORS,
|
||||
centerButtonElement,
|
||||
MenuElementParams,
|
||||
rootMenuElement,
|
||||
} from "./RadialMenuElements";
|
||||
|
||||
@customElement("main-radial-menu")
|
||||
export class MainRadialMenu extends LitElement implements Layer {
|
||||
@@ -90,13 +90,7 @@ export class MainRadialMenu extends LitElement implements Layer {
|
||||
const actions = await myPlayer.actions(tile);
|
||||
// Stale check: user might have clicked somewhere else already
|
||||
if (this.clickedTile !== tile) return;
|
||||
this.updatePlayerActions(
|
||||
myPlayer,
|
||||
actions,
|
||||
tile,
|
||||
event.x,
|
||||
event.y,
|
||||
);
|
||||
this.updatePlayerActions(myPlayer, actions, tile, event.x, event.y);
|
||||
} catch (err) {
|
||||
console.error("Failed to fetch player actions:", err);
|
||||
}
|
||||
@@ -152,11 +146,7 @@ export class MainRadialMenu extends LitElement implements Layer {
|
||||
try {
|
||||
const actions = await myPlayer.actions(tile);
|
||||
if (this.clickedTile !== tile) return; // stale
|
||||
this.updatePlayerActions(
|
||||
myPlayer,
|
||||
actions,
|
||||
tile,
|
||||
);
|
||||
this.updatePlayerActions(myPlayer, actions, tile);
|
||||
} catch (err) {
|
||||
console.error("Failed to refresh player actions:", err);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { GameEnv } from "../../../core/configuration/Config";
|
||||
import { GameType } from "../../../core/game/Game";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { Layer } from "./Layer";
|
||||
import { MultiTabDetector } from "../../MultiTabDetector";
|
||||
import { translateText } from "../../Utils";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
@customElement("multi-tab-modal")
|
||||
export class MultiTabModal extends LitElement implements Layer {
|
||||
|
||||
@@ -1,13 +1,3 @@
|
||||
import { AllPlayers, Cell, nukeTypes } from "../../../core/game/Game";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { createCanvas, renderNumber, renderTroops } from "../../Utils";
|
||||
import { AlternateViewEvent } from "../../InputHandler";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { Layer } from "./Layer";
|
||||
import { PseudoRandom } from "../../../core/PseudoRandom";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { UserSettings } from "../../../core/game/UserSettings";
|
||||
import allianceIcon from "../../../../resources/images/AllianceIcon.svg";
|
||||
import allianceRequestBlackIcon from "../../../../resources/images/AllianceRequestBlackIcon.svg";
|
||||
import allianceRequestWhiteIcon from "../../../../resources/images/AllianceRequestWhiteIcon.svg";
|
||||
@@ -17,10 +7,20 @@ import embargoBlackIcon from "../../../../resources/images/EmbargoBlackIcon.svg"
|
||||
import embargoWhiteIcon from "../../../../resources/images/EmbargoWhiteIcon.svg";
|
||||
import nukeRedIcon from "../../../../resources/images/NukeIconRed.svg";
|
||||
import nukeWhiteIcon from "../../../../resources/images/NukeIconWhite.svg";
|
||||
import { renderPlayerFlag } from "../../../core/CustomFlag";
|
||||
import shieldIcon from "../../../../resources/images/ShieldIconBlack.svg";
|
||||
import targetIcon from "../../../../resources/images/TargetIcon.svg";
|
||||
import traitorIcon from "../../../../resources/images/TraitorIcon.svg";
|
||||
import { renderPlayerFlag } from "../../../core/CustomFlag";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { AllPlayers, Cell, nukeTypes } from "../../../core/game/Game";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { UserSettings } from "../../../core/game/UserSettings";
|
||||
import { PseudoRandom } from "../../../core/PseudoRandom";
|
||||
import { AlternateViewEvent } from "../../InputHandler";
|
||||
import { createCanvas, renderNumber, renderTroops } from "../../Utils";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
class RenderInfo {
|
||||
public icons: Map<string, HTMLImageElement> = new Map(); // Track icon elements
|
||||
@@ -611,8 +611,8 @@ export class NameLayer implements Layer {
|
||||
// Position element with scale
|
||||
if (render.location && render.location !== oldLocation) {
|
||||
const scale = Math.min(baseSize * 0.25, 3);
|
||||
render.element.style.transform =
|
||||
`translate(${render.location.x}px, ${render.location.y}px) translate(-50%, -50%) scale(${scale})`;
|
||||
// eslint-disable-next-line max-len
|
||||
render.element.style.transform = `translate(${render.location.x}px, ${render.location.y}px) translate(-50%, -50%) scale(${scale})`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { AlternateViewEvent, RedrawGraphicsEvent } from "../../InputHandler";
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { GameType } from "../../../core/game/Game";
|
||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { Layer } from "./Layer";
|
||||
import { PauseGameEvent } from "../../Transport";
|
||||
import { UserSettings } from "../../../core/game/UserSettings";
|
||||
import { AlternateViewEvent, RedrawGraphicsEvent } from "../../InputHandler";
|
||||
import { PauseGameEvent } from "../../Transport";
|
||||
import { translateText } from "../../Utils";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
const button = ({
|
||||
classes = "",
|
||||
@@ -209,10 +209,7 @@ export class OptionsMenu extends LitElement implements Layer {
|
||||
<div
|
||||
class="options-menu flex flex-col justify-around gap-y-3 mt-2
|
||||
bg-opacity-60 bg-gray-900 p-1 lg:p-2 rounded-lg backdrop-blur-md
|
||||
${!this
|
||||
.showSettings
|
||||
? "hidden"
|
||||
: ""}"
|
||||
${!this.showSettings ? "hidden" : ""}"
|
||||
>
|
||||
${button({
|
||||
onClick: this.onTerrainButtonClick,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { PlayerActions, PlayerID } from "../../../core/game/Game";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { PlayerView } from "../../../core/game/GameView";
|
||||
import {
|
||||
SendAllianceRequestIntentEvent,
|
||||
SendAttackIntentEvent,
|
||||
@@ -13,9 +16,6 @@ import {
|
||||
SendSpawnIntentEvent,
|
||||
SendTargetPlayerIntentEvent,
|
||||
} from "../../Transport";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { PlayerView } from "../../../core/game/GameView";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { UIState } from "../UIState";
|
||||
|
||||
export class PlayerActionHandler {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { ContextMenuEvent, MouseMoveEvent } from "../../InputHandler";
|
||||
import { GameView, PlayerView, UnitView } from "../../../core/game/GameView";
|
||||
import { LitElement, TemplateResult, html } from "lit";
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { ref } from "lit-html/directives/ref.js";
|
||||
import { translateText } from "../../../client/Utils";
|
||||
import { renderPlayerFlag } from "../../../core/CustomFlag";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import {
|
||||
PlayerProfile,
|
||||
PlayerType,
|
||||
@@ -8,16 +11,13 @@ import {
|
||||
Unit,
|
||||
UnitType,
|
||||
} from "../../../core/game/Game";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { renderNumber, renderTroops } from "../../Utils";
|
||||
import { CloseRadialMenuEvent } from "./RadialMenu";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { Layer } from "./Layer";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { GameView, PlayerView, UnitView } from "../../../core/game/GameView";
|
||||
import { ContextMenuEvent, MouseMoveEvent } from "../../InputHandler";
|
||||
import { renderNumber, renderTroops } from "../../Utils";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { ref } from "lit-html/directives/ref.js";
|
||||
import { renderPlayerFlag } from "../../../core/CustomFlag";
|
||||
import { translateText } from "../../../client/Utils";
|
||||
import { Layer } from "./Layer";
|
||||
import { CloseRadialMenuEvent } from "./RadialMenu";
|
||||
|
||||
function euclideanDistWorld(
|
||||
coord: { x: number; y: number },
|
||||
@@ -222,17 +222,18 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
|
||||
return html`
|
||||
<div class="p-2">
|
||||
<button
|
||||
class="text-bold text-sm lg:text-lg font-bold mb-1 inline-flex break-all ${isFriendly
|
||||
? "text-green-500"
|
||||
: "text-white"}"
|
||||
class="text-bold text-sm lg:text-lg font-bold mb-1 inline-flex break-all ${
|
||||
isFriendly ? "text-green-500" : "text-white"
|
||||
}"
|
||||
@click=${() => {
|
||||
this.showDetails = !this.showDetails;
|
||||
this.requestUpdate?.();
|
||||
}}
|
||||
>
|
||||
${player.cosmetics.flag
|
||||
? player.cosmetics.flag.startsWith("!")
|
||||
? html`<div
|
||||
${
|
||||
player.cosmetics.flag
|
||||
? player.cosmetics.flag.startsWith("!")
|
||||
? html`<div
|
||||
class="h-8 mr-1 aspect-[3/4] player-flag"
|
||||
${ref((el) => {
|
||||
if (el instanceof HTMLElement) {
|
||||
@@ -242,38 +243,46 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
|
||||
}
|
||||
})}
|
||||
></div>`
|
||||
: html`<img
|
||||
: html`<img
|
||||
class="h-8 mr-1 aspect-[3/4]"
|
||||
src=${"/flags/" + player.cosmetics.flag + ".svg"}
|
||||
/>`
|
||||
: html``}
|
||||
: html``
|
||||
}
|
||||
${player.name()}
|
||||
</button>
|
||||
|
||||
<!-- Collapsible section -->
|
||||
${this.showDetails
|
||||
? html`
|
||||
${player.team() !== null
|
||||
? html`<div class="text-sm opacity-80">
|
||||
${
|
||||
this.showDetails
|
||||
? html`
|
||||
${
|
||||
player.team() !== null
|
||||
? html`<div class="text-sm opacity-80">
|
||||
${translateText("player_info_overlay.team")}:
|
||||
${player.team()}
|
||||
</div>`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
<div class="text-sm opacity-80">
|
||||
${translateText("player_info_overlay.type")}: ${playerType}
|
||||
</div>
|
||||
${player.troops() >= 1
|
||||
? html`<div class="text-sm opacity-80" translate="no">
|
||||
${
|
||||
player.troops() >= 1
|
||||
? html`<div class="text-sm opacity-80" translate="no">
|
||||
${translateText("player_info_overlay.d_troops")}:
|
||||
${renderTroops(player.troops())}
|
||||
</div>`
|
||||
: ""}
|
||||
${attackingTroops >= 1
|
||||
? html`<div class="text-sm opacity-80" translate="no">
|
||||
: ""
|
||||
}
|
||||
${
|
||||
attackingTroops >= 1
|
||||
? html`<div class="text-sm opacity-80" translate="no">
|
||||
${translateText("player_info_overlay.a_troops")}:
|
||||
${renderTroops(attackingTroops)}
|
||||
</div>`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
<div class="text-sm opacity-80" translate="no">
|
||||
${translateText("player_info_overlay.gold")}:
|
||||
${renderNumber(player.gold())}
|
||||
@@ -310,7 +319,8 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
|
||||
)}
|
||||
${relationHtml}
|
||||
`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -328,14 +338,16 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
<div class="text-sm opacity-80">${unit.type()}</div>
|
||||
${unit.hasHealth()
|
||||
? html`
|
||||
${
|
||||
unit.hasHealth()
|
||||
? html`
|
||||
<div class="text-sm opacity-80">
|
||||
${translateText("player_info_overlay.health")}:
|
||||
${unit.health()}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import allianceIcon from "../../../../resources/images/AllianceIconWhite.svg";
|
||||
import chatIcon from "../../../../resources/images/ChatIconWhite.svg";
|
||||
import donateGoldIcon from "../../../../resources/images/DonateGoldIconWhite.svg";
|
||||
import donateTroopIcon from "../../../../resources/images/DonateTroopIconWhite.svg";
|
||||
import emojiIcon from "../../../../resources/images/EmojiIconWhite.svg";
|
||||
import targetIcon from "../../../../resources/images/TargetIconWhite.svg";
|
||||
import traitorIcon from "../../../../resources/images/TraitorIconWhite.svg";
|
||||
import { translateText } from "../../../client/Utils";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { AllPlayers, PlayerActions } from "../../../core/game/Game";
|
||||
import { CloseViewEvent, MouseUpEvent } from "../../InputHandler";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { LitElement, html } from "lit";
|
||||
import { flattenedEmojiTable } from "../../../core/Util";
|
||||
import Countries from "../../data/countries.json";
|
||||
import { CloseViewEvent, MouseUpEvent } from "../../InputHandler";
|
||||
import {
|
||||
SendAllianceRequestIntentEvent,
|
||||
SendBreakAllianceIntentEvent,
|
||||
@@ -11,24 +24,11 @@ import {
|
||||
SendEmojiIntentEvent,
|
||||
SendTargetPlayerIntentEvent,
|
||||
} from "../../Transport";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { renderNumber, renderTroops } from "../../Utils";
|
||||
import { ChatModal } from "./ChatModal";
|
||||
import Countries from "../../data/countries.json";
|
||||
import { EmojiTable } from "./EmojiTable";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { Layer } from "./Layer";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { UIState } from "../UIState";
|
||||
import allianceIcon from "../../../../resources/images/AllianceIconWhite.svg";
|
||||
import chatIcon from "../../../../resources/images/ChatIconWhite.svg";
|
||||
import donateGoldIcon from "../../../../resources/images/DonateGoldIconWhite.svg";
|
||||
import donateTroopIcon from "../../../../resources/images/DonateTroopIconWhite.svg";
|
||||
import emojiIcon from "../../../../resources/images/EmojiIconWhite.svg";
|
||||
import { flattenedEmojiTable } from "../../../core/Util";
|
||||
import targetIcon from "../../../../resources/images/TargetIconWhite.svg";
|
||||
import traitorIcon from "../../../../resources/images/TraitorIconWhite.svg";
|
||||
import { translateText } from "../../../client/Utils";
|
||||
import { ChatModal } from "./ChatModal";
|
||||
import { EmojiTable } from "./EmojiTable";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
@customElement("player-panel")
|
||||
export class PlayerPanel extends LitElement implements Layer {
|
||||
@@ -195,10 +195,7 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
const remainingTicks = expiresAt - this.g.ticks();
|
||||
|
||||
if (remainingTicks > 0) {
|
||||
const remainingSeconds = Math.max(
|
||||
0,
|
||||
Math.floor(remainingTicks / 10),
|
||||
); // 10 ticks per second
|
||||
const remainingSeconds = Math.max(0, Math.floor(remainingTicks / 10)); // 10 ticks per second
|
||||
this.allianceExpiryText = this.formatDuration(remainingSeconds);
|
||||
}
|
||||
} else {
|
||||
@@ -246,7 +243,10 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
|
||||
//flag icon in the playerPanel
|
||||
const flagCode = other.cosmetics.flag;
|
||||
const country = typeof flagCode === "string" ? Countries.find((c) => c.code === flagCode) : undefined;
|
||||
const country =
|
||||
typeof flagCode === "string"
|
||||
? Countries.find((c) => c.code === flagCode)
|
||||
: undefined;
|
||||
const flagName = country?.name;
|
||||
|
||||
return html`
|
||||
@@ -283,8 +283,9 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
</div>
|
||||
</div>
|
||||
<!-- Flag -->
|
||||
${country
|
||||
? html`
|
||||
${
|
||||
country
|
||||
? html`
|
||||
<div>
|
||||
<div class="text-white text-opacity-80 text-sm px-2">
|
||||
${translateText("player_panel.flag")}
|
||||
@@ -298,7 +299,8 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
<!-- Resources section -->
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="flex flex-col gap-1">
|
||||
@@ -333,9 +335,11 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
${translateText("player_panel.traitor")}
|
||||
</div>
|
||||
<div class="bg-opacity-50 bg-gray-700 rounded p-2 text-white">
|
||||
${other.isTraitor()
|
||||
? translateText("player_panel.yes")
|
||||
: translateText("player_panel.no")}
|
||||
${
|
||||
other.isTraitor()
|
||||
? translateText("player_panel.yes")
|
||||
: translateText("player_panel.no")
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -355,9 +359,11 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
${translateText("player_panel.embargo")}
|
||||
</div>
|
||||
<div class="bg-opacity-50 bg-gray-700 rounded p-2 text-white">
|
||||
${other.hasEmbargoAgainst(myPlayer)
|
||||
? translateText("player_panel.yes")
|
||||
: translateText("player_panel.no")}
|
||||
${
|
||||
other.hasEmbargoAgainst(myPlayer)
|
||||
? translateText("player_panel.yes")
|
||||
: translateText("player_panel.no")
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -371,17 +377,20 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
class="bg-opacity-50 bg-gray-700 rounded p-2 text-white max-w-72 max-h-20 overflow-y-auto"
|
||||
translate="no"
|
||||
>
|
||||
${other.allies().length > 0
|
||||
? other
|
||||
.allies()
|
||||
.map((p) => p.name())
|
||||
.join(", ")
|
||||
: translateText("player_panel.none")}
|
||||
${
|
||||
other.allies().length > 0
|
||||
? other
|
||||
.allies()
|
||||
.map((p) => p.name())
|
||||
.join(", ")
|
||||
: translateText("player_panel.none")
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${this.allianceExpiryText !== null
|
||||
? html`
|
||||
${
|
||||
this.allianceExpiryText !== null
|
||||
? html`
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="text-white text-opacity-80 text-sm px-2">
|
||||
${translateText("player_panel.alliance_time_remaining")}
|
||||
@@ -393,7 +402,8 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
|
||||
<!-- Action buttons -->
|
||||
<div class="flex justify-center gap-2">
|
||||
@@ -406,8 +416,9 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
>
|
||||
<img src=${chatIcon} alt="Target" class="w-6 h-6" />
|
||||
</button>
|
||||
${canTarget
|
||||
? html`<button
|
||||
${
|
||||
canTarget
|
||||
? html`<button
|
||||
@click=${(e: MouseEvent) =>
|
||||
this.handleTargetClick(e, other)}
|
||||
class="w-10 h-10 flex items-center justify-center
|
||||
@@ -416,9 +427,11 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
>
|
||||
<img src=${targetIcon} alt="Target" class="w-6 h-6" />
|
||||
</button>`
|
||||
: ""}
|
||||
${canBreakAlliance
|
||||
? html`<button
|
||||
: ""
|
||||
}
|
||||
${
|
||||
canBreakAlliance
|
||||
? html`<button
|
||||
@click=${(e: MouseEvent) =>
|
||||
this.handleBreakAllianceClick(e, myPlayer, other)}
|
||||
class="w-10 h-10 flex items-center justify-center
|
||||
@@ -431,9 +444,11 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
class="w-6 h-6"
|
||||
/>
|
||||
</button>`
|
||||
: ""}
|
||||
${canSendAllianceRequest
|
||||
? html`<button
|
||||
: ""
|
||||
}
|
||||
${
|
||||
canSendAllianceRequest
|
||||
? html`<button
|
||||
@click=${(e: MouseEvent) =>
|
||||
this.handleAllianceClick(e, myPlayer, other)}
|
||||
class="w-10 h-10 flex items-center justify-center
|
||||
@@ -442,9 +457,11 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
>
|
||||
<img src=${allianceIcon} alt="Alliance" class="w-6 h-6" />
|
||||
</button>`
|
||||
: ""}
|
||||
${canDonateTroops
|
||||
? html`<button
|
||||
: ""
|
||||
}
|
||||
${
|
||||
canDonateTroops
|
||||
? html`<button
|
||||
@click=${(e: MouseEvent) =>
|
||||
this.handleDonateTroopClick(e, myPlayer, other)}
|
||||
class="w-10 h-10 flex items-center justify-center
|
||||
@@ -457,9 +474,11 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
class="w-6 h-6"
|
||||
/>
|
||||
</button>`
|
||||
: ""}
|
||||
${canDonateGold
|
||||
? html`<button
|
||||
: ""
|
||||
}
|
||||
${
|
||||
canDonateGold
|
||||
? html`<button
|
||||
@click=${(e: MouseEvent) =>
|
||||
this.handleDonateGoldClick(e, myPlayer, other)}
|
||||
class="w-10 h-10 flex items-center justify-center
|
||||
@@ -468,9 +487,11 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
>
|
||||
<img src=${donateGoldIcon} alt="Donate" class="w-6 h-6" />
|
||||
</button>`
|
||||
: ""}
|
||||
${canSendEmoji
|
||||
? html`<button
|
||||
: ""
|
||||
}
|
||||
${
|
||||
canSendEmoji
|
||||
? html`<button
|
||||
@click=${(e: MouseEvent) =>
|
||||
this.handleEmojiClick(e, myPlayer, other)}
|
||||
class="w-10 h-10 flex items-center justify-center
|
||||
@@ -479,10 +500,12 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
>
|
||||
<img src=${emojiIcon} alt="Emoji" class="w-6 h-6" />
|
||||
</button>`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
${canEmbargo && other !== myPlayer
|
||||
? html`<button
|
||||
${
|
||||
canEmbargo && other !== myPlayer
|
||||
? html`<button
|
||||
@click=${(e: MouseEvent) =>
|
||||
this.handleEmbargoClick(e, myPlayer, other)}
|
||||
class="w-100 h-10 flex items-center justify-center
|
||||
@@ -491,9 +514,11 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
>
|
||||
${translateText("player_panel.stop_trade")}
|
||||
</button>`
|
||||
: ""}
|
||||
${!canEmbargo && other !== myPlayer
|
||||
? html`<button
|
||||
: ""
|
||||
}
|
||||
${
|
||||
!canEmbargo && other !== myPlayer
|
||||
? html`<button
|
||||
@click=${(e: MouseEvent) =>
|
||||
this.handleStopEmbargoClick(e, myPlayer, other)}
|
||||
class="w-100 h-10 flex items-center justify-center
|
||||
@@ -502,7 +527,8 @@ export class PlayerPanel extends LitElement implements Layer {
|
||||
>
|
||||
${translateText("player_panel.start_trade")}
|
||||
</button>`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/* eslint-disable max-lines */
|
||||
import * as d3 from "d3";
|
||||
import backIcon from "../../../../resources/images/BackIconWhite.svg";
|
||||
import { EventBus, GameEvent } from "../../../core/EventBus";
|
||||
import { CloseViewEvent } from "../../InputHandler";
|
||||
import { translateText } from "../../Utils";
|
||||
import { Layer } from "./Layer";
|
||||
import {
|
||||
CenterButtonElement,
|
||||
MenuElement,
|
||||
MenuElementParams,
|
||||
TooltipKey,
|
||||
} from "./RadialMenuElements";
|
||||
import { EventBus, GameEvent } from "../../../core/EventBus";
|
||||
import { CloseViewEvent } from "../../InputHandler";
|
||||
import { Layer } from "./Layer";
|
||||
import backIcon from "../../../../resources/images/BackIconWhite.svg";
|
||||
import { translateText } from "../../Utils";
|
||||
|
||||
export class CloseRadialMenuEvent implements GameEvent {
|
||||
constructor() {}
|
||||
@@ -41,7 +41,9 @@ type CenterButtonState = "default" | "back";
|
||||
type RequiredRadialMenuConfig = Required<RadialMenuConfig>;
|
||||
|
||||
export class RadialMenu implements Layer {
|
||||
private menuElement: d3.Selection<HTMLDivElement, unknown, null, undefined> | undefined;
|
||||
private menuElement:
|
||||
| d3.Selection<HTMLDivElement, unknown, null, undefined>
|
||||
| undefined;
|
||||
private tooltipElement: HTMLDivElement | null = null;
|
||||
private isVisible = false;
|
||||
|
||||
@@ -150,9 +152,12 @@ export class RadialMenu implements Layer {
|
||||
.style("position", "absolute")
|
||||
.style("top", "50%")
|
||||
.style("left", "50%")
|
||||
.style("transition", `top ${
|
||||
this.config.menuTransitionDuration}ms ease, left ${
|
||||
this.config.menuTransitionDuration}ms ease`)
|
||||
.style(
|
||||
"transition",
|
||||
`top ${this.config.menuTransitionDuration}ms ease, left ${
|
||||
this.config.menuTransitionDuration
|
||||
}ms ease`,
|
||||
)
|
||||
.style("transform", "translate(-50%, -50%)")
|
||||
.style("pointer-events", "all")
|
||||
.on("click", (event) => this.hideRadialMenu());
|
||||
@@ -392,9 +397,10 @@ export class RadialMenu implements Layer {
|
||||
>,
|
||||
level: number,
|
||||
) {
|
||||
const onHover = (d: d3.PieArcDatum<MenuElement>, path: d3.Selection<
|
||||
d3.BaseType, unknown, HTMLElement, unknown
|
||||
>) => {
|
||||
const onHover = (
|
||||
d: d3.PieArcDatum<MenuElement>,
|
||||
path: d3.Selection<d3.BaseType, unknown, HTMLElement, unknown>,
|
||||
) => {
|
||||
const disabled = this.params === null || d.data.disabled(this.params);
|
||||
if (d.data.tooltipItems && d.data.tooltipItems.length > 0) {
|
||||
this.showTooltip(d.data.tooltipItems);
|
||||
@@ -413,9 +419,10 @@ export class RadialMenu implements Layer {
|
||||
path.attr("stroke-width", "3");
|
||||
};
|
||||
|
||||
const onMouseOut = (d: d3.PieArcDatum<MenuElement>, path: d3.Selection<
|
||||
d3.BaseType, unknown, HTMLElement, unknown
|
||||
>) => {
|
||||
const onMouseOut = (
|
||||
d: d3.PieArcDatum<MenuElement>,
|
||||
path: d3.Selection<d3.BaseType, unknown, HTMLElement, unknown>,
|
||||
) => {
|
||||
const disabled = this.params === null || d.data.disabled(this.params);
|
||||
if (this.submenuHoverTimeout !== null) {
|
||||
window.clearTimeout(this.submenuHoverTimeout);
|
||||
@@ -1072,14 +1079,18 @@ export class RadialMenu implements Layer {
|
||||
const vh = window.innerHeight;
|
||||
|
||||
// If the menu cannot fully fit on an axis, pin it to the viewport center on that axis.
|
||||
const clampedX = 2 * margin > vw ? vw / 2 : Math.min(Math.max(this.anchorX, margin), vw - margin);
|
||||
const clampedY = 2 * margin > vh ? vh / 2 : Math.min(Math.max(this.anchorY, margin), vh - margin);
|
||||
const clampedX =
|
||||
2 * margin > vw
|
||||
? vw / 2
|
||||
: Math.min(Math.max(this.anchorX, margin), vw - margin);
|
||||
const clampedY =
|
||||
2 * margin > vh
|
||||
? vh / 2
|
||||
: Math.min(Math.max(this.anchorY, margin), vh - margin);
|
||||
|
||||
if (this.menuElement === undefined) throw new Error("Not initialized");
|
||||
const svgSel = this.menuElement.select("svg");
|
||||
svgSel
|
||||
.style("top", `${clampedY}px`)
|
||||
.style("left", `${clampedX}px`);
|
||||
svgSel.style("top", `${clampedY}px`).style("left", `${clampedX}px`);
|
||||
}
|
||||
|
||||
private readonly handleResize = () => {
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
import { AllPlayers, PlayerActions, UnitType } from "../../../core/game/Game";
|
||||
import { BuildItemDisplay, BuildMenu, flattenedBuildTable } from "./BuildMenu";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { renderNumber, translateText } from "../../Utils";
|
||||
import { ChatIntegration } from "./ChatIntegration";
|
||||
import { Config } from "../../../core/configuration/Config";
|
||||
import { EmojiTable } from "./EmojiTable";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { PlayerActionHandler } from "./PlayerActionHandler";
|
||||
import { PlayerPanel } from "./PlayerPanel";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { TooltipItem } from "./RadialMenu";
|
||||
import allianceIcon from "../../../../resources/images/AllianceIconWhite.svg";
|
||||
import boatIcon from "../../../../resources/images/BoatIconWhite.svg";
|
||||
import buildIcon from "../../../../resources/images/BuildIconWhite.svg";
|
||||
@@ -17,12 +5,24 @@ import chatIcon from "../../../../resources/images/ChatIconWhite.svg";
|
||||
import donateGoldIcon from "../../../../resources/images/DonateGoldIconWhite.svg";
|
||||
import donateTroopIcon from "../../../../resources/images/DonateTroopIconWhite.svg";
|
||||
import emojiIcon from "../../../../resources/images/EmojiIconWhite.svg";
|
||||
import { flattenedEmojiTable } from "../../../core/Util";
|
||||
import infoIcon from "../../../../resources/images/InfoIcon.svg";
|
||||
import swordIcon from "../../../../resources/images/SwordIconWhite.svg";
|
||||
import targetIcon from "../../../../resources/images/TargetIconWhite.svg";
|
||||
import traitorIcon from "../../../../resources/images/TraitorIconWhite.svg";
|
||||
import xIcon from "../../../../resources/images/XIcon.svg";
|
||||
import { Config } from "../../../core/configuration/Config";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { AllPlayers, PlayerActions, UnitType } from "../../../core/game/Game";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { flattenedEmojiTable } from "../../../core/Util";
|
||||
import { renderNumber, translateText } from "../../Utils";
|
||||
import { BuildItemDisplay, BuildMenu, flattenedBuildTable } from "./BuildMenu";
|
||||
import { ChatIntegration } from "./ChatIntegration";
|
||||
import { EmojiTable } from "./EmojiTable";
|
||||
import { PlayerActionHandler } from "./PlayerActionHandler";
|
||||
import { PlayerPanel } from "./PlayerPanel";
|
||||
import { TooltipItem } from "./RadialMenu";
|
||||
|
||||
export type MenuElementParams = {
|
||||
myPlayer: PlayerView;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { Colord } from "colord";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { PlayerID } from "../../../core/game/Game";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import {
|
||||
GameUpdateType,
|
||||
RailroadUpdate,
|
||||
RailTile,
|
||||
RailType,
|
||||
RailroadUpdate,
|
||||
} from "../../../core/game/GameUpdates";
|
||||
import { Colord } from "colord";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { Layer } from "./Layer";
|
||||
import { PlayerID } from "../../../core/game/Game";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { getRailroadRects } from "./RailroadSprites";
|
||||
|
||||
type RailRef = {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import {
|
||||
ReplaySpeedMultiplier,
|
||||
defaultReplaySpeedMultiplier,
|
||||
} from "../../utilities/ReplaySpeedMultiplier";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { Layer } from "./Layer";
|
||||
import { ReplaySpeedChangeEvent } from "../../InputHandler";
|
||||
import { translateText } from "../../Utils";
|
||||
import {
|
||||
defaultReplaySpeedMultiplier,
|
||||
ReplaySpeedMultiplier,
|
||||
} from "../../utilities/ReplaySpeedMultiplier";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
export class ShowReplayPanelEvent {
|
||||
constructor(
|
||||
@@ -71,9 +71,11 @@ export class ReplayPanel extends LitElement implements Layer {
|
||||
@contextmenu=${(e: Event) => e.preventDefault()}
|
||||
>
|
||||
<label class="block mb-1 text-white" translate="no">
|
||||
${this.isSingleplayer
|
||||
? translateText("replay_panel.game_speed")
|
||||
: translateText("replay_panel.replay_speed")}
|
||||
${
|
||||
this.isSingleplayer
|
||||
? translateText("replay_panel.game_speed")
|
||||
: translateText("replay_panel.replay_speed")
|
||||
}
|
||||
</label>
|
||||
<div class="grid grid-cols-2 gap-1">
|
||||
${this.renderSpeedButton(ReplaySpeedMultiplier.slow, "×0.5")}
|
||||
@@ -92,9 +94,9 @@ export class ReplayPanel extends LitElement implements Layer {
|
||||
const isActive = this._replaySpeedMultiplier === value;
|
||||
return html`
|
||||
<button
|
||||
class="text-white font-bold py-0 rounded border transition ${isActive
|
||||
? "bg-blue-500 border-gray-400"
|
||||
: "border-gray-500"}"
|
||||
class="text-white font-bold py-0 rounded border transition ${
|
||||
isActive ? "bg-blue-500 border-gray-400" : "border-gray-500"
|
||||
}"
|
||||
@click=${() => this.onReplaySpeedChange(value)}
|
||||
>
|
||||
${label}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { AlternateViewEvent, RedrawGraphicsEvent } from "../../InputHandler";
|
||||
import { LitElement, html } from "lit";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators.js";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { Layer } from "./Layer";
|
||||
import { PauseGameEvent } from "../../Transport";
|
||||
import { UserSettings } from "../../../core/game/UserSettings";
|
||||
import structureIcon from "../../../../resources/images/CityIconWhite.svg";
|
||||
import darkModeIcon from "../../../../resources/images/DarkModeIconWhite.svg";
|
||||
import emojiIcon from "../../../../resources/images/EmojiIconWhite.svg";
|
||||
import exitIcon from "../../../../resources/images/ExitIconWhite.svg";
|
||||
@@ -12,9 +8,13 @@ import explosionIcon from "../../../../resources/images/ExplosionIconWhite.svg";
|
||||
import mouseIcon from "../../../../resources/images/MouseIconWhite.svg";
|
||||
import ninjaIcon from "../../../../resources/images/NinjaIconWhite.svg";
|
||||
import settingsIcon from "../../../../resources/images/SettingIconWhite.svg";
|
||||
import structureIcon from "../../../../resources/images/CityIconWhite.svg";
|
||||
import { translateText } from "../../Utils";
|
||||
import treeIcon from "../../../../resources/images/TreeIconWhite.svg";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { UserSettings } from "../../../core/game/UserSettings";
|
||||
import { AlternateViewEvent, RedrawGraphicsEvent } from "../../InputHandler";
|
||||
import { PauseGameEvent } from "../../Transport";
|
||||
import { translateText } from "../../Utils";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
export class ShowSettingsModalEvent {
|
||||
constructor(
|
||||
@@ -199,15 +199,19 @@ export class SettingsModal extends LitElement implements Layer {
|
||||
${translateText("user_setting.toggle_terrain")}
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${this.alternateView
|
||||
? translateText("user_setting.terrain_enabled")
|
||||
: translateText("user_setting.terrain_disabled")}
|
||||
${
|
||||
this.alternateView
|
||||
? translateText("user_setting.terrain_enabled")
|
||||
: translateText("user_setting.terrain_disabled")
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${this.alternateView
|
||||
? translateText("user_setting.on")
|
||||
: translateText("user_setting.off")}
|
||||
${
|
||||
this.alternateView
|
||||
? translateText("user_setting.on")
|
||||
: translateText("user_setting.off")
|
||||
}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -222,15 +226,19 @@ export class SettingsModal extends LitElement implements Layer {
|
||||
${translateText("user_setting.emojis_label")}
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${this.userSettings?.emojis()
|
||||
? translateText("user_setting.emojis_visible")
|
||||
: translateText("user_setting.emojis_hidden")}
|
||||
${
|
||||
this.userSettings?.emojis()
|
||||
? translateText("user_setting.emojis_visible")
|
||||
: translateText("user_setting.emojis_hidden")
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${this.userSettings?.emojis()
|
||||
? translateText("user_setting.on")
|
||||
: translateText("user_setting.off")}
|
||||
${
|
||||
this.userSettings?.emojis()
|
||||
? translateText("user_setting.on")
|
||||
: translateText("user_setting.off")
|
||||
}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -250,15 +258,19 @@ export class SettingsModal extends LitElement implements Layer {
|
||||
${translateText("user_setting.dark_mode_label")}
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${this.userSettings?.darkMode()
|
||||
? translateText("user_setting.dark_mode_enabled")
|
||||
: translateText("user_setting.light_mode_enabled")}
|
||||
${
|
||||
this.userSettings?.darkMode()
|
||||
? translateText("user_setting.dark_mode_enabled")
|
||||
: translateText("user_setting.light_mode_enabled")
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${this.userSettings?.darkMode()
|
||||
? translateText("user_setting.on")
|
||||
: translateText("user_setting.off")}
|
||||
${
|
||||
this.userSettings?.darkMode()
|
||||
? translateText("user_setting.on")
|
||||
: translateText("user_setting.off")
|
||||
}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -278,15 +290,19 @@ export class SettingsModal extends LitElement implements Layer {
|
||||
${translateText("user_setting.special_effects_label")}
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${this.userSettings?.fxLayer()
|
||||
? translateText("user_setting.special_effects_enabled")
|
||||
: translateText("user_setting.special_effects_disabled")}
|
||||
${
|
||||
this.userSettings?.fxLayer()
|
||||
? translateText("user_setting.special_effects_enabled")
|
||||
: translateText("user_setting.special_effects_disabled")
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${this.userSettings?.fxLayer()
|
||||
? translateText("user_setting.on")
|
||||
: translateText("user_setting.off")}
|
||||
${
|
||||
this.userSettings?.fxLayer()
|
||||
? translateText("user_setting.on")
|
||||
: translateText("user_setting.off")
|
||||
}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -306,15 +322,19 @@ export class SettingsModal extends LitElement implements Layer {
|
||||
${translateText("user_setting.structure_sprites_label")}
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${this.userSettings?.structureSprites()
|
||||
? translateText("user_setting.structure_sprites_enabled")
|
||||
: translateText("user_setting.structure_sprites_disabled")}
|
||||
${
|
||||
this.userSettings?.structureSprites()
|
||||
? translateText("user_setting.structure_sprites_enabled")
|
||||
: translateText("user_setting.structure_sprites_disabled")
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${this.userSettings?.structureSprites()
|
||||
? translateText("user_setting.on")
|
||||
: translateText("user_setting.off")}
|
||||
${
|
||||
this.userSettings?.structureSprites()
|
||||
? translateText("user_setting.on")
|
||||
: translateText("user_setting.off")
|
||||
}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -329,15 +349,19 @@ export class SettingsModal extends LitElement implements Layer {
|
||||
${translateText("user_setting.anonymous_names_label")}
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${this.userSettings?.anonymousNames()
|
||||
? translateText("user_setting.anonymous_names_enabled")
|
||||
: translateText("user_setting.real_names_shown")}
|
||||
${
|
||||
this.userSettings?.anonymousNames()
|
||||
? translateText("user_setting.anonymous_names_enabled")
|
||||
: translateText("user_setting.real_names_shown")
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${this.userSettings?.anonymousNames()
|
||||
? translateText("user_setting.on")
|
||||
: translateText("user_setting.off")}
|
||||
${
|
||||
this.userSettings?.anonymousNames()
|
||||
? translateText("user_setting.on")
|
||||
: translateText("user_setting.off")
|
||||
}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -352,15 +376,19 @@ export class SettingsModal extends LitElement implements Layer {
|
||||
${translateText("user_setting.left_click_menu")}
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${this.userSettings?.leftClickOpensMenu()
|
||||
? translateText("user_setting.left_click_opens_menu")
|
||||
: translateText("user_setting.right_click_opens_menu")}
|
||||
${
|
||||
this.userSettings?.leftClickOpensMenu()
|
||||
? translateText("user_setting.left_click_opens_menu")
|
||||
: translateText("user_setting.right_click_opens_menu")
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${this.userSettings?.leftClickOpensMenu()
|
||||
? translateText("user_setting.on")
|
||||
: translateText("user_setting.off")}
|
||||
${
|
||||
this.userSettings?.leftClickOpensMenu()
|
||||
? translateText("user_setting.on")
|
||||
: translateText("user_setting.off")
|
||||
}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -380,17 +408,23 @@ export class SettingsModal extends LitElement implements Layer {
|
||||
${translateText("user_setting.performance_overlay_label")}
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${this.userSettings?.performanceOverlay()
|
||||
? translateText("user_setting.performance_overlay_enabled")
|
||||
: translateText(
|
||||
"user_setting.performance_overlay_disabled",
|
||||
)}
|
||||
${
|
||||
this.userSettings?.performanceOverlay()
|
||||
? translateText(
|
||||
"user_setting.performance_overlay_enabled",
|
||||
)
|
||||
: translateText(
|
||||
"user_setting.performance_overlay_disabled",
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${this.userSettings?.performanceOverlay()
|
||||
? translateText("user_setting.on")
|
||||
: translateText("user_setting.off")}
|
||||
${
|
||||
this.userSettings?.performanceOverlay()
|
||||
? translateText("user_setting.on")
|
||||
: translateText("user_setting.off")
|
||||
}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { Layer } from "./Layer";
|
||||
import { getGamesPlayed } from "../../Utils";
|
||||
import { translateText } from "../../../client/Utils";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { getGamesPlayed } from "../../Utils";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
const AD_TYPE = "bottom_rail";
|
||||
const AD_CONTAINER_ID = "bottom-rail-ad-container";
|
||||
@@ -125,11 +125,13 @@ export class SpawnAd extends LitElement implements Layer {
|
||||
id="${AD_CONTAINER_ID}"
|
||||
class="w-full h-full flex items-center justify-center"
|
||||
>
|
||||
${!this.adLoaded
|
||||
? html`<span class="text-white text-sm"
|
||||
${
|
||||
!this.adLoaded
|
||||
? html`<span class="text-white text-sm"
|
||||
>${translateText("spawn_ad.loading")}</span
|
||||
>`
|
||||
: ""}
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { GameMode, Team } from "../../../core/game/Game";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { Layer } from "./Layer";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
export class SpawnTimer implements Layer {
|
||||
private ratios = [0];
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import * as PIXI from "pixi.js";
|
||||
import { Cell, PlayerID, UnitType } from "../../../core/game/Game";
|
||||
import { GameView, PlayerView, UnitView } from "../../../core/game/GameView";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||
import { Layer } from "./Layer";
|
||||
import { OutlineFilter } from "pixi-filters";
|
||||
import SAMMissileIcon from "../../../../resources/images/SamLauncherUnit.png";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { ToggleStructureEvent } from "../../InputHandler";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import anchorIcon from "../../../../resources/images/AnchorIcon.png";
|
||||
import bitmapFont from "../../../../resources/fonts/round_6x6_modified.xml";
|
||||
import anchorIcon from "../../../../resources/images/AnchorIcon.png";
|
||||
import cityIcon from "../../../../resources/images/CityIcon.png";
|
||||
import factoryIcon from "../../../../resources/images/FactoryUnit.png";
|
||||
import missileSiloIcon from "../../../../resources/images/MissileSiloUnit.png";
|
||||
import SAMMissileIcon from "../../../../resources/images/SamLauncherUnit.png";
|
||||
import shieldIcon from "../../../../resources/images/ShieldIcon.png";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { Cell, PlayerID, UnitType } from "../../../core/game/Game";
|
||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||
import { GameView, PlayerView, UnitView } from "../../../core/game/GameView";
|
||||
import { ToggleStructureEvent } from "../../InputHandler";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
type ShapeType = "triangle" | "square" | "pentagon" | "octagon" | "circle";
|
||||
|
||||
@@ -357,12 +357,12 @@ export class StructureIconsLayer implements Layer {
|
||||
const shape = STRUCTURE_SHAPES[structureType];
|
||||
const texture = shape
|
||||
? this.createIcon(
|
||||
unit.owner(),
|
||||
structureType,
|
||||
isConstruction,
|
||||
shape,
|
||||
renderIcon,
|
||||
)
|
||||
unit.owner(),
|
||||
structureType,
|
||||
isConstruction,
|
||||
shape,
|
||||
renderIcon,
|
||||
)
|
||||
: PIXI.Texture.EMPTY;
|
||||
|
||||
this.textureCache.set(cacheKey, texture);
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { Cell, UnitType } from "../../../core/game/Game";
|
||||
import { Colord, colord } from "colord";
|
||||
import { GameView, UnitView } from "../../../core/game/GameView";
|
||||
import { euclDistFN, isometricDistFN } from "../../../core/game/GameMap";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||
import { Layer } from "./Layer";
|
||||
import SAMMissileIcon from "../../../../resources/non-commercial/images/buildings/silo4.png";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import anchorIcon from "../../../../resources/non-commercial/images/buildings/port1.png";
|
||||
import cityIcon from "../../../../resources/non-commercial/images/buildings/cityAlt1.png";
|
||||
import factoryIcon from "../../../../resources/non-commercial/images/buildings/factoryAlt1.png";
|
||||
import missileSiloIcon from "../../../../resources/non-commercial/images/buildings/silo1.png";
|
||||
import shieldIcon from "../../../../resources/non-commercial/images/buildings/fortAlt3.png";
|
||||
import anchorIcon from "../../../../resources/non-commercial/images/buildings/port1.png";
|
||||
import missileSiloIcon from "../../../../resources/non-commercial/images/buildings/silo1.png";
|
||||
import SAMMissileIcon from "../../../../resources/non-commercial/images/buildings/silo4.png";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { Cell, UnitType } from "../../../core/game/Game";
|
||||
import { euclDistFN, isometricDistFN } from "../../../core/game/GameMap";
|
||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||
import { GameView, UnitView } from "../../../core/game/GameView";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
const underConstructionColor = colord({ r: 150, g: 150, b: 150 });
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { GameMode, Team, UnitType } from "../../../core/game/Game";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { renderNumber, translateText } from "../../Utils";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
type TeamEntry = {
|
||||
@@ -130,8 +130,9 @@ export class TeamStats extends LitElement implements Layer {
|
||||
<div class="py-1.5 md:py-2.5 text-center border-b border-slate-500">
|
||||
${translateText("leaderboard.team")}
|
||||
</div>
|
||||
${this.showUnits
|
||||
? html`
|
||||
${
|
||||
this.showUnits
|
||||
? html`
|
||||
<div class="py-1.5 text-center border-b border-slate-500">
|
||||
${translateText("leaderboard.launchers")}
|
||||
</div>
|
||||
@@ -145,7 +146,7 @@ export class TeamStats extends LitElement implements Layer {
|
||||
${translateText("leaderboard.cities")}
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
: html`
|
||||
<div class="py-1.5 text-center border-b border-slate-500">
|
||||
${translateText("leaderboard.owned")}
|
||||
</div>
|
||||
@@ -155,7 +156,8 @@ export class TeamStats extends LitElement implements Layer {
|
||||
<div class="py-1.5 text-center border-b border-slate-500">
|
||||
${translateText("leaderboard.troops")}
|
||||
</div>
|
||||
`}
|
||||
`
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Data rows -->
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { Layer } from "./Layer";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
export class TerrainLayer implements Layer {
|
||||
private canvas: HTMLCanvasElement | undefined;
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import { PriorityQueue } from "@datastructures-js/priority-queue";
|
||||
import { Colord } from "colord";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { Cell, PlayerType, UnitType } from "../../../core/game/Game";
|
||||
import { euclDistFN, TileRef } from "../../../core/game/GameMap";
|
||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { UserSettings } from "../../../core/game/UserSettings";
|
||||
import { PseudoRandom } from "../../../core/PseudoRandom";
|
||||
import {
|
||||
AlternateViewEvent,
|
||||
DragEvent,
|
||||
MouseOverEvent,
|
||||
RedrawGraphicsEvent,
|
||||
} from "../../InputHandler";
|
||||
import { Cell, PlayerType, UnitType } from "../../../core/game/Game";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { TileRef, euclDistFN } from "../../../core/game/GameMap";
|
||||
import { Colord } from "colord";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||
import { Layer } from "./Layer";
|
||||
import { PriorityQueue } from "@datastructures-js/priority-queue";
|
||||
import { PseudoRandom } from "../../../core/PseudoRandom";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { UserSettings } from "../../../core/game/UserSettings";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
export class TerritoryLayer implements Layer {
|
||||
private readonly userSettings: UserSettings;
|
||||
@@ -323,7 +323,8 @@ export class TerritoryLayer implements Layer {
|
||||
initImageData() {
|
||||
this.game.forEachTile((tile) => {
|
||||
if (this.imageData === undefined) throw new Error("Not initialized");
|
||||
if (this.alternativeImageData === undefined) throw new Error("Not initialized");
|
||||
if (this.alternativeImageData === undefined)
|
||||
throw new Error("Not initialized");
|
||||
const cell = new Cell(this.game.x(tile), this.game.y(tile));
|
||||
const index = cell.y * this.game.width() + cell.x;
|
||||
const offset = index * 4;
|
||||
@@ -337,7 +338,8 @@ export class TerritoryLayer implements Layer {
|
||||
if (this.highlightCanvas === undefined) throw new Error("Not initialized");
|
||||
if (this.context === undefined) throw new Error("Not initialized");
|
||||
if (this.imageData === undefined) throw new Error("Not initialized");
|
||||
if (this.alternativeImageData === undefined) throw new Error("Not initialized");
|
||||
if (this.alternativeImageData === undefined)
|
||||
throw new Error("Not initialized");
|
||||
const now = Date.now();
|
||||
if (
|
||||
now > this.lastDragTime + this.nodrawDragDuration &&
|
||||
@@ -413,7 +415,8 @@ export class TerritoryLayer implements Layer {
|
||||
return;
|
||||
}
|
||||
if (this.imageData === undefined) throw new Error("Not initialized");
|
||||
if (this.alternativeImageData === undefined) throw new Error("Not initialized");
|
||||
if (this.alternativeImageData === undefined)
|
||||
throw new Error("Not initialized");
|
||||
|
||||
if (!this.game.hasOwner(tile)) {
|
||||
if (this.game.hasFallout(tile)) {
|
||||
@@ -509,7 +512,8 @@ export class TerritoryLayer implements Layer {
|
||||
}
|
||||
|
||||
paintAlternateViewTile(tile: TileRef, other: PlayerView) {
|
||||
if (this.alternativeImageData === undefined) throw new Error("Not initialized");
|
||||
if (this.alternativeImageData === undefined)
|
||||
throw new Error("Not initialized");
|
||||
const color = this.alternateViewColor(other);
|
||||
this.paintTile(this.alternativeImageData, tile, color, 255);
|
||||
}
|
||||
@@ -525,14 +529,16 @@ export class TerritoryLayer implements Layer {
|
||||
clearTile(tile: TileRef) {
|
||||
const offset = tile * 4;
|
||||
if (this.imageData === undefined) throw new Error("Not initialized");
|
||||
if (this.alternativeImageData === undefined) throw new Error("Not initialized");
|
||||
if (this.alternativeImageData === undefined)
|
||||
throw new Error("Not initialized");
|
||||
this.imageData.data[offset + 3] = 0; // Set alpha to 0 (fully transparent)
|
||||
this.alternativeImageData.data[offset + 3] = 0; // Set alpha to 0 (fully transparent)
|
||||
}
|
||||
|
||||
clearAlternativeTile(tile: TileRef) {
|
||||
const offset = tile * 4;
|
||||
if (this.alternativeImageData === undefined) throw new Error("Not initialized");
|
||||
if (this.alternativeImageData === undefined)
|
||||
throw new Error("Not initialized");
|
||||
this.alternativeImageData.data[offset + 3] = 0; // Set alpha to 0 (fully transparent)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { GameView, UnitView } from "../../../core/game/GameView";
|
||||
import { Colord } from "colord";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||
import { Layer } from "./Layer";
|
||||
import { ProgressBar } from "../ProgressBar";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { UnitSelectionEvent } from "../../InputHandler";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { UnitType } from "../../../core/game/Game";
|
||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||
import { GameView, UnitView } from "../../../core/game/GameView";
|
||||
import { UserSettings } from "../../../core/game/UserSettings";
|
||||
import { UnitSelectionEvent } from "../../InputHandler";
|
||||
import { ProgressBar } from "../ProgressBar";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
const COLOR_PROGRESSION = [
|
||||
"rgb(232, 25, 25)",
|
||||
@@ -69,8 +69,7 @@ export class UILayer implements Layer {
|
||||
|
||||
this.game
|
||||
.updatesSinceLastTick()
|
||||
?.[GameUpdateType.Unit]
|
||||
?.map((unit) => this.game.unit(unit.id))
|
||||
?.[GameUpdateType.Unit]?.map((unit) => this.game.unit(unit.id))
|
||||
?.forEach((unitView) => {
|
||||
if (unitView === undefined) return;
|
||||
this.onUnitEvent(unitView);
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { Layer } from "./Layer";
|
||||
import { ToggleStructureEvent } from "../../InputHandler";
|
||||
import { UnitType } from "../../../core/game/Game";
|
||||
import cityIcon from "../../../../resources/images/CityIconWhite.svg";
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
import defensePostIcon from "../../../../resources/images/ShieldIconWhite.svg";
|
||||
import portIcon from "../../../../resources/images/AnchorIcon.png";
|
||||
import cityIcon from "../../../../resources/images/CityIconWhite.svg";
|
||||
import factoryIcon from "../../../../resources/images/FactoryIconWhite.svg";
|
||||
import missileSiloIcon from "../../../../resources/images/MissileSiloUnit.png";
|
||||
import portIcon from "../../../../resources/images/AnchorIcon.png";
|
||||
import { renderNumber } from "../../Utils";
|
||||
import defensePostIcon from "../../../../resources/images/ShieldIconWhite.svg";
|
||||
import samLauncherIcon from "../../../../resources/non-commercial/svg/SamLauncherIconWhite.svg";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { UnitType } from "../../../core/game/Game";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { ToggleStructureEvent } from "../../InputHandler";
|
||||
import { renderNumber } from "../../Utils";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
@customElement("unit-display")
|
||||
export class UnitDisplay extends LitElement implements Layer {
|
||||
@@ -69,9 +69,9 @@ export class UnitDisplay extends LitElement implements Layer {
|
||||
return html`
|
||||
<div
|
||||
class="px-2 flex items-center gap-2 cursor-pointer hover:bg-slate-700/50 rounded text-white"
|
||||
style="background: ${this._selectedStructure === unitType
|
||||
? "#ffffff2e"
|
||||
: "none"}"
|
||||
style="background: ${
|
||||
this._selectedStructure === unitType ? "#ffffff2e" : "none"
|
||||
}"
|
||||
@mouseenter="${() =>
|
||||
this.eventBus?.emit(new ToggleStructureEvent(unitType))}"
|
||||
@mouseleave="${() =>
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import { Colord, colord } from "colord";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { UnitType } from "../../../core/game/Game";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||
import { GameView, UnitView } from "../../../core/game/GameView";
|
||||
import { BezenhamLine } from "../../../core/utilities/Line";
|
||||
import {
|
||||
AlternateViewEvent,
|
||||
MouseUpEvent,
|
||||
UnitSelectionEvent,
|
||||
} from "../../InputHandler";
|
||||
import { Colord, colord } from "colord";
|
||||
import { GameView, UnitView } from "../../../core/game/GameView";
|
||||
import { MoveWarshipIntentEvent } from "../../Transport";
|
||||
import {
|
||||
getColoredSprite,
|
||||
isSpriteReady,
|
||||
loadAllSprites,
|
||||
} from "../SpriteLoader";
|
||||
import { BezenhamLine } from "../../../core/utilities/Line";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||
import { Layer } from "./Layer";
|
||||
import { MoveWarshipIntentEvent } from "../../Transport";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { TileRef } from "../../../core/game/GameMap";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { UnitType } from "../../../core/game/Game";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
enum Relationship {
|
||||
Self,
|
||||
@@ -156,7 +156,8 @@ export class UnitLayer implements Layer {
|
||||
}
|
||||
|
||||
renderLayer(context: CanvasRenderingContext2D) {
|
||||
if (this.transportShipTrailCanvas === undefined) throw new Error("Not initialized");
|
||||
if (this.transportShipTrailCanvas === undefined)
|
||||
throw new Error("Not initialized");
|
||||
if (this.canvas === undefined) throw new Error("Not initialized");
|
||||
context.drawImage(
|
||||
this.transportShipTrailCanvas,
|
||||
@@ -197,7 +198,8 @@ export class UnitLayer implements Layer {
|
||||
this.updateUnitsSprites(this.game.units().map((unit) => unit.id()));
|
||||
|
||||
this.unitToTrail.forEach((trail, unit) => {
|
||||
if (this.unitTrailContext === undefined) throw new Error("Not initialized");
|
||||
if (this.unitTrailContext === undefined)
|
||||
throw new Error("Not initialized");
|
||||
for (const t of trail) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
@@ -309,7 +311,11 @@ export class UnitLayer implements Layer {
|
||||
const rel = this.relationship(unit);
|
||||
|
||||
// Clear current and previous positions
|
||||
this.clearCell(this.game.x(unit.lastTile()), this.game.y(unit.lastTile()), this.context);
|
||||
this.clearCell(
|
||||
this.game.x(unit.lastTile()),
|
||||
this.game.y(unit.lastTile()),
|
||||
this.context,
|
||||
);
|
||||
const oldTile = this.oldShellTile.get(unit);
|
||||
if (oldTile !== undefined) {
|
||||
this.clearCell(this.game.x(oldTile), this.game.y(oldTile), this.context);
|
||||
@@ -432,7 +438,11 @@ export class UnitLayer implements Layer {
|
||||
const rel = this.relationship(unit);
|
||||
|
||||
if (this.context === undefined) throw new Error("Not initialized");
|
||||
this.clearCell(this.game.x(unit.lastTile()), this.game.y(unit.lastTile()), this.context);
|
||||
this.clearCell(
|
||||
this.game.x(unit.lastTile()),
|
||||
this.game.y(unit.lastTile()),
|
||||
this.context,
|
||||
);
|
||||
|
||||
if (unit.isActive()) {
|
||||
// Paint area
|
||||
@@ -504,11 +514,7 @@ export class UnitLayer implements Layer {
|
||||
context.fillRect(x, y, 1, 1);
|
||||
}
|
||||
|
||||
clearCell(
|
||||
x: number,
|
||||
y: number,
|
||||
context: CanvasRenderingContext2D,
|
||||
) {
|
||||
clearCell(x: number, y: number, context: CanvasRenderingContext2D) {
|
||||
context.clearRect(x, y, 1, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { translateText } from "../../../client/Utils";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { GameUpdateType } from "../../../core/game/GameUpdates";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { SendWinnerEvent } from "../../Transport";
|
||||
import { GutterAdModalEvent } from "./GutterAdModal";
|
||||
import { Layer } from "./Layer";
|
||||
import { SendWinnerEvent } from "../../Transport";
|
||||
import { translateText } from "../../../client/Utils";
|
||||
|
||||
@customElement("win-modal")
|
||||
export class WinModal extends LitElement implements Layer {
|
||||
|
||||
+2
-2
@@ -1,3 +1,5 @@
|
||||
import { decodeJwt } from "jose";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
RefreshResponseSchema,
|
||||
TokenPayload,
|
||||
@@ -5,9 +7,7 @@ import {
|
||||
UserMeResponse,
|
||||
UserMeResponseSchema,
|
||||
} from "../core/ApiSchemas";
|
||||
import { decodeJwt } from "jose";
|
||||
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
|
||||
import { z } from "zod";
|
||||
|
||||
function getAudience() {
|
||||
const { hostname } = new URL(window.location.href);
|
||||
|
||||
+35
-26
@@ -134,7 +134,7 @@
|
||||
|
||||
.option-image {
|
||||
width: 100%;
|
||||
aspect-ratio: 4/2;
|
||||
aspect-ratio: 4 / 2;
|
||||
color: #aaa;
|
||||
transition: transform 0.2s ease-in-out;
|
||||
border-radius: 8px;
|
||||
@@ -373,17 +373,18 @@ label.option-card:hover {
|
||||
}
|
||||
|
||||
#helpModal .city-icon {
|
||||
mask: url("../../resources/images/CityIconWhite.svg") no-repeat center / cover;
|
||||
mask:
|
||||
url("../../resources/images/CityIconWhite.svg") no-repeat center / cover;
|
||||
}
|
||||
|
||||
#helpModal .factory-icon {
|
||||
mask: url("../../resources/images/FactoryIconWhite.svg") no-repeat center /
|
||||
cover;
|
||||
mask:
|
||||
url("../../resources/images/FactoryIconWhite.svg") no-repeat center / cover;
|
||||
}
|
||||
|
||||
#helpModal .defense-post-icon {
|
||||
mask: url("../../resources/images/ShieldIconWhite.svg") no-repeat center /
|
||||
cover;
|
||||
mask:
|
||||
url("../../resources/images/ShieldIconWhite.svg") no-repeat center / cover;
|
||||
}
|
||||
|
||||
#helpModal .port-icon {
|
||||
@@ -391,27 +392,32 @@ label.option-card:hover {
|
||||
}
|
||||
|
||||
#helpModal .warship-icon {
|
||||
mask: url("../../resources/images/BattleshipIconWhite.svg") no-repeat center /
|
||||
mask:
|
||||
url("../../resources/images/BattleshipIconWhite.svg") no-repeat center /
|
||||
cover;
|
||||
}
|
||||
|
||||
#helpModal .missile-silo-icon {
|
||||
mask: url("../../resources/non-commercial/svg/MissileSiloIconWhite.svg")
|
||||
no-repeat center / cover;
|
||||
mask:
|
||||
url("../../resources/non-commercial/svg/MissileSiloIconWhite.svg") no-repeat
|
||||
center / cover;
|
||||
}
|
||||
|
||||
#helpModal .sam-launcher-icon {
|
||||
mask: url("../../resources/non-commercial/svg/SamLauncherIconWhite.svg")
|
||||
no-repeat center / cover;
|
||||
mask:
|
||||
url("../../resources/non-commercial/svg/SamLauncherIconWhite.svg") no-repeat
|
||||
center / cover;
|
||||
}
|
||||
|
||||
#helpModal .atom-bomb-icon {
|
||||
mask: url("../../resources/images/NukeIconWhite.svg") no-repeat center / cover;
|
||||
mask:
|
||||
url("../../resources/images/NukeIconWhite.svg") no-repeat center / cover;
|
||||
}
|
||||
|
||||
#helpModal .hydrogen-bomb-icon {
|
||||
mask: url("../../resources/images/MushroomCloudIconWhite.svg") no-repeat
|
||||
center / cover;
|
||||
mask:
|
||||
url("../../resources/images/MushroomCloudIconWhite.svg") no-repeat center /
|
||||
cover;
|
||||
}
|
||||
|
||||
#helpModal .mirv-icon {
|
||||
@@ -419,7 +425,8 @@ label.option-card:hover {
|
||||
}
|
||||
|
||||
#helpModal .chat-icon {
|
||||
mask: url("../../resources/images/ChatIconWhite.svg") no-repeat center / cover;
|
||||
mask:
|
||||
url("../../resources/images/ChatIconWhite.svg") no-repeat center / cover;
|
||||
}
|
||||
|
||||
#helpModal .target-icon {
|
||||
@@ -427,33 +434,35 @@ label.option-card:hover {
|
||||
}
|
||||
|
||||
#helpModal .alliance-icon {
|
||||
mask: url("../../resources/images/AllianceIconWhite.svg") no-repeat center /
|
||||
cover;
|
||||
mask:
|
||||
url("../../resources/images/AllianceIconWhite.svg") no-repeat center / cover;
|
||||
}
|
||||
|
||||
#helpModal .emoji-icon {
|
||||
mask: url("../../resources/images/EmojiIconWhite.svg") no-repeat center /
|
||||
cover;
|
||||
mask:
|
||||
url("../../resources/images/EmojiIconWhite.svg") no-repeat center / cover;
|
||||
}
|
||||
|
||||
#helpModal .betray-icon {
|
||||
mask: url("../../resources/images/TraitorIconWhite.svg") no-repeat center /
|
||||
cover;
|
||||
mask:
|
||||
url("../../resources/images/TraitorIconWhite.svg") no-repeat center / cover;
|
||||
}
|
||||
|
||||
#helpModal .donate-icon {
|
||||
mask: url("../../resources/images/DonateTroopIconWhite.svg") no-repeat
|
||||
center / cover;
|
||||
mask:
|
||||
url("../../resources/images/DonateTroopIconWhite.svg") no-repeat center /
|
||||
cover;
|
||||
}
|
||||
|
||||
#helpModal .donate-gold-icon {
|
||||
mask: url("../../resources/images/DonateGoldIconWhite.svg") no-repeat center /
|
||||
mask:
|
||||
url("../../resources/images/DonateGoldIconWhite.svg") no-repeat center /
|
||||
cover;
|
||||
}
|
||||
|
||||
#helpModal .build-icon {
|
||||
mask: url("../../resources/images/BuildIconWhite.svg") no-repeat center /
|
||||
cover;
|
||||
mask:
|
||||
url("../../resources/images/BuildIconWhite.svg") no-repeat center / cover;
|
||||
}
|
||||
|
||||
#helpModal .info-icon {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// renderUnitTypeOptions.ts
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { UnitType } from "../../core/game/Game";
|
||||
import { translateText } from "../Utils";
|
||||
|
||||
@@ -36,7 +36,7 @@ export function renderUnitTypeOptions({
|
||||
type="checkbox"
|
||||
.checked=${disabledUnits.includes(type)}
|
||||
@change=${(e: Event) => {
|
||||
const { checked } = (e.target as HTMLInputElement);
|
||||
const { checked } = e.target as HTMLInputElement;
|
||||
toggleUnit(type, checked);
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// This file contains schemas for api.openfront.io
|
||||
import { base64urlToUuid } from "./Base64";
|
||||
|
||||
import { z } from "zod";
|
||||
import { base64urlToUuid } from "./Base64";
|
||||
|
||||
export const RefreshResponseSchema = z.object({
|
||||
token: z.string(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { RequiredPatternSchema } from "./Schemas";
|
||||
import { z } from "zod";
|
||||
import { RequiredPatternSchema } from "./Schemas";
|
||||
|
||||
export const ProductSchema = z.object({
|
||||
productId: z.string(),
|
||||
|
||||
@@ -4,11 +4,11 @@ const ANIMATION_DURATIONS: Record<string, number> = {
|
||||
"bright-rainbow": 4000,
|
||||
"copper-glow": 3000,
|
||||
"gold-glow": 3000,
|
||||
"lava": 6000,
|
||||
"neon": 3000,
|
||||
"rainbow": 4000,
|
||||
lava: 6000,
|
||||
neon: 3000,
|
||||
rainbow: 4000,
|
||||
"silver-glow": 3000,
|
||||
"water": 6200,
|
||||
water: 6200,
|
||||
};
|
||||
|
||||
// TODO: Pass in cosmetics as a parameter when
|
||||
|
||||
@@ -6,8 +6,10 @@ export type EventConstructor<T extends GameEvent = GameEvent> = new (
|
||||
) => T;
|
||||
|
||||
export class EventBus {
|
||||
private readonly listeners: Map<EventConstructor, Array<(event: GameEvent) => void>> =
|
||||
new Map();
|
||||
private readonly listeners: Map<
|
||||
EventConstructor,
|
||||
Array<(event: GameEvent) => void>
|
||||
> = new Map();
|
||||
|
||||
emit<T extends GameEvent>(event: T): void {
|
||||
const eventConstructor = event.constructor as EventConstructor<T>;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// This file contians schemas for the primary openfront express server
|
||||
import { GameInfoSchema } from "./Schemas";
|
||||
|
||||
import { z } from "zod";
|
||||
import { GameInfoSchema } from "./Schemas";
|
||||
|
||||
export const ApiEnvResponseSchema = z.object({
|
||||
game_env: z.string(),
|
||||
|
||||
+18
-18
@@ -1,3 +1,7 @@
|
||||
import { placeName } from "../client/graphics/NameBoxCalculator";
|
||||
import { getConfig } from "./configuration/ConfigLoader";
|
||||
import { Executor } from "./execution/ExecutionManager";
|
||||
import { WinCheckExecution } from "./execution/WinCheckExecution";
|
||||
import {
|
||||
AllPlayers,
|
||||
Attack,
|
||||
@@ -14,23 +18,19 @@ import {
|
||||
PlayerProfile,
|
||||
PlayerType,
|
||||
} from "./game/Game";
|
||||
import { ClientID, GameStartInfo, Turn } from "./Schemas";
|
||||
import { createGame } from "./game/GameImpl";
|
||||
import { TileRef } from "./game/GameMap";
|
||||
import { GameMapLoader } from "./game/GameMapLoader";
|
||||
import {
|
||||
ErrorUpdate,
|
||||
GameUpdateType,
|
||||
GameUpdateViewData,
|
||||
} from "./game/GameUpdates";
|
||||
import { sanitize, simpleHash } from "./Util";
|
||||
import { Executor } from "./execution/ExecutionManager";
|
||||
import { GameMapLoader } from "./game/GameMapLoader";
|
||||
import { PseudoRandom } from "./PseudoRandom";
|
||||
import { TileRef } from "./game/GameMap";
|
||||
import { WinCheckExecution } from "./execution/WinCheckExecution";
|
||||
import { createGame } from "./game/GameImpl";
|
||||
import { fixProfaneUsername } from "./validations/username";
|
||||
import { getConfig } from "./configuration/ConfigLoader";
|
||||
import { loadTerrainMap } from "./game/TerrainMapLoader";
|
||||
import { placeName } from "../client/graphics/NameBoxCalculator";
|
||||
import { PseudoRandom } from "./PseudoRandom";
|
||||
import { ClientID, GameStartInfo, Turn } from "./Schemas";
|
||||
import { sanitize, simpleHash } from "./Util";
|
||||
import { fixProfaneUsername } from "./validations/username";
|
||||
|
||||
export async function createGameRunner(
|
||||
gameStart: GameStartInfo,
|
||||
@@ -57,13 +57,13 @@ export async function createGameRunner(
|
||||
const nations = gameStart.config.disableNPCs
|
||||
? []
|
||||
: gameMap.manifest.nations.map(
|
||||
(n) =>
|
||||
new Nation(
|
||||
new Cell(n.coordinates[0], n.coordinates[1]),
|
||||
n.strength,
|
||||
new PlayerInfo(n.name, PlayerType.FakeHuman, null, random.nextID()),
|
||||
),
|
||||
);
|
||||
(n) =>
|
||||
new Nation(
|
||||
new Cell(n.coordinates[0], n.coordinates[1]),
|
||||
n.strength,
|
||||
new PlayerInfo(n.name, PlayerType.FakeHuman, null, random.nextID()),
|
||||
),
|
||||
);
|
||||
|
||||
const game: Game = createGame(
|
||||
humans,
|
||||
|
||||
+7
-5
@@ -1,3 +1,10 @@
|
||||
import { base64url } from "jose";
|
||||
import { z } from "zod";
|
||||
import quickChatData from "../../resources/QuickChat.json" with {
|
||||
type: "json",
|
||||
};
|
||||
import countries from "../client/data/countries.json" with { type: "json" };
|
||||
import { ID } from "./BaseSchemas";
|
||||
import {
|
||||
AllPlayers,
|
||||
Difficulty,
|
||||
@@ -10,14 +17,9 @@ import {
|
||||
Trios,
|
||||
UnitType,
|
||||
} from "./game/Game";
|
||||
import { ID } from "./BaseSchemas";
|
||||
import { PatternDecoder } from "./PatternDecoder";
|
||||
import { PlayerStatsSchema } from "./StatsSchemas";
|
||||
import { base64url } from "jose";
|
||||
import countries from "../client/data/countries.json" with { type: "json" };
|
||||
import { flattenedEmojiTable } from "./Util";
|
||||
import quickChatData from "../../resources/QuickChat.json" with { type: "json" };
|
||||
import { z } from "zod";
|
||||
|
||||
export type GameID = string;
|
||||
export type ClientID = string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { UnitType } from "./game/Game";
|
||||
import { z } from "zod";
|
||||
import { UnitType } from "./game/Game";
|
||||
|
||||
export const BombUnitSchema = z.union([
|
||||
z.literal("abomb"),
|
||||
|
||||
+11
-6
@@ -1,8 +1,13 @@
|
||||
import DOMPurify from "dompurify";
|
||||
import { customAlphabet } from "nanoid";
|
||||
import { ID } from "./BaseSchemas";
|
||||
import { ServerConfig } from "./configuration/Config";
|
||||
import {
|
||||
BOT_NAME_PREFIXES,
|
||||
BOT_NAME_SUFFIXES,
|
||||
} from "./execution/utils/BotNames";
|
||||
import { Cell, Unit } from "./game/Game";
|
||||
import { GameMap, TileRef } from "./game/GameMap";
|
||||
import {
|
||||
ClientID,
|
||||
GameConfig,
|
||||
@@ -12,11 +17,6 @@ import {
|
||||
Turn,
|
||||
Winner,
|
||||
} from "./Schemas";
|
||||
import { GameMap, TileRef } from "./game/GameMap";
|
||||
import DOMPurify from "dompurify";
|
||||
import { ID } from "./BaseSchemas";
|
||||
import { ServerConfig } from "./configuration/Config";
|
||||
import { customAlphabet } from "nanoid";
|
||||
|
||||
export function manhattanDistWrapped(
|
||||
c1: Cell,
|
||||
@@ -233,7 +233,12 @@ export function getClientID(gameID: GameID): ClientID {
|
||||
const cachedGame = localStorage.getItem("game_id");
|
||||
const cachedClient = localStorage.getItem("client_id");
|
||||
|
||||
if (gameID === cachedGame && cachedClient && ID.safeParse(cachedClient).success) return cachedClient;
|
||||
if (
|
||||
gameID === cachedGame &&
|
||||
cachedClient &&
|
||||
ID.safeParse(cachedClient).success
|
||||
)
|
||||
return cachedClient;
|
||||
|
||||
const clientId = generateID();
|
||||
localStorage.setItem("game_id", gameID);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// This file contians schemas for the openfront worker express server
|
||||
import { GameConfigSchema, GameRecordSchema } from "./Schemas";
|
||||
|
||||
import { z } from "zod";
|
||||
import { GameConfigSchema, GameRecordSchema } from "./Schemas";
|
||||
|
||||
export const CreateGameInputSchema = GameConfigSchema.or(
|
||||
z
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { Colord, extend } from "colord";
|
||||
import labPlugin from "colord/plugins/lab";
|
||||
import lchPlugin from "colord/plugins/lch";
|
||||
import Color from "colorjs.io";
|
||||
import { ColoredTeams, Team } from "../game/Game";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { simpleHash } from "../Util";
|
||||
import {
|
||||
blueTeamColors,
|
||||
botTeamColors,
|
||||
@@ -10,11 +15,7 @@ import {
|
||||
tealTeamColors,
|
||||
yellowTeamColors,
|
||||
} from "./Colors";
|
||||
import Color from "colorjs.io";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import labPlugin from "colord/plugins/lab";
|
||||
import lchPlugin from "colord/plugins/lch";
|
||||
import { simpleHash } from "../Util";
|
||||
|
||||
extend([lchPlugin]);
|
||||
extend([labPlugin]);
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Colord } from "colord";
|
||||
import { JWK } from "jose";
|
||||
import {
|
||||
Difficulty,
|
||||
Game,
|
||||
@@ -12,13 +14,11 @@ import {
|
||||
UnitInfo,
|
||||
UnitType,
|
||||
} from "../game/Game";
|
||||
import { GameConfig, GameID, TeamCountConfig } from "../Schemas";
|
||||
import { GameMap, TileRef } from "../game/GameMap";
|
||||
import { Colord } from "colord";
|
||||
import { JWK } from "jose";
|
||||
import { NukeType } from "../StatsSchemas";
|
||||
import { PlayerView } from "../game/GameView";
|
||||
import { UserSettings } from "../game/UserSettings";
|
||||
import { GameConfig, GameID, TeamCountConfig } from "../Schemas";
|
||||
import { NukeType } from "../StatsSchemas";
|
||||
|
||||
export enum GameEnv {
|
||||
Dev,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Config, GameEnv, ServerConfig } from "./Config";
|
||||
import { DevConfig, DevServerConfig } from "./DevConfig";
|
||||
import { ApiEnvResponseSchema } from "../ExpressSchemas";
|
||||
import { DefaultConfig } from "./DefaultConfig";
|
||||
import { GameConfig } from "../Schemas";
|
||||
import { UserSettings } from "../game/UserSettings";
|
||||
import { GameConfig } from "../Schemas";
|
||||
import { Config, GameEnv, ServerConfig } from "./Config";
|
||||
import { DefaultConfig } from "./DefaultConfig";
|
||||
import { DevConfig, DevServerConfig } from "./DevConfig";
|
||||
import { preprodConfig } from "./PreprodConfig";
|
||||
import { prodConfig } from "./ProdConfig";
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/* eslint-disable max-lines */
|
||||
import { Config, GameEnv, NukeMagnitude, ServerConfig, Theme } from "./Config";
|
||||
|
||||
import { JWK } from "jose";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
Difficulty,
|
||||
Duos,
|
||||
@@ -12,23 +14,22 @@ import {
|
||||
PlayerInfo,
|
||||
PlayerType,
|
||||
Quads,
|
||||
TerraNullius,
|
||||
TerrainType,
|
||||
TerraNullius,
|
||||
Tick,
|
||||
Trios,
|
||||
UnitInfo,
|
||||
UnitType,
|
||||
} from "../game/Game";
|
||||
import { TileRef } from "../game/GameMap";
|
||||
import { PlayerView } from "../game/GameView";
|
||||
import { UserSettings } from "../game/UserSettings";
|
||||
import { GameConfig, GameID, TeamCountConfig } from "../Schemas";
|
||||
import { assertNever, simpleHash, within } from "../Util";
|
||||
import { JWK } from "jose";
|
||||
import { NukeType } from "../StatsSchemas";
|
||||
import { assertNever, simpleHash, within } from "../Util";
|
||||
import { Config, GameEnv, NukeMagnitude, ServerConfig, Theme } from "./Config";
|
||||
import { PastelTheme } from "./PastelTheme";
|
||||
import { PastelThemeDark } from "./PastelThemeDark";
|
||||
import { PlayerView } from "../game/GameView";
|
||||
import { TileRef } from "../game/GameMap";
|
||||
import { UserSettings } from "../game/UserSettings";
|
||||
import { z } from "zod";
|
||||
|
||||
const JwksSchema = z.object({
|
||||
keys: z
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { DefaultConfig, DefaultServerConfig } from "./DefaultConfig";
|
||||
import { GameEnv, ServerConfig } from "./Config";
|
||||
import { UnitInfo, UnitType } from "../game/Game";
|
||||
import { GameConfig } from "../Schemas";
|
||||
import { UserSettings } from "../game/UserSettings";
|
||||
import { GameConfig } from "../Schemas";
|
||||
import { GameEnv, ServerConfig } from "./Config";
|
||||
import { DefaultConfig, DefaultServerConfig } from "./DefaultConfig";
|
||||
|
||||
export class DevServerConfig extends DefaultServerConfig {
|
||||
adminToken(): string {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user