From d1da506201f0a8b0ea8198f827f2370a9671bfa2 Mon Sep 17 00:00:00 2001 From: Evan Date: Mon, 7 Apr 2025 18:31:42 -0700 Subject: [PATCH 01/22] update win modal to link to patreon, create game mode rotation --- src/client/graphics/layers/WinModal.ts | 49 ++++++++++++-------------- src/client/index.html | 2 +- src/server/MapPlaylist.ts | 12 ++++++- src/server/Master.ts | 15 ++++++-- 4 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/client/graphics/layers/WinModal.ts b/src/client/graphics/layers/WinModal.ts index c2bfd2528..5fdf03478 100644 --- a/src/client/graphics/layers/WinModal.ts +++ b/src/client/graphics/layers/WinModal.ts @@ -149,7 +149,7 @@ export class WinModal extends LitElement implements Layer { return html`

${this._title || ""}

- ${this.supportHTML()} + ${this.innerHtml()}
@@ -158,34 +158,31 @@ export class WinModal extends LitElement implements Layer { `; } - updated(changedProperties) { - super.updated(changedProperties); - // Initialize ads if modal is visible and showing ads - if (changedProperties.has("isVisible") && this.isVisible && !this.won) { - try { - setTimeout(() => { - (adsbygoogle = window.adsbygoogle || []).push({}); - }, 0); - } catch (error) { - console.error("Error initializing ad:", error); - } - } - } - - supportHTML() { + innerHtml() { return html` -
+

- Like the game? Help make this my full-time project! - - Support the game! - + Time's running out!
+ I need your support to continue working on OpenFront full-time. Please + donate now to keep the updates, new features, and improvements coming.

+ + Support on Patreon +
`; } diff --git a/src/client/index.html b/src/client/index.html index 16eaed4b8..8688200cb 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -184,7 +184,7 @@ /> -
v21.0
+
v21.1
diff --git a/src/server/MapPlaylist.ts b/src/server/MapPlaylist.ts index 5fdd442f8..5c3d89633 100644 --- a/src/server/MapPlaylist.ts +++ b/src/server/MapPlaylist.ts @@ -1,4 +1,4 @@ -import { GameMapType } from "../core/game/Game"; +import { GameMapType, GameMode } from "../core/game/Game"; import { PseudoRandom } from "../core/PseudoRandom"; enum PlaylistType { @@ -9,6 +9,9 @@ enum PlaylistType { const random = new PseudoRandom(123); export class MapPlaylist { + private gameModeRotation = [GameMode.FFA, GameMode.FFA, GameMode.Team]; + private currentGameModeIndex = 0; + private mapsPlaylistBig: GameMapType[] = []; private mapsPlaylistSmall: GameMapType[] = []; private currentPlaylistCounter = 0; @@ -20,6 +23,13 @@ export class MapPlaylist { return mapsPlaylist.shift()!; } + public getNextGameMode(): GameMode { + const nextGameMode = this.gameModeRotation[this.currentGameModeIndex]; + this.currentGameModeIndex = + (this.currentGameModeIndex + 1) % this.gameModeRotation.length; + return nextGameMode; + } + private getNextMapsPlayList(playlistType: PlaylistType): GameMapType[] { switch (playlistType) { case PlaylistType.BigMaps: diff --git a/src/server/Master.ts b/src/server/Master.ts index 3e8aecf2a..5062e4220 100644 --- a/src/server/Master.ts +++ b/src/server/Master.ts @@ -222,11 +222,22 @@ async function fetchLobbies(): Promise { return publicLobbyIDs.size; } +let lastGameMode: GameMode = GameMode.FFA; + // Function to schedule a new public game async function schedulePublicGame(playlist: MapPlaylist) { const gameID = generateID(); const map = playlist.getNextMap(); publicLobbyIDs.add(gameID); + + if (lastGameMode == GameMode.FFA) { + lastGameMode = GameMode.Team; + } else { + lastGameMode = GameMode.FFA; + } + + const gameMode = playlist.getNextGameMode(); + // Create the default public game config (from your GameManager) const defaultGameConfig = { gameMap: map, @@ -236,9 +247,9 @@ async function schedulePublicGame(playlist: MapPlaylist) { infiniteGold: false, infiniteTroops: false, instantBuild: false, - disableNPCs: false, + disableNPCs: gameMode == GameMode.Team, disableNukes: false, - gameMode: Math.random() < 0.5 ? GameMode.FFA : GameMode.Team, + gameMode: gameMode, bots: 400, } as GameConfig; From 76e066c793a693b51664471eda3dcae166d5e558 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 9 Apr 2025 20:10:10 -0700 Subject: [PATCH 02/22] make cities 1 million --- src/core/configuration/DefaultConfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index 28626f7f0..ee5c0c25e 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -336,7 +336,7 @@ export class DefaultConfig implements Config { p.type() == PlayerType.Human && this.infiniteGold() ? 0 : Math.min( - 2_000_000, + 1_000_000, Math.pow( 2, p.unitsIncludingConstruction(UnitType.City).length, From da100630d13772b4c761ffbefe27b150ce4bdf4a Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 9 Apr 2025 20:56:41 -0700 Subject: [PATCH 03/22] update meta: cities 1M, gold addition rate --- src/core/configuration/DefaultConfig.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index ee5c0c25e..a8d7b835f 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -620,7 +620,8 @@ export class DefaultConfig implements Config { } goldAdditionRate(player: Player): number { - return Math.sqrt(player.workers() * player.numTilesOwned()) / 200; + const ratio = Math.pow(player.workers() / player.population(), 1.3); + return Math.floor(Math.sqrt(player.workers()) * ratio * 5); } troopAdjustmentRate(player: Player): number { From 6cb99ea921bcb95b494f247d487057bdefa861a9 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 9 Apr 2025 22:05:55 -0700 Subject: [PATCH 04/22] update attack logic meta --- src/core/configuration/DefaultConfig.ts | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index a8d7b835f..75291af8b 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -473,18 +473,25 @@ export class DefaultConfig implements Config { } if (defender.isPlayer()) { + const ratio = within( + Math.pow(defender.troops() / attackTroops, 0.2), + 0.1, + 10, + ); + const speedRatio = within( + defender.troops() / (5 * attackTroops), + 0.1, + 10, + ); + return { attackerTroopLoss: - within(defender.troops() / attackTroops, 0.6, 2) * + ratio * mag * - 0.8 * largeLossModifier * (defender.isTraitor() ? this.traitorDefenseDebuff() : 1), - defenderTroopLoss: defender.troops() / defender.numTilesOwned(), - tilesPerTickUsed: - within(defender.troops() / (5 * attackTroops), 0.2, 1.5) * - speed * - largeSpeedMalus, + defenderTroopLoss: defender.population() / defender.numTilesOwned(), + tilesPerTickUsed: Math.floor(speedRatio * speed * largeSpeedMalus), }; } else { return { From f273bc13152d80ed40976d34d29e93ff08a45e6b Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 9 Apr 2025 22:17:40 -0700 Subject: [PATCH 05/22] give more attack bonus larger attack. --- src/core/configuration/DefaultConfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index 75291af8b..724aea00e 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -474,7 +474,7 @@ export class DefaultConfig implements Config { if (defender.isPlayer()) { const ratio = within( - Math.pow(defender.troops() / attackTroops, 0.2), + Math.pow(defender.troops() / attackTroops, 0.4), 0.1, 10, ); From 1f581901c4dbc1222a377060157f0fb4cc73bbd5 Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 10 Apr 2025 13:37:06 -0700 Subject: [PATCH 06/22] add google analytics --- src/client/index.html | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/client/index.html b/src/client/index.html index 8688200cb..89ee71015 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -122,6 +122,20 @@ src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-7035513310742290" crossorigin="anonymous" > + + + Date: Thu, 10 Apr 2025 14:28:51 -0700 Subject: [PATCH 07/22] sam has 100% chance to hit atom bomb --- src/core/execution/SAMLauncherExecution.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/execution/SAMLauncherExecution.ts b/src/core/execution/SAMLauncherExecution.ts index b7c41f524..e3337f8fd 100644 --- a/src/core/execution/SAMLauncherExecution.ts +++ b/src/core/execution/SAMLauncherExecution.ts @@ -105,7 +105,10 @@ export class SAMLauncherExecution implements Execution { if (this.target && !this.sam.isCooldown() && !this.target.targetedBySAM()) { this.sam.setCooldown(true); const random = this.pseudoRandom.next(); - const hit = random < this.mg.config().samHittingChance(); + let hit = true; + if (this.target.type() != UnitType.AtomBomb) { + hit = random < this.mg.config().samHittingChance(); + } if (!hit) { this.mg.displayMessage( `Missile failed to intercept ${this.target.type()}`, From dd85f3ba6934da19bfd1546193adc74039afaf9f Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 10 Apr 2025 14:32:25 -0700 Subject: [PATCH 08/22] update version --- src/client/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/index.html b/src/client/index.html index 89ee71015..c1a475095 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -198,7 +198,7 @@ /> -
v21.1
+
v21.2
From aeaf747ab7faeea145f27a81cb27b2f818ae2a72 Mon Sep 17 00:00:00 2001 From: evanpelle Date: Thu, 10 Apr 2025 21:05:55 -0700 Subject: [PATCH 09/22] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..dd84ea782 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..bbcbbe7d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From 4dfdfbd4cc085fe01fcc39b58e5174dfb717562c Mon Sep 17 00:00:00 2001 From: APuddle210 Date: Fri, 11 Apr 2025 12:49:16 -0400 Subject: [PATCH 10/22] Add New Contribution Issues Template, Fix Prettier Problems with Existing Issues Templates (#451) ## Description: These changes impact GitHub Behavior only, restricted entirely to the .github folder. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: aPuddle --- .github/ISSUE_TEMPLATE/bug_report.md | 24 +++--- .github/ISSUE_TEMPLATE/feature_request.md | 7 +- .../new-contribution-template.md | 76 +++++++++++++++++++ 3 files changed, 92 insertions(+), 15 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/new-contribution-template.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea782..9b77ea713 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,10 +1,9 @@ --- name: Bug report about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - +title: "" +labels: "" +assignees: "" --- **Describe the bug** @@ -12,6 +11,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] **Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7d6..2bc5d5f71 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,10 +1,9 @@ --- name: Feature request about: Suggest an idea for this project -title: '' -labels: '' -assignees: '' - +title: "" +labels: "" +assignees: "" --- **Is your feature request related to a problem? Please describe.** diff --git a/.github/ISSUE_TEMPLATE/new-contribution-template.md b/.github/ISSUE_TEMPLATE/new-contribution-template.md new file mode 100644 index 000000000..731517927 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/new-contribution-template.md @@ -0,0 +1,76 @@ +--- +name: New Contribution Template +about: "For new Contributions to be added to the Project Management Process " +title: "" +labels: "" +assignees: "" +--- + +By the time the contribution is ready for final review, the main post of the Issue should be able to serve as the PR description, supplemented by the testing results/peer reviews. + +## Purpose + +Very brief statement as to what this contribution's purpose is. Examples: + +- Add new feature that allows users to create custom flags. +- Fix bug: modals not closing on game start. +- Add new Iceland map. + +--- + +## Objectives + +Please provide a short list of objectives for the contribution, the number and level of specificity of the items will depend on the scope of the contribution. Is the means by which completion can be measured and success of the changes assessed. May be the starting point for the creation of sub-issues if collaboration is needed/desired. Examples: + +**For the Custom Flags Feature** + +- Create UI for Flag Customization. +- Create customization features that provide a wide range of possibilities for users to express themselves. +- Ensure some customization features are limited to users with certain traits. +- Assess user traits related to limited customization against Discord roles. +- Create method for customized flags to be displayed correctly on all connected clients. +- Create means of restricting certain customization combinations to prevent hate symbols, or ensure it is not possible to create hate symbols with provided customizations. + +**For a Bug Fix** + +- Ensure all open modals except game start (and any other expected) modal close as expected when the game starts. +- Ensure all modals still function as expected prior to game start. + +**For New Map** + +- Add New Map, Iceland to the game for use in Singleplayer and Custom lobbies. +- Add map to lobby rotation. + +--- + +## Description + +Please provide a more detailed description of what is being changed/added, how it is being changed/added, provide any additional detail related to the objectives which would be needed or useful in assessing the contribution overall. + +Examples of what info would be expected: + +**For the Custom Flags Feature** + +- Details about where the UI would be accessed from, mockups of a concept UI if they exist. +- What kind of customizations will be available. +- What traits will be provided limited customization features? (Patreons, Devs, Owner, etc.) +- What kind of customizations will be limited. +- Any high-level technical implementation details that may be useful in assessing viability, or which will require assistance from other contributors or the project owner (communication between the clients, use or access to a backend DB, discord integration, etc.) + +**For a Bug Fix** + +- Details about the bug's behavior +- Details about the cause of the bug (once known) +- Details about the fix +- Any secondary effects of implementing the fix + +**For a New Map** + +- Size of the map +- Number of Nation Bots +- Images of the Map +- Any interesting features of the map + +--- + +[Discord Name if Different] From 51dd21d7f793e57459aecd4f452dcdaab7cd4836 Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 11 Apr 2025 19:54:47 -0700 Subject: [PATCH 11/22] update node user --- supervisord.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/supervisord.conf b/supervisord.conf index f6cb1191a..c31d0429c 100644 --- a/supervisord.conf +++ b/supervisord.conf @@ -18,6 +18,7 @@ command=npm run start:server directory=/usr/src/app autostart=true autorestart=true +user=node stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr From 31441ac3ddc5f370279e4204ef6cac27cf3e2f65 Mon Sep 17 00:00:00 2001 From: APuddle210 Date: Fri, 11 Apr 2025 23:56:13 -0400 Subject: [PATCH 12/22] Add Clean Ver of New Contribution Template (#468) ## Description: Clean version of the New Contribution Template without the example text, for once people are familiar or when they need to create a bunch all at once to clear a backlog (like me, today). Fixes #469 ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [X] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: aPuddle --- .../new-contribution-template.md | 59 ++----------------- 1 file changed, 5 insertions(+), 54 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/new-contribution-template.md b/.github/ISSUE_TEMPLATE/new-contribution-template.md index 731517927..3851903bc 100644 --- a/.github/ISSUE_TEMPLATE/new-contribution-template.md +++ b/.github/ISSUE_TEMPLATE/new-contribution-template.md @@ -1,75 +1,26 @@ --- -name: New Contribution Template -about: "For new Contributions to be added to the Project Management Process " +name: New Contribution Template (Clean) +about: "For new Contributions to be added to the Project Management Process, examples removed" title: "" labels: "" assignees: "" --- -By the time the contribution is ready for final review, the main post of the Issue should be able to serve as the PR description, supplemented by the testing results/peer reviews. - ## Purpose -Very brief statement as to what this contribution's purpose is. Examples: - -- Add new feature that allows users to create custom flags. -- Fix bug: modals not closing on game start. -- Add new Iceland map. +. --- ## Objectives -Please provide a short list of objectives for the contribution, the number and level of specificity of the items will depend on the scope of the contribution. Is the means by which completion can be measured and success of the changes assessed. May be the starting point for the creation of sub-issues if collaboration is needed/desired. Examples: - -**For the Custom Flags Feature** - -- Create UI for Flag Customization. -- Create customization features that provide a wide range of possibilities for users to express themselves. -- Ensure some customization features are limited to users with certain traits. -- Assess user traits related to limited customization against Discord roles. -- Create method for customized flags to be displayed correctly on all connected clients. -- Create means of restricting certain customization combinations to prevent hate symbols, or ensure it is not possible to create hate symbols with provided customizations. - -**For a Bug Fix** - -- Ensure all open modals except game start (and any other expected) modal close as expected when the game starts. -- Ensure all modals still function as expected prior to game start. - -**For New Map** - -- Add New Map, Iceland to the game for use in Singleplayer and Custom lobbies. -- Add map to lobby rotation. +- . --- ## Description -Please provide a more detailed description of what is being changed/added, how it is being changed/added, provide any additional detail related to the objectives which would be needed or useful in assessing the contribution overall. - -Examples of what info would be expected: - -**For the Custom Flags Feature** - -- Details about where the UI would be accessed from, mockups of a concept UI if they exist. -- What kind of customizations will be available. -- What traits will be provided limited customization features? (Patreons, Devs, Owner, etc.) -- What kind of customizations will be limited. -- Any high-level technical implementation details that may be useful in assessing viability, or which will require assistance from other contributors or the project owner (communication between the clients, use or access to a backend DB, discord integration, etc.) - -**For a Bug Fix** - -- Details about the bug's behavior -- Details about the cause of the bug (once known) -- Details about the fix -- Any secondary effects of implementing the fix - -**For a New Map** - -- Size of the map -- Number of Nation Bots -- Images of the Map -- Any interesting features of the map +. --- From 77abece577a1bddc505d6017b373ea168f5665e0 Mon Sep 17 00:00:00 2001 From: Aotumuri Date: Sat, 12 Apr 2025 12:58:40 +0900 Subject: [PATCH 13/22] Fixed flag size (#431) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description: before スクリーンショット 2025-04-05 16 45 08 now スクリーンショット 2025-04-05 16 44 43 Fixes #474 ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: Aotumuri --- resources/flags/uk_us_flag.svg | 424 ++++++++++++++++++++++++--------- src/client/LangSelector.ts | 2 +- 2 files changed, 314 insertions(+), 112 deletions(-) diff --git a/resources/flags/uk_us_flag.svg b/resources/flags/uk_us_flag.svg index a4f09a725..231cb4e04 100644 --- a/resources/flags/uk_us_flag.svg +++ b/resources/flags/uk_us_flag.svg @@ -1,112 +1,314 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/client/LangSelector.ts b/src/client/LangSelector.ts index fa3bf9462..d177f2cbf 100644 --- a/src/client/LangSelector.ts +++ b/src/client/LangSelector.ts @@ -246,7 +246,7 @@ export class LangSelector extends LitElement { : { native: "English", en: "English", - svg: "xx", + svg: "uk_us_flag", }); return html` From 608f57b2a9bc21406c8f651d16c198731d960627 Mon Sep 17 00:00:00 2001 From: Tom <88888824+ImDarkTom@users.noreply.github.com> Date: Sat, 12 Apr 2025 16:57:04 +0100 Subject: [PATCH 14/22] Improve private lobby code readability (#438) ## Description: Use mono font for code generated for private lobbies, making it easier to distinguish some letters (lowercase L's vs. capital I's). Fixes #391. Fixes #462 Before: ![image](https://github.com/user-attachments/assets/0b6f6f15-5e7c-4c31-abed-3301a3f508e9) After: ![image](https://github.com/user-attachments/assets/abd7b6f7-75e6-4461-85fa-528ebf92cc14) ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [X] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: imdarktom --- src/client/styles.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/client/styles.css b/src/client/styles.css index b7c531151..9409a90fa 100644 --- a/src/client/styles.css +++ b/src/client/styles.css @@ -214,6 +214,8 @@ label.option-card:hover { font-size: 14px; color: #fff; text-align: center; + font-family: monospace; + font-weight: 600; } .players-list { From 4c777412b6efc3260c75ffacb4c1ec3a9fd8b0a9 Mon Sep 17 00:00:00 2001 From: Aotumuri Date: Sun, 13 Apr 2025 01:34:15 +0900 Subject: [PATCH 15/22] Improve Key Interaction Visibility (#428) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description: Enhanced the visual clarity of key inputs using custom CSS. Keyboard keys now appear more distinct and intuitive, making the help modal easier to understand at a glance. Keys can be changed easily. Fixes #477 ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: aotumuri(.w. / (๑-̀ㅂ-́)و✧) --- src/client/HelpModal.ts | 61 ++++++++++++++--- src/client/styles.css | 1 + src/client/styles/components/controls.css | 80 +++++++++++++++++++++++ 3 files changed, 132 insertions(+), 10 deletions(-) create mode 100644 src/client/styles/components/controls.css diff --git a/src/client/HelpModal.ts b/src/client/HelpModal.ts index 8de819377..3fb41cc97 100644 --- a/src/client/HelpModal.ts +++ b/src/client/HelpModal.ts @@ -35,43 +35,84 @@ export class HelpModal extends LitElement { - Space + Space ${translateText("help_modal.action_alt_view")} - Shift + left click + +
+ Shift + + +
+
+
+
+
+ ${translateText("help_modal.action_attack_altclick")} - Ctrl + left click + +
+ Ctrl + + +
+
+
+
+
+ ${translateText("help_modal.action_build")} - Alt + left click + +
+ Alt + + +
+
+
+
+
+ ${translateText("help_modal.action_emote")} - C + C ${translateText("help_modal.action_center")} - Q / E + Q / E ${translateText("help_modal.action_zoom")} - W / A / S / D + W A S D ${translateText("help_modal.action_move_camera")} - 1 / 2 + 1 / 2 ${translateText("help_modal.action_ratio_change")} - Shift + scroll down / scroll up + +
+ Shift + + +
+
+
+
+
+
+
+
+
+
+ ${translateText("help_modal.action_ratio_change")} - ALT + R + ALT + R ${translateText("help_modal.action_reset_gfx")} diff --git a/src/client/styles.css b/src/client/styles.css index 9409a90fa..c85b38c60 100644 --- a/src/client/styles.css +++ b/src/client/styles.css @@ -8,6 +8,7 @@ @import url("./styles/layout/container.css"); @import url("./styles/components/button.css"); @import url("./styles/components/modal.css"); +@import url("./styles/components/controls.css"); * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; diff --git a/src/client/styles/components/controls.css b/src/client/styles/components/controls.css new file mode 100644 index 000000000..0bd8d162f --- /dev/null +++ b/src/client/styles/components/controls.css @@ -0,0 +1,80 @@ +.scroll-combo-horizontal { + display: inline-flex; + align-items: center; + gap: 12px; + font-family: sans-serif; + color: white; +} + +.key { + display: inline-block; + padding: 4px 14px; + border-radius: 6px; + background-color: #000; + color: #fff; + font-weight: bold; + box-shadow: 0 2px 0 #444; +} + +.plus { + font-size: 16px; + color: #ccc; +} + +.mouse-shell { + width: 28px; + height: 45px; + border: 2px solid #ccc; + border-radius: 50px; + position: relative; + background: transparent; +} + +.mouse-left-corner { + position: absolute; + top: 0; + left: 0; + width: 50%; + height: 50%; + background-color: #ff4d4d; + border-top-left-radius: 50px; +} + +.mouse-right-corner { + position: absolute; + top: 0; + right: 0; + width: 50%; + height: 50%; + background-color: #ff4d4d; + border-top-right-radius: 50px; +} + +.mouse-wheel { + width: 4px; + height: 8px; + background-color: #ccc; + border-radius: 2px; + position: absolute; + top: 8px; + left: 50%; + transform: translateX(-50%); +} + +#highlighted-wheel { + background-color: #ff4d4d; +} + +.mouse-with-arrows { + display: flex; + align-items: center; + gap: 6px; +} + +.mouse-arrows-side { + display: flex; + flex-direction: column; + gap: 4px; + font-size: 14px; + color: #ccc; +} From 3caecc19781b3ea129b2065fc31e58248d049e44 Mon Sep 17 00:00:00 2001 From: Evan Date: Sat, 12 Apr 2025 10:14:52 -0700 Subject: [PATCH 16/22] create logger component at top of file to prevent reassigning worker --- src/server/Worker.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/server/Worker.ts b/src/server/Worker.ts index 0a2d0db83..5527cf52a 100644 --- a/src/server/Worker.ts +++ b/src/server/Worker.ts @@ -18,14 +18,12 @@ import { metrics } from "./WorkerMetrics"; const config = getServerConfigFromServer(); -let log = logger.child({ component: "Worker" }); +const workerId = parseInt(process.env.WORKER_ID || "0"); +const log = logger.child({ component: `worker_${workerId}` }); // Worker setup export function startWorker() { - // Get worker ID from environment variable - const workerId = parseInt(process.env.WORKER_ID || "0"); - log = log.child({ workerId: workerId }); - log.info(`Worker ${workerId} starting...`); + log.info(`Worker starting...`); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); From 58b151f5857eeeb068bb6e4d941ac90eaaedec34 Mon Sep 17 00:00:00 2001 From: Evan Date: Sat, 12 Apr 2025 17:54:36 -0700 Subject: [PATCH 17/22] remove slog, use winston instead. --- src/server/GameServer.ts | 91 ++++++++++++++++++++++--------------- src/server/Master.ts | 2 +- src/server/StructuredLog.ts | 57 ----------------------- src/server/Worker.ts | 35 +++----------- 4 files changed, 62 insertions(+), 123 deletions(-) delete mode 100644 src/server/StructuredLog.ts diff --git a/src/server/GameServer.ts b/src/server/GameServer.ts index 9210f7660..41d74a92b 100644 --- a/src/server/GameServer.ts +++ b/src/server/GameServer.ts @@ -24,7 +24,6 @@ import { GameType } from "../core/game/Game"; import { archive } from "./Archive"; import { Client } from "./Client"; import { gatekeeper } from "./Gatekeeper"; -import { slog } from "./StructuredLog"; export enum GamePhase { Lobby = "LOBBY", Active = "ACTIVE", @@ -99,19 +98,11 @@ export class GameServer { } public addClient(client: Client, lastTurn: number) { - this.log.info(`adding client ${client.clientID}`); - slog({ - logKey: "client_joined_game", - msg: `client ${client.clientID} (re)joining game ${this.id}`, - data: { - clientID: client.clientID, - clientIP: client.ip, - gameID: this.id, - isRejoin: lastTurn > 0, - }, + this.log.info("client (re)joining game", { clientID: client.clientID, persistentID: client.persistentID, - gameID: this.id, + clientIP: client.ip, + isRejoin: lastTurn > 0, }); if ( @@ -120,9 +111,10 @@ export class GameServer { (c) => c.ip == client.ip && c.clientID != client.clientID, ).length >= 3 ) { - this.log.info( - `cannot add client ${client.clientID}, already have 3 ips (${client.ip})`, - ); + this.log.warn("cannot add client, already have 3 ips", { + clientID: client.clientID, + clientIP: client.ip, + }); return; } @@ -156,6 +148,10 @@ export class GameServer { if (client.persistentID != clientMsg.persistentID) { this.log.warn( `Client ID ${clientMsg.clientID} sent incorrect id ${clientMsg.persistentID}, does not match persistent id ${client.persistentID}`, + { + clientID: clientMsg.clientID, + persistentID: clientMsg.persistentID, + }, ); return; } @@ -168,9 +164,10 @@ export class GameServer { if (clientMsg.gameID == this.id) { this.addIntent(clientMsg.intent); } else { - this.log.warn( - `${this.id}: client ${clientMsg.clientID} sent to wrong game`, - ); + this.log.warn("client sent to wrong game", { + clientID: clientMsg.clientID, + persistentID: clientMsg.persistentID, + }); } } if (clientMsg.type == "ping") { @@ -187,12 +184,18 @@ export class GameServer { } catch (error) { this.log.info( `error handline websocket request in game server: ${error}`, + { + clientID: client.clientID, + }, ); } }), ); client.ws.on("close", () => { - this.log.info(`${this.id}: client ${client.clientID} disconnected`); + this.log.info("client disconnected", { + clientID: client.clientID, + persistentID: client.persistentID, + }); this.activeClients = this.activeClients.filter( (c) => c.clientID != client.clientID, ); @@ -245,7 +248,10 @@ export class GameServer { const msg = JSON.stringify(prestartMsg.data); this.activeClients.forEach((c) => { - this.log.info(`${this.id}: sending prestart message to ${c.clientID}`); + this.log.info("sending prestart message", { + clientID: c.clientID, + persistentID: c.persistentID, + }); c.ws.send(msg); }); } @@ -276,7 +282,10 @@ export class GameServer { this.config.turnIntervalMs(), ); this.activeClients.forEach((c) => { - this.log.info(`${this.id}: sending start message to ${c.clientID}`); + this.log.info("sending start message", { + clientID: c.clientID, + persistentID: c.persistentID, + }); this.sendStartGameMsg(c.ws, 0); }); } @@ -327,10 +336,8 @@ export class GameServer { ); } catch (error) { this.log.info( - `error sending message for game ${this.id}, error ${error}`.substring( - 0, - 250, - ), + `error sending message for game: ${error.substring(0, 250)}`, + {}, ); return; } @@ -349,9 +356,7 @@ export class GameServer { client.ws.close(1000, "game has ended"); } }); - this.log.info( - `${this.id}: ending game ${this.id} with ${this.turns.length} turns`, - ); + this.log.info("ending game", { gameID: this.id, turns: this.turns.length }); try { if (this.allClients.size > 0) { const playerRecords: PlayerRecord[] = Array.from( @@ -376,7 +381,9 @@ export class GameServer { ), ); } else { - this.log.info(`${this.id}: no clients joined, not archiving game`); + this.log.info("no clients joined, not archiving game", { + gameID: this.id, + }); } } catch (error) { let errorDetails; @@ -408,9 +415,10 @@ export class GameServer { const alive = []; for (const client of this.activeClients) { if (now - client.lastPing > 60_000) { - this.log.info( - `${this.id}: no pings from ${client.clientID}, terminating connection`, - ); + this.log.info("no pings received, terminating connection", { + clientID: client.clientID, + persistentID: client.persistentID, + }); if (client.ws.readyState === WebSocket.OPEN) { client.ws.close(1000, "no heartbeats received, closing connection"); } @@ -420,7 +428,9 @@ export class GameServer { } this.activeClients = alive; if (now > this.createdAt + this.maxGameDuration) { - this.log.warn(`${this.id}: game past max duration ${this.id}`); + this.log.warn("game past max duration", { + gameID: this.id, + }); return GamePhase.Finished; } @@ -430,7 +440,9 @@ export class GameServer { if (this.gameConfig.gameType != GameType.Public) { if (this._hasStarted) { if (noActive && noRecentPings) { - this.log.info(`${this.id}: private game: ${this.id} complete`); + this.log.info("private game complete", { + gameID: this.id, + }); return GamePhase.Finished; } else { return GamePhase.Active; @@ -508,7 +520,10 @@ export class GameServer { totalActiveClients: this.activeClients.length, }); if (!serverDesync.success) { - this.log.warn(`failed to create desync message ${serverDesync.error}`); + this.log.warn("failed to create desync message", { + gameID: this.id, + error: serverDesync.error, + }); return; } @@ -518,7 +533,11 @@ export class GameServer { continue; } this.sentDesyncMessageClients.add(c.clientID); - this.log.info(`game: ${this.id}: sending desync to client ${c.clientID}`); + this.log.info("sending desync to client", { + gameID: this.id, + clientID: c.clientID, + persistentID: c.persistentID, + }); c.ws.send(desyncMsg); } } diff --git a/src/server/Master.ts b/src/server/Master.ts index 5062e4220..24a198cdd 100644 --- a/src/server/Master.ts +++ b/src/server/Master.ts @@ -24,7 +24,7 @@ const server = http.createServer(app); const metricsApp = express(); const metricsServer = http.createServer(metricsApp); -const log = logger.child({ component: "Master" }); +const log = logger.child({ comp: "m" }); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); diff --git a/src/server/StructuredLog.ts b/src/server/StructuredLog.ts deleted file mode 100644 index f15bfda94..000000000 --- a/src/server/StructuredLog.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { ClientID, GameID, LogSeverity } from "../core/Schemas"; - -export interface slogMsg { - logKey: string; - msg: string; - data?: { - stack?: unknown; - clientID?: unknown; - clientIP?: unknown; - gameID?: unknown; - isRejoin?: unknown; - }; - severity?: LogSeverity; - gameID?: GameID; - clientID?: ClientID; - persistentID?: string; - stack?: string; // Added stack property -} - -export function slog(msg: slogMsg): void { - msg.severity = msg.severity ?? LogSeverity.Info; - - // Format stack trace if available - if (msg.stack) { - // Keep the stack trace in the log data - if (!msg.data) { - msg.data = { stack: msg.stack }; - } else if (typeof msg.data === "object") { - msg.data.stack = msg.stack; - } - } - - if (process.env.GAME_ENV == "dev") { - // Avoid blowing up the log during development. - if (msg.logKey == "client_console_log") { - return; - } - if (msg.severity != LogSeverity.Debug) { - console.log(msg.msg); - // Print stack trace in development for errors - if (msg.severity === LogSeverity.Error && msg.stack) { - console.error(msg.stack); - } - } - } else { - try { - console.log(JSON.stringify(msg)); - } catch (error) { - console.error("Failed to stringify log message:", error); - // Fallback to basic logging - console.log(`${msg.severity}: ${msg.msg}`); - if (msg.severity === LogSeverity.Error && msg.stack) { - console.error(msg.stack); - } - } - } -} diff --git a/src/server/Worker.ts b/src/server/Worker.ts index 5527cf52a..8a6d0ddd4 100644 --- a/src/server/Worker.ts +++ b/src/server/Worker.ts @@ -7,19 +7,18 @@ import { WebSocket, WebSocketServer } from "ws"; import { GameEnv } from "../core/configuration/Config"; import { getServerConfigFromServer } from "../core/configuration/ConfigLoader"; import { GameType } from "../core/game/Game"; -import { GameConfig, GameRecord, LogSeverity } from "../core/Schemas"; +import { GameConfig, GameRecord } from "../core/Schemas"; import { archive, readGameRecord } from "./Archive"; import { Client } from "./Client"; import { GameManager } from "./GameManager"; import { gatekeeper, LimiterType } from "./Gatekeeper"; import { logger } from "./Logger"; -import { slog } from "./StructuredLog"; import { metrics } from "./WorkerMetrics"; const config = getServerConfigFromServer(); const workerId = parseInt(process.env.WORKER_ID || "0"); -const log = logger.child({ component: `worker_${workerId}` }); +const log = logger.child({ comp: `w_${workerId}` }); // Worker setup export function startWorker() { @@ -340,7 +339,7 @@ export function startWorker() { // The load balancer will handle routing to this server based on path const PORT = config.workerPortByIndex(workerId); server.listen(PORT, () => { - log.info(`Worker ${workerId} running on http://localhost:${PORT}`); + log.info(`running on http://localhost:${PORT}`); log.info(`Handling requests with path prefix /w${workerId}/`); // Signal to the master process that this worker is ready if (process.send) { @@ -348,44 +347,22 @@ export function startWorker() { type: "WORKER_READY", workerId: workerId, }); - log.info(`Worker ${workerId} signaled ready state to master`); + log.info(`signaled ready state to master`); } }); // Global error handler app.use((err: Error, req: Request, res: Response, next: NextFunction) => { log.error(`Error in ${req.method} ${req.path}:`, err); - slog({ - logKey: "server_error", - msg: `Unhandled exception in ${req.method} ${req.path}: ${err.message}`, - severity: LogSeverity.Error, - stack: err.stack, - }); res.status(500).json({ error: "An unexpected error occurred" }); }); // Process-level error handlers process.on("uncaughtException", (err) => { - log.error(`Worker ${workerId} uncaught exception:`, err); - slog({ - logKey: "uncaught_exception", - msg: `Worker ${workerId} uncaught exception: ${err.message}`, - severity: LogSeverity.Error, - stack: err.stack, - }); + log.error(`uncaught exception:`, err); }); process.on("unhandledRejection", (reason, promise) => { - log.error( - `Worker ${workerId} unhandled rejection at:`, - promise, - "reason:", - reason, - ); - slog({ - logKey: "unhandled_rejection", - msg: `Worker ${workerId} unhandled promise rejection: ${reason}`, - severity: LogSeverity.Error, - }); + log.error(`unhandled rejection at:`, promise, "reason:", reason); }); } From adb29035c0fdf8c2bce043ebb299e9bd498519be Mon Sep 17 00:00:00 2001 From: Aotumuri Date: Sun, 13 Apr 2025 10:01:01 +0900 Subject: [PATCH 18/22] Change lang modal (#432) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description: I updated the language modal to match the style of the other modals. スクリーンショット 2025-04-05 18 17 56 Fixes #473 ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: aotumuri --- resources/lang/bg.json | 12 ++- resources/lang/en.json | 3 + resources/lang/{eo => eo.json} | 108 ++++++++++++--------- resources/lang/ja.json | 3 + src/client/LanguageModal.ts | 170 +++++++++++++++++++++------------ 5 files changed, 184 insertions(+), 112 deletions(-) rename resources/lang/{eo => eo.json} (68%) diff --git a/resources/lang/bg.json b/resources/lang/bg.json index da444e6c0..62075f44a 100644 --- a/resources/lang/bg.json +++ b/resources/lang/bg.json @@ -28,7 +28,7 @@ "ui_pop": "Pop (Популация) - Количеството популация, която притежавате, максималната Ви популация и скоростта, с която тя се увеличава.", "ui_gold": "Gold (Злато) - Количеството злато, което притежавате и скоростта, с която го получавате.", "ui_troops_workers": "Войници и Работници - Количеството разпределени войници и работници. Войниците се използват за атакуване или защитаване срещу атаки. Работниците се използват за генериране на злато. Можете да коригирате количеството войници и работници, използвайки плъзгача.", - "ui_attack_ratio": "Съотношение на атака - Количеството войници, които ще се използват при атака. Можете да коригирате съотношението на атака, използвайки плъзгача.", + "ui_attack_ratio": "Съотношение на атака - Количеството войници, които ще се използват при атака. Можете да коригирате съотношението на атака, използвайки плъзгача. Притежаването на повече атакуващи войници от тези в защита ще ви накара да загубите по-малко войници при атаката, докато притежаването на по-малко ще увеличи щетите, нанесени на вашите атакуващи войници. Ефектът не надхвърля съотношения 2:1.", "ui_options": "Опции", "ui_options_desc": "Следните елементи могат да бъдат намерени вътре:", "option_pause": "Поставяне/Премахване на пауза на играта - Налични само в самостоятелна игра.", @@ -48,7 +48,7 @@ "info_emoji": "Изпращане на емоджи до играча.", "info_ally_panel": "Информационно меню за съюзници", "info_ally_desc": "Когато се съюзите с играч, следните иконки стават налични:", - "ally_betray": "Предаване на съюзника Ви, прекратявайки съюзничеството. Сега ще имате перманентна иконка, заседнала до името Ви. Има по-малка вероятност ботове да се съюзят с Вас, а играчи ще мислят два пъти преди да го направят.", + "ally_betray": "Предаване на съюзника Ви, прекратявайки съюзничеството. Сега ще имате постоянна икона, залепена до името Ви, освен ако самата друга нация не е била предател. Атаките срещу вас ще доведат до по-малко загуби за нападателя до края на играта, по-малко вероятно е ботовете да се съюзят с Вас и играчите ще мислят два пъти, преди да го направят.", "ally_donate": "Даряване на част от войниците си на съюзника Ви. Използвано, когато той е с малко войници и бива атакуван или когато му е нужна тази допълнителна сила, за да размаже противника.", "build_menu_title": "Меню за строене", "build_name": "Име", @@ -61,7 +61,7 @@ "build_port": "Пристанище", "build_port_desc": "Автоматично изпраща търговски кораби между пристанищата на вашата страна и други държави (освен ако сте щракнали върху \"спиране на търговията\" върху тях или те са щракнали върху \"спиране на търговията с вас\"), давайки злато и на двете страни. Позволява изграждането на бойни кораби. Може да се строи само близо до вода.", "build_warship": "Боен кораб", - "build_warship_desc": "Патрулира в даден район, като превзема търговски кораби и унищожава вражески военни кораби и лодки. Създава се от най-близкото пристанище и патрулира района, в който първо сте щракнали, за да го построите.", + "build_warship_desc": "Патрулира в даден район, като превзема търговски кораби и унищожава вражески военни кораби и лодки. Създава се от най-близкото пристанище и патрулира района, в който първо сте щракнали, за да го построите. Можете да контролирате бойните кораби, като щракнете с атака върху тях и след това щракнете с атака върху новата област, към която искате да се преместят.", "build_silo": "Ракетен силоз", "build_silo_desc": "Позволява изстрелване на ракети.", "build_sam": "Противоракетна установка земя-въздух SAM", @@ -150,7 +150,8 @@ "players": "Играчи", "waiting": "Изчакване на играчи...", "start": "Започване на игра", - "mode": "Начин на игра" + "mode": "Начин на игра", + "action_emote": "Отваряне на менюто за емоджита" }, "difficulty": { "Relaxed": "Релаксирано", @@ -172,5 +173,8 @@ "game_mode": { "ffa": "Всеки срещу всеки (FFA)", "teams": "Отбори" + }, + "select_lang": { + "title": "Изберете език" } } diff --git a/resources/lang/en.json b/resources/lang/en.json index 06ae51508..05a3c6695 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -174,5 +174,8 @@ "game_mode": { "ffa": "Free for All", "teams": "Teams" + }, + "select_lang": { + "title": "Select Language" } } diff --git a/resources/lang/eo b/resources/lang/eo.json similarity index 68% rename from resources/lang/eo rename to resources/lang/eo.json index 802d98b8c..e783bc971 100644 --- a/resources/lang/eo +++ b/resources/lang/eo.json @@ -1,88 +1,83 @@ { - "lang": { - "en": "Esperanto", - "native": "Esperanto", - "svg": "eo", - "lang_code": "eo" - }, "main": { "join_discord": "Kunigu la Discord-servilon !", "create_lobby": "Krei salonon", "join_lobby": "Kunigi salonon", - "single_player": "Ludi solan", - "instructions": "Instruadoj", - "how_to_play": "Kiel ludi ?", + "single_player": "Sola Ludanto", + "instructions": "Instrukcioj", + "how_to_play": "Kiel ludi", "wiki": "Vikio" }, "help_modal": { - "hotkeys": "Agklavoj", + "hotkeys": "Rapidklavoj", "table_key": "Klavo", "table_action": "Influo", "action_alt_view": "Alternativa vido (tereno/lando)", "action_attack_altclick": "Ataki (kiam la maldekstra klako estas formi por malfermi la menuon)", "action_build": "Malfermi la konstruan menuon", - "action_center": "Reenfokusigi la fotilon", + "action_center": "Recentrigi la kameraon sur la ludanto", "action_zoom": "Zomi", - "action_move_camera": "Movi la fotilon", + "action_move_camera": "Moviĝi kameraon", "action_ratio_change": "Malgrandigi/Pligrandigi la atakan kvocienton", "action_reset_gfx": "Restarigi la grafismoj", "ui_section": "Ludo uzantinterfaco", - "ui_leaderboard": "Ordigo", + "ui_leaderboard": "Ĉampionejo", "ui_leaderboard_desc": "Afiŝas la plej bonajn ludantojn kun iliaj nomoj, la % de la poseda teritorio kaj la oro.", "ui_control": "Kontrolpanelo", - "ui_control_desc": "La kontrolpanelo enhavas la sekvajn elementojn :", - "ui_pop": "Pop - La nombro de trupunoj ke vi havas, via maximuma populado kaj la procento kun kiu vi recivas ilin.", - "ui_gold": "Oro - La kvanto da oro ke vi havas kaj la procento kun kiu vi recivas ilin.", + "ui_control_desc": "La kontrolpanelo enhavas la sekvajn elementojn:", + "ui_pop": "Pop - La nombro de trupunoj ke vi havas, via maksimuma populacio kaj la procento kun kiu vi ricevas ilin.", + "ui_gold": "La kvanto da oro ke vi havas kaj la procento kun kiu vi ricevas ilin.", "ui_troops_workers": "Trupoj kaj Laboristoj - La nombro da soldatoj kaj laboristoj asignitaj. Trupoj kutimas ataki aŭ defendi. Laboristoj kutimas generi oron. Vi povas ĝustigi la nombron da trupoj kaj laboristoj uzante la glitilon.", - "ui_attack_ratio": "Ataka kvociento - La nombro da uzaj soldatoj kiam vi atakas. Vi povas ĝustigi la atakan kvocienton uzante la glitilon", + "ui_attack_ratio": "Ataka kvociento - La nombro da uzaj soldatoj kiam vi atakas. Vi povas ĝustigi la atakan kvocienton uzante la glitilon. Havi pli da atakaj trupoj ol la defendaj trupoj malpliigos la perdojn dum atako, dum havi malpli multe pliigos la damaĝon ricevitajn de viaj atakaj trupoj. La efiko ne superas rilatumon de 2:1.", "ui_options": "Opcioj", - "ui_options_desc": "La sekvaj elementoj povas esti trovigita interne :", - "option_pause": "Paŭzigi/Rekomenci la ludon - Nur elbla en la solan modo", - "option_timer": "Kronometro - Pasinta tempo écoulé ekde la luda komenco.", + "ui_options_desc": "La sekvaj elementoj povas esti trovigita interne:", + "option_pause": "Paŭzigi/Rekomenci la ludon - Nur ebla en la solan modo.", + "option_timer": "Kronometro - Pasinta tempo ekde la luda komenco.", "option_exit": "Elira butono", - "option_settings": "Parametroj - Malfermi la parametran menuon. Vi povas aktivi la ous pouvez y eblegi la alterna vido, la malhela reĝimo, la emoĝioj kaj la maldekstra alklaku ago.", + "option_settings": "Parametroj - Malfermi la parametran menuon. Vi povas aktivi la alterna vido, la malhela reĝimo, la emoĝioj kaj la maldekstra alklako.", "radial_title": "Radia menuo", "radial_desc": "Dekstra klako (aŭ tuŝo sur poŝtelefono) malfermas la radian menuon. De tie, vi povas:", "radial_build": "Malfermi la konstrumenuon.", "radial_info": "Malfermi la informmenuon.", "radial_boat": "Sendi boaton por ataki ĉe la elektita loko (disponebla nur se vi havas aliron al akvo).", - "radial_close": "Fermi la menuon." + "radial_close": "Fermi la menuon.", "info_title": "Informmenuo", "info_enemy_desc": "Enhavas informojn kiel la nomon de la elektita ludanto, la oron, la trupojn, kaj ĉu la ludanto estas perfidulo. Perfidulo estas ludanto, kiu perfidis kaj atakis alian ludanton kun kiu li estis en alianco. La subaj piktogramoj reprezentas la jenajn interagojn:", "info_target": "Meti celmarkon sur la ludanton, videblan por ĉiuj aliancanoj, uzata por kunordigi atakojn.", "info_alliance": "Sendi aliancpeton al la ludanto. Aliancanoj povas dividi rimedojn kaj trupojn, sed ne povas ataki unu la alian.", - "info_emoji": "Sendi emoji al la ludanto." + "info_emoji": "Sendi emoĝioj al la ludanto.", "info_ally_panel": "Aliancana informpanelo", "info_ally_desc": "Kiam vi alianciĝas kun ludanto, la jenaj novaj piktogramoj fariĝas disponeblaj:", - "ally_betray": "Perfidi aliancanon, finante la aliancon. Vi tiam havos konstantan ikonon apud via nomo. La robotoj malpli verŝajne alianciĝos kun vi, kaj ludantoj, al kiuj vi petos aliancon, pli atente pripensos.", - "ally_donate": "Doni parton de viaj trupoj al aliancano. Uzata kiam ili mankas da trupoj kaj estas atakataj, aŭ kiam ili bezonas kroman potencon por venki malamikon." + "ally_betray": "Kronometro - Pasinta tempo ekde la luda komenco.", + "ally_donate": "Doni parton de viaj trupoj al aliancano. Uzata kiam ili mankas da trupoj kaj estas atakataj, aŭ kiam ili bezonas kroman potencon por venki malamikon.", "build_menu_title": "Konstrua menuo", "build_name": "Nomo", "build_icon": "Ikono", - "build_desc": "Priskribo" + "build_desc": "Priskribo", "build_city": "Urbo", - "build_city_desc": "Utila kiam vi ne povas pligrandigi vian teritorion aŭ kiam vi atingas vian loĝlimon.", + "build_city_desc": "Utila kiam vi ne povas pligrandigi vian teritorion aŭ kiam vi atingas vian loĝantlimon.", "build_defense": "Defenda posteno", "build_defense_desc": "Plifortigas la defendojn ĉirkaŭ proksimaj limoj. Malamikaj atakoj fariĝas pli malrapidaj kaj kaŭzas pli da perdoj.", "build_port": "Haveno", - "build_port_desc": "Aŭtomate sendas komercajn ŝipojn de viaj havenoj al aliaj landoj (krom se vi elektis 'ĉesi komercon' kun ludanto, aŭ se ili faris same kun vi), donante oron al ambaŭ flankoj. Permesas konstrui militŝipojn. Povas esti konstruita nur proksime de akvo.", + "build_port_desc": "Aŭtomate sendas komercŝipojn inter la havenoj de via lando kaj aliaj landoj (krom se vi alklakis \"ĉesi komerco\" kun ili aŭ ili alklakis \"ĉesi komerco\" kun vi), donante oron al ambaŭ flankoj. Permesas konstrui batalŝipojn. Eblas konstrui nur proksime de akvo.", "build_warship": "Militŝipo", - "build_warship_desc": "Patrolas en areo, kaptante komercajn ŝipojn kaj detruante militŝipojn kaj malamikajn boatojn. Aperas en la plej proksima haveno kaj patrolas la elektitan areon.", + "build_warship_desc": "Patroloj en areo, kaptante komercajn ŝipojn kaj detruante militŝipojn kaj malamikajn boatojn. Aperas en la plej proksima haveno kaj patrolas la elektita areo.", "build_silo": "Misila silo", "build_silo_desc": "Ebligas lanĉi misilojn.", "build_sam": "SAM-lanĉilo", - "build_sam_desc": "Havas 75 % da ŝanco interkapti malamikajn misilojn ene de sia 100-piksela atingo. La SAM havas reŝarĝotempon de 7,5 sekundoj kaj ne povas interkapti MIRV-ojn.", + "build_sam_desc": "Havas 75 % da ŝanco interkapti malamikajn misilojn ene de sia 100-piksela atingo. La SAM havas reŝarĝa tempon de 7,5 sekundoj kaj ne povas interkapti MIRV-ojn.", "build_atom": "Atombombo", "build_atom_desc": "Eta eksploda bombo kiu detruas teritorion, konstruaĵojn, ŝipojn kaj boatojn. Aperas el la plej proksima Misila silo kaj atingas la elektitan areon.", "build_hydrogen": "Hidrogenbombo", "build_hydrogen_desc": "Granda eksploda bombo. Aperas el la plej proksima Misila silo kaj atingas la elektitan areon.", "build_mirv": "MIRV", - "build_mirv_desc": "La plej potenca bombo en la ludo. Dividiĝas en pli malgrandajn bombojn kiuj kovros vastan teritorion. Damaĝas nur la ludanton, kontraŭ kiu vi celis ĝin. Aperas el la plej proksima Misila silo kaj atingas la elektitan areon." + "build_mirv_desc": "La plej potenca bombo en la ludo. Dividiĝas en pli malgrandajn bombojn kiuj kovros vastan teritorion. Damaĝas nur la ludanton, kontraŭ kiu vi celis ĝin. Aperas el la plej proksima Misila silo kaj atingas la elektitan areon.", "player_icons": "Ludantaj Ikonoj", - "icon_desc": "Ekzemploj de iuj ludikonoj, kiujn vi renkontos, kaj ilia signifo:", - "icon_crown": "Krono - Ĉi tiu ludanto estas numero 1 en la ranglisto.", + "icon_desc": "Ekzemploj de iuj ludaj ikonoj, kiujn vi renkontos, kaj ilia signifo:", + "icon_crown": "Krono - Ĉi tiu ludanto estas numero 1 en la ranglisto", "icon_traitor": "Krucitaj glavoj - Perfida. Ĉi tiu ludanto atakis aliancanon.", - "icon_ally": "Manpremo - Aliancano. Ĉi tiu ludanto estas via aliancano." + "icon_ally": "Manpremo - Aliancano. Ĉi tiu ludanto estas via aliancano.", + "info_enemy_panel": "Malamiko informpanelo" }, "single_modal": { "title": "Sola Ludanto", @@ -98,10 +93,9 @@ "start": "Komenci la ludon" }, "map": { - "map": "Mapo", "world": "Mondo", "europe": "Eŭropo", - "mena": "MENA", + "mena": "Mezoriento kaj Nordafriko", "northamerica": "Nordameriko", "oceania": "Oceanio", "blacksea": "Nigra Maro", @@ -112,8 +106,13 @@ "britannia": "Granda Britio", "gatewaytotheatlantic": "Pordo al Atlantiko", "australia": "Aŭstralio", + "random": "Hazarda", "iceland": "Islando", - "random": "Hazarda" + "pangaea": "Pangeo", + "map": "Karto", + "betweentwoseas": "Inter du maroj", + "japan": "Japanio kaj najbaroj", + "knownworld": "Konata Mondo" }, "private_lobby": { "title": "Aliĝi al privata salono", @@ -124,11 +123,11 @@ "checking": "Kontrolado de la salono...", "not_found": "Salono ne trovita. Bonvolu kontroli la ID kaj reprovi.", "error": "Eraro okazis. Bonvolu reprovi.", - "joined_waiting": "Sukcese aliĝite! Atendante la komencon de la ludo..." + "joined_waiting": "Sukcese aliĝis! Atendante la komencon de la ludo..." }, "public_lobby": { - "join": "Kunigi la baldaŭa ludo", - "waiting": "Atendante ludantoj" + "join": "Kunigi la baldaŭan ludon", + "waiting": "atendante ludantoj" }, "username": { "enter_username": "Enigu vian uzantnomon", @@ -143,20 +142,39 @@ "bots": "Robotoj :", "bots_disabled": "Malŝaltita", "disable_nations": "Malŝalti naciojn", - "instant_build": "Imposta Konstruado", + "instant_build": "Tujkonstruaĵo", "infinite_gold": "Senfina oro", "infinite_troops": "Senfinaj trupoj", "disable_nukes": "Malŝalti nukleajn armilojn", "player": "Ludanto", "players": "Ludantoj", "waiting": "Atendante ludantojn...", - "start": "Komenci la ludon" + "start": "Komenci la ludon", + "mode": "Reĝimo", + "action_emote": "Malfermi miensimbolan menuon" }, "difficulty": { - "difficulty": "Malfacileco", - "Relaxed": "Relaksa", + "Relaxed": "Senstreĉa", "Balanced": "Ekvilibriga", "Intense": "Intensa", - "Impossible": "Neebla" + "Impossible": "Neebla", + "difficulty": "Malfacileco" + }, + "game_starting_modal": { + "title": "Ludo komenciĝas...", + "desc": "Preparante por komenci la salonon. Bonvolu atendi." + }, + "lang": { + "en": "Esperanto", + "native": "Esperanto", + "svg": "eo", + "lang_code": "eo" + }, + "game_mode": { + "ffa": "Senpaga por ĉiuj", + "teams": "Teamoj" + }, + "select_lang": { + "title": "Elekti lingvon" } } diff --git a/resources/lang/ja.json b/resources/lang/ja.json index 13514913e..a86528b71 100644 --- a/resources/lang/ja.json +++ b/resources/lang/ja.json @@ -173,5 +173,8 @@ "game_mode": { "ffa": "バトルロワイヤル", "teams": "チーム戦" + }, + "select_lang": { + "title": "言語を選択" } } diff --git a/src/client/LanguageModal.ts b/src/client/LanguageModal.ts index 358716727..3a03b2c93 100644 --- a/src/client/LanguageModal.ts +++ b/src/client/LanguageModal.ts @@ -1,5 +1,6 @@ import { LitElement, css, html } from "lit"; import { customElement, property } from "lit/decorators.js"; +import { translateText } from "../client/Utils"; @customElement("language-modal") export class LanguageModal extends LitElement { @@ -8,35 +9,55 @@ export class LanguageModal extends LitElement { @property({ type: String }) currentLang = "en"; static styles = css` - .modal { + .c-modal { position: fixed; - inset: 0; + padding: 1rem; + z-index: 1000; + left: 0; + bottom: 0; + right: 0; + top: 0; background-color: rgba(0, 0, 0, 0.5); - z-index: 50; + overflow-y: auto; display: flex; - justify-content: center; align-items: center; - } - .hidden { - display: none; - } - .modal-content { - background: white; - border-radius: 0.5rem; - box-shadow: 0 10px 15px rgba(0, 0, 0, 0.2); - padding: 1.5rem; - width: 24rem; - max-width: 100%; - max-height: 90vh; - display: flex; - flex-direction: column; + justify-content: center; } - .language-list { + .c-modal__wrapper { + background: #23232382; + border-radius: 8px; + min-width: 340px; + max-width: 480px; + width: 100%; + } + + .c-modal__header { + position: relative; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + font-size: 18px; + background: #000000a1; + text-align: center; + color: #fff; + padding: 1rem 2.4rem 1rem 1.4rem; + } + + .c-modal__close { + cursor: pointer; + position: absolute; + right: 1rem; + top: 1rem; + font-weight: bold; + } + + .c-modal__content { + position: relative; + color: #fff; + padding: 1.4rem; + max-height: 60dvh; overflow-y: auto; - flex: 1; - min-height: 0; - margin-bottom: 1rem; + backdrop-filter: blur(8px); } .lang-button { @@ -44,19 +65,23 @@ export class LanguageModal extends LitElement { display: flex; align-items: center; gap: 0.5rem; - padding: 0.5rem; + padding: 0.5rem 1rem; + margin-bottom: 0.5rem; border-radius: 0.375rem; transition: background-color 0.3s; - border: 1px solid #ccc; - background-color: #f8f8f8; + border: 1px solid #aaa; + background-color: #505050; + color: #fff; } .lang-button:hover { - background-color: #ebf8ff; + background-color: #969696; } .lang-button.active { - background-color: #bee3f8; + background-color: #aaaaaa; + border-color: #bbb; + color: #000; } .flag-icon { @@ -65,30 +90,44 @@ export class LanguageModal extends LitElement { object-fit: contain; } - .close-button { - background-color: #3182ce; - color: white; - padding: 0.5rem 1rem; - border-radius: 0.375rem; - cursor: pointer; - font-weight: bold; - border: none; + @keyframes rainbow { + 0% { + background-color: #990033; + } + 20% { + background-color: #996600; + } + 40% { + background-color: #336600; + } + 60% { + background-color: #008080; + } + 80% { + background-color: #1c3f99; + } + 100% { + background-color: #5e0099; + } } - .close-button:hover { - background-color: #2b6cb0; + .lang-button.debug { + animation: rainbow 10s infinite; + font-weight: bold; + color: #fff; + border: 2px dashed aqua; + box-shadow: 0 0 4px aqua; } `; - private selectLanguage(lang: string) { + private close = () => { this.dispatchEvent( - new CustomEvent("language-selected", { - detail: { lang }, + new CustomEvent("close-modal", { bubbles: true, composed: true, }), ); - } + }; updated(changedProps: Map) { if (changedProps.has("visible")) { @@ -105,18 +144,36 @@ export class LanguageModal extends LitElement { document.body.style.overflow = "auto"; } - render() { - return html` -