From 9fa3691c5c985f79512841bd28e143093607e429 Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 3 Apr 2025 19:31:42 -0700 Subject: [PATCH 01/16] bugfix: SpawnExecution couldn't find existing player because Game.Players() only returns alive players. This caused SpawnExecution to respawn each human player. --- src/core/GameRunner.ts | 6 +++++- src/core/execution/ExecutionManager.ts | 14 ++------------ src/core/execution/SpawnExecution.ts | 6 ++---- src/core/game/PlayerImpl.ts | 2 +- 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/core/GameRunner.ts b/src/core/GameRunner.ts index cc7e24b2a..13d8fcc10 100644 --- a/src/core/GameRunner.ts +++ b/src/core/GameRunner.ts @@ -25,6 +25,8 @@ import { } from "./game/GameUpdates"; import { loadTerrainMap as loadGameMap } from "./game/TerrainMapLoader"; import { ClientID, GameStartInfo, Turn } from "./Schemas"; +import { sanitize } from "./Util"; +import { fixProfaneUsername } from "./validations/username"; export async function createGameRunner( gameStart: GameStartInfo, @@ -38,7 +40,9 @@ export async function createGameRunner( (p) => new PlayerInfo( p.flag, - p.username, + p.clientID == clientID + ? sanitize(p.username) + : fixProfaneUsername(sanitize(p.username)), PlayerType.Human, p.clientID, p.playerID, diff --git a/src/core/execution/ExecutionManager.ts b/src/core/execution/ExecutionManager.ts index ad6e95d4d..968455cc1 100644 --- a/src/core/execution/ExecutionManager.ts +++ b/src/core/execution/ExecutionManager.ts @@ -1,8 +1,7 @@ import { Execution, Game, PlayerInfo, PlayerType } from "../game/Game"; import { PseudoRandom } from "../PseudoRandom"; import { ClientID, GameID, Intent, Turn } from "../Schemas"; -import { sanitize, simpleHash } from "../Util"; -import { fixProfaneUsername } from "../validations/username"; +import { simpleHash } from "../Util"; import { AllianceRequestExecution } from "./alliance/AllianceRequestExecution"; import { AllianceRequestReplyExecution } from "./alliance/AllianceRequestReplyExecution"; import { BreakAllianceExecution } from "./alliance/BreakAllianceExecution"; @@ -62,16 +61,7 @@ export class Executor { return new MoveWarshipExecution(intent.unitId, intent.tile); case "spawn": return new SpawnExecution( - new PlayerInfo( - intent.flag, - // Players see their original name, others see a sanitized version - intent.clientID == this.clientID - ? sanitize(intent.name) - : fixProfaneUsername(sanitize(intent.name)), - PlayerType.Human, - intent.clientID, - playerID, - ), + player.info(), this.mg.ref(intent.x, intent.y), ); case "boat": diff --git a/src/core/execution/SpawnExecution.ts b/src/core/execution/SpawnExecution.ts index d315a0a53..27b036eb1 100644 --- a/src/core/execution/SpawnExecution.ts +++ b/src/core/execution/SpawnExecution.ts @@ -25,10 +25,8 @@ export class SpawnExecution implements Execution { return; } - const existing = this.mg - .players() - .find((p) => p.id() == this.playerInfo.id); - if (existing) { + if (this.mg.hasPlayer(this.playerInfo.id)) { + const existing = this.mg.player(this.playerInfo.id); existing.tiles().forEach((t) => existing.relinquish(t)); getSpawnTiles(this.mg, this.tile).forEach((t) => { existing.conquer(t); diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index 9606e205b..7bdf19edc 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -99,7 +99,7 @@ export class PlayerImpl implements Player { private _smallID: number, private readonly playerInfo: PlayerInfo, startTroops: number, - private _team: Team | null, + private readonly _team: Team | null, ) { this._flag = playerInfo.flag; this._name = sanitizeUsername(playerInfo.name); From a3a05f9ed26661d69205f0cf2d5cc21ff2e1a335 Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 3 Apr 2025 20:02:22 -0700 Subject: [PATCH 02/16] bugfix: start troops were set at zero, username was not set in singleplayer --- src/client/SinglePlayerModal.ts | 10 +++++++++- src/core/game/GameImpl.ts | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/client/SinglePlayerModal.ts b/src/client/SinglePlayerModal.ts index 7faaa92c1..fbc23d0bd 100644 --- a/src/client/SinglePlayerModal.ts +++ b/src/client/SinglePlayerModal.ts @@ -11,6 +11,7 @@ import "./components/Difficulties"; import { DifficultyDescription } from "./components/Difficulties"; import "./components/Maps"; import { JoinLobbyEvent } from "./Main"; +import { UsernameInput } from "./UsernameInput"; @customElement("single-player-modal") export class SinglePlayerModal extends LitElement { @@ -326,6 +327,13 @@ export class SinglePlayerModal extends LitElement { const clientID = generateID(); const gameID = generateID(); + const usernameInput = document.querySelector( + "username-input", + ) as UsernameInput; + if (!usernameInput) { + consolex.warn("Username input element not found"); + } + this.dispatchEvent( new CustomEvent("join-lobby", { detail: { @@ -337,7 +345,7 @@ export class SinglePlayerModal extends LitElement { { playerID: generateID(), clientID, - username: "PLACEHOLDER", + username: usernameInput.getCurrentUsername(), }, ], config: { diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index fd0bb9301..dc4807ca6 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -107,7 +107,9 @@ export class GameImpl implements Game { private addHumans() { if (this.config().gameConfig().gameMode != GameMode.Team) { - this._humans.forEach((p) => this.addPlayer(p, 0)); + this._humans.forEach((p) => + this.addPlayer(p, this.config().startManpower(p)), + ); return; } const playerToTeam = assignTeams(this._humans); From 9d1b4f35e44a82485c6a30703bc0ecb3f3af2c72 Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 3 Apr 2025 20:12:36 -0700 Subject: [PATCH 03/16] bugfix: SpawnExecution was not attaching PlayerExecution to humans bugfix: WinModal was saying player died even if they hadn't spawned added hasSpawned() method to player --- src/client/graphics/layers/WinModal.ts | 3 ++- src/core/execution/SpawnExecution.ts | 28 ++++++++++++++------------ src/core/game/Game.ts | 3 +++ src/core/game/GameUpdates.ts | 1 + src/core/game/GameView.ts | 3 +++ src/core/game/PlayerImpl.ts | 11 ++++++++++ 6 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/client/graphics/layers/WinModal.ts b/src/client/graphics/layers/WinModal.ts index 6f38b380a..7b421237b 100644 --- a/src/client/graphics/layers/WinModal.ts +++ b/src/client/graphics/layers/WinModal.ts @@ -215,7 +215,8 @@ export class WinModal extends LitElement implements Layer { !this.hasShownDeathModal && myPlayer && !myPlayer.isAlive() && - !this.game.inSpawnPhase() + !this.game.inSpawnPhase() && + myPlayer.hasSpawned() ) { this.hasShownDeathModal = true; this._title = "You died"; diff --git a/src/core/execution/SpawnExecution.ts b/src/core/execution/SpawnExecution.ts index 27b036eb1..695096c9f 100644 --- a/src/core/execution/SpawnExecution.ts +++ b/src/core/execution/SpawnExecution.ts @@ -25,26 +25,28 @@ export class SpawnExecution implements Execution { return; } + let player: Player = null; if (this.mg.hasPlayer(this.playerInfo.id)) { - const existing = this.mg.player(this.playerInfo.id); - existing.tiles().forEach((t) => existing.relinquish(t)); - getSpawnTiles(this.mg, this.tile).forEach((t) => { - existing.conquer(t); - }); - return; + player = this.mg.player(this.playerInfo.id); + } else { + player = this.mg.addPlayer( + this.playerInfo, + this.mg.config().startManpower(this.playerInfo), + ); } - const player = this.mg.addPlayer( - this.playerInfo, - this.mg.config().startManpower(this.playerInfo), - ); + player.tiles().forEach((t) => player.relinquish(t)); getSpawnTiles(this.mg, this.tile).forEach((t) => { player.conquer(t); }); - this.mg.addExecution(new PlayerExecution(player.id())); - if (player.type() == PlayerType.Bot) { - this.mg.addExecution(new BotExecution(player)); + + if (!player.hasSpawned()) { + this.mg.addExecution(new PlayerExecution(player.id())); + if (player.type() == PlayerType.Bot) { + this.mg.addExecution(new BotExecution(player)); + } } + player.setHasSpawned(true); } owner(): Player { diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 1c316ed5b..002aeff18 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -316,6 +316,9 @@ export interface Player { largestClusterBoundingBox: { min: Cell; max: Cell } | null; lastTileChange(): Tick; + hasSpawned(): boolean; + setHasSpawned(hasSpawned: boolean): void; + // Territory tiles(): ReadonlySet; borderTiles(): ReadonlySet; diff --git a/src/core/game/GameUpdates.ts b/src/core/game/GameUpdates.ts index 5bb7c5693..044a2327e 100644 --- a/src/core/game/GameUpdates.ts +++ b/src/core/game/GameUpdates.ts @@ -111,6 +111,7 @@ export interface PlayerUpdate { incomingAttacks: AttackUpdate[]; outgoingAllianceRequests: PlayerID[]; stats: PlayerStats; + hasSpawned: boolean; } export interface AllianceRequestUpdate { diff --git a/src/core/game/GameView.ts b/src/core/game/GameView.ts index 430a24cb4..6bd58eb79 100644 --- a/src/core/game/GameView.ts +++ b/src/core/game/GameView.ts @@ -266,6 +266,9 @@ export class PlayerView { stats(): PlayerStats { return this.data.stats; } + hasSpawned(): boolean { + return this.data.hasSpawned; + } } export class GameView implements GameMap { diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index 7bdf19edc..e84f70d97 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -94,6 +94,8 @@ export class PlayerImpl implements Player { public _outgoingAttacks: Attack[] = []; public _outgoingLandAttacks: Attack[] = []; + private _hasSpawned = false; + constructor( private mg: GameImpl, private _smallID: number, @@ -162,6 +164,7 @@ export class PlayerImpl implements Player { ), outgoingAllianceRequests: outgoingAllianceRequests, stats: this.mg.stats().getPlayerStats(this.id()), + hasSpawned: this.hasSpawned(), }; } @@ -291,6 +294,14 @@ export class PlayerImpl implements Player { return this._tiles.size > 0; } + hasSpawned(): boolean { + return this._hasSpawned; + } + + setHasSpawned(hasSpawned: boolean): void { + this._hasSpawned = hasSpawned; + } + incomingAllianceRequests(): AllianceRequest[] { return this.mg.allianceRequests.filter((ar) => ar.recipient() == this); } From abc68bf9a437ea8046cb9c17ca9b20fb09bc3b82 Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 4 Apr 2025 08:54:09 -0700 Subject: [PATCH 04/16] rename GameStartingModal --- src/client/{gameStartingModal.ts => GameStartingModal.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/client/{gameStartingModal.ts => GameStartingModal.ts} (100%) diff --git a/src/client/gameStartingModal.ts b/src/client/GameStartingModal.ts similarity index 100% rename from src/client/gameStartingModal.ts rename to src/client/GameStartingModal.ts From 38e3e0483184077be16c46f73d1e571dc8a67375 Mon Sep 17 00:00:00 2001 From: Duwibi <86431918+Duwibi@users.noreply.github.com> Date: Fri, 4 Apr 2025 18:42:02 +0200 Subject: [PATCH 05/16] Update README.md to include instructions on how to help with translation (#420) ## Description: Updates the README.md file to include instructions on how to help with translation ## Please put your Discord username so you can be contacted if a bug or regression is found: --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 46ec42fa8..6a6734d3b 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,15 @@ Contributions are welcome! Please feel free to submit a Pull Request. 4. Push to the branch (`git push origin amazing-feature`) 5. Open a Pull Request +## 🌐 Translation + +Translators are welcome! Please feel free to help translate into your language. +How to help? +1. Go to the project's Crowdin translation page: [https://crowdin.com/project/openfrontio](https://crowdin.com/project/openfrontio) +2. Login if you already have an account/ Sign up if you don't have one +3. Select the language you want to translate in/ If your language isn't on the list, create a new topic in "Discussions" about adding the language +4. Translate the strings + ### Project Governance - The project maintainer ([evan](https://github.com/evanpelle)) has final authority on all code changes and design decisions From c700b7956f1be22599a4be8297ce3da9c889fab5 Mon Sep 17 00:00:00 2001 From: Duwibi <86431918+Duwibi@users.noreply.github.com> Date: Fri, 4 Apr 2025 18:42:26 +0200 Subject: [PATCH 06/16] Update translations (#419) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description: This PR updates the translations for: Bulgarian, Russian, Ukrainian, German, Polish and Dutch Acknowledgements for the MLS project: Bulgarian🇧🇬: Nikola123 & NewHappyRabbit Japanese🇯🇵: Aotumuri & daimyo_panda2 French🇫🇷: cldprv, gx2 & Lucasisc Dutch🇳🇱: cldprv & tryout33 German🇩🇪: Pilkey & jacks0n Spanish🇪🇸: 6uzm4n Russian🇷🇺: Rulfam Ukrainian🇺🇦: Rulfam Polish🇵🇱: zibi, RinkyDinky & Rulfam ## Please put your Discord username so you can be contacted if a bug or regression is found: --- resources/lang/bg.json | 5 +++-- resources/lang/de.json | 3 ++- resources/lang/nl.json | 5 ++++- resources/lang/pl.json | 7 +++++-- resources/lang/ru.json | 3 ++- resources/lang/uk.json | 3 ++- 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/resources/lang/bg.json b/resources/lang/bg.json index 1e4cc641e..da444e6c0 100644 --- a/resources/lang/bg.json +++ b/resources/lang/bg.json @@ -111,7 +111,8 @@ "pangaea": "Пангея", "map": "Карта", "betweentwoseas": "Между Две Морета", - "japanandneighbors": "Япония и съседи" + "japan": "Япония и съседи", + "knownworld": "Познат Свят" }, "private_lobby": { "title": "Присъединяване към частна игра", @@ -169,7 +170,7 @@ "lang_code": "bg" }, "game_mode": { - "ffa": "Свободна игра (FFA)", + "ffa": "Всеки срещу всеки (FFA)", "teams": "Отбори" } } diff --git a/resources/lang/de.json b/resources/lang/de.json index 6d65b8bcb..3b5466e09 100644 --- a/resources/lang/de.json +++ b/resources/lang/de.json @@ -111,7 +111,8 @@ "pangaea": "Pangaea", "map": "Karte", "betweentwoseas": "Zwischen zwei Meeren", - "japanandneighbors": "Japan und Nachbarländer" + "japan": "Japan und Nachbarländer", + "knownworld": "Bekannte Welt" }, "private_lobby": { "title": "Privater Lobby beitreten", diff --git a/resources/lang/nl.json b/resources/lang/nl.json index 3940c397a..428ca3204 100644 --- a/resources/lang/nl.json +++ b/resources/lang/nl.json @@ -109,7 +109,10 @@ "random": "Willekeurig", "iceland": "IJsland", "pangaea": "Pangea", - "map": "Kaart" + "map": "Kaart", + "betweentwoseas": "Tussen twee zeeën", + "japan": "Japan en buren", + "knownworld": "Bekende Wereld" }, "private_lobby": { "title": "Privélobby toetreden", diff --git a/resources/lang/pl.json b/resources/lang/pl.json index 8fdec8e07..30118ab54 100644 --- a/resources/lang/pl.json +++ b/resources/lang/pl.json @@ -109,7 +109,10 @@ "random": "Losowe", "iceland": "Islandia", "pangaea": "Pangea", - "map": "Mapa" + "map": "Mapa", + "betweentwoseas": "Między dwoma morzami", + "japan": "Japonia i sąsiedzi", + "knownworld": "Znany Świat" }, "private_lobby": { "title": "Dołącz do prywatnego Lobby", @@ -167,7 +170,7 @@ "lang_code": "pl" }, "game_mode": { - "ffa": "Darmowe dla wszystkich", + "ffa": "Każdy na Każdego", "teams": "Drużyny" } } diff --git a/resources/lang/ru.json b/resources/lang/ru.json index 42d438c35..1587414f4 100644 --- a/resources/lang/ru.json +++ b/resources/lang/ru.json @@ -111,7 +111,8 @@ "pangaea": "Пангея", "map": "Карта", "betweentwoseas": "Между двух морей", - "japanandneighbors": "Япония и соседи" + "japan": "Япония и соседи", + "knownworld": "Известный мир" }, "private_lobby": { "title": "Присоединиться к приватному лобби", diff --git a/resources/lang/uk.json b/resources/lang/uk.json index 0ff47257a..9597207e1 100644 --- a/resources/lang/uk.json +++ b/resources/lang/uk.json @@ -111,7 +111,8 @@ "pangaea": "Пангея", "map": "Мапа", "betweentwoseas": "Поміж двох морів", - "japanandneighbors": "Японія та сусіди" + "japan": "Японія та сусіди", + "knownworld": "Відомий світ" }, "private_lobby": { "title": "Приєднатися до приватного лобі", From 51bbf2becdd7b056bc7cf5ae2360910fb740dae2 Mon Sep 17 00:00:00 2001 From: Duwibi <86431918+Duwibi@users.noreply.github.com> Date: Fri, 4 Apr 2025 18:42:56 +0200 Subject: [PATCH 07/16] Fix difference in translation strings regarding the new Between Two Seas map (#416) ##Description: I renamed all instances of "TwoSeas" to "BetweenTwoSeas". That way it lowers the confusion but more importrantly - it removes the difference between the translation strings for the map. Please put your Discord username so you can be contacted if a bug or regression is found: --- resources/maps/{TwoSeas.bin => BetweenTwoSeas.bin} | 0 .../maps/{TwoSeas.json => BetweenTwoSeas.json} | 2 +- resources/maps/{TwoSeas.png => BetweenTwoSeas.png} | Bin .../{TwoSeasMini.bin => BetweenTwoSeasMini.bin} | 0 .../{TwoSeasThumb.webp => BetweenTwoSeasThumb.webp} | Bin src/client/components/Maps.ts | 2 +- src/client/utilities/Maps.ts | 6 +++--- src/core/configuration/DefaultConfig.ts | 8 +++++--- src/core/game/Game.ts | 2 +- src/core/game/TerrainMapFileLoader.ts | 2 +- src/scripts/generateTerrainMaps.ts | 2 +- src/server/MapPlaylist.ts | 2 +- 12 files changed, 14 insertions(+), 12 deletions(-) rename resources/maps/{TwoSeas.bin => BetweenTwoSeas.bin} (100%) rename resources/maps/{TwoSeas.json => BetweenTwoSeas.json} (98%) rename resources/maps/{TwoSeas.png => BetweenTwoSeas.png} (100%) rename resources/maps/{TwoSeasMini.bin => BetweenTwoSeasMini.bin} (100%) rename resources/maps/{TwoSeasThumb.webp => BetweenTwoSeasThumb.webp} (100%) diff --git a/resources/maps/TwoSeas.bin b/resources/maps/BetweenTwoSeas.bin similarity index 100% rename from resources/maps/TwoSeas.bin rename to resources/maps/BetweenTwoSeas.bin diff --git a/resources/maps/TwoSeas.json b/resources/maps/BetweenTwoSeas.json similarity index 98% rename from resources/maps/TwoSeas.json rename to resources/maps/BetweenTwoSeas.json index 38bdaa90d..ea35ffa16 100644 --- a/resources/maps/TwoSeas.json +++ b/resources/maps/BetweenTwoSeas.json @@ -1,5 +1,5 @@ { - "name": "TwoSeas", + "name": "BetweenTwoSeas", "width": 1778, "height": 1062, "nations": [ diff --git a/resources/maps/TwoSeas.png b/resources/maps/BetweenTwoSeas.png similarity index 100% rename from resources/maps/TwoSeas.png rename to resources/maps/BetweenTwoSeas.png diff --git a/resources/maps/TwoSeasMini.bin b/resources/maps/BetweenTwoSeasMini.bin similarity index 100% rename from resources/maps/TwoSeasMini.bin rename to resources/maps/BetweenTwoSeasMini.bin diff --git a/resources/maps/TwoSeasThumb.webp b/resources/maps/BetweenTwoSeasThumb.webp similarity index 100% rename from resources/maps/TwoSeasThumb.webp rename to resources/maps/BetweenTwoSeasThumb.webp diff --git a/src/client/components/Maps.ts b/src/client/components/Maps.ts index 1ff02fe1c..f4d419185 100644 --- a/src/client/components/Maps.ts +++ b/src/client/components/Maps.ts @@ -21,7 +21,7 @@ export const MapDescription: Record = { Australia: "Australia", Iceland: "Iceland", Japan: "Japan", - TwoSeas: "Between Two Seas", + BetweenTwoSeas: "Between Two Seas", KnownWorld: "Known World", }; diff --git a/src/client/utilities/Maps.ts b/src/client/utilities/Maps.ts index d3be87796..a616f7310 100644 --- a/src/client/utilities/Maps.ts +++ b/src/client/utilities/Maps.ts @@ -1,6 +1,7 @@ import africa from "../../../resources/maps/AfricaThumb.webp"; import asia from "../../../resources/maps/AsiaThumb.webp"; import australia from "../../../resources/maps/AustraliaThumb.webp"; +import betweenTwoSeas from "../../../resources/maps/BetweenTwoSeasThumb.webp"; import blackSea from "../../../resources/maps/BlackSeaThumb.webp"; import britannia from "../../../resources/maps/BritanniaThumb.webp"; import europe from "../../../resources/maps/EuropeThumb.webp"; @@ -14,7 +15,6 @@ import northAmerica from "../../../resources/maps/NorthAmericaThumb.webp"; import oceania from "../../../resources/maps/OceaniaThumb.webp"; import pangaea from "../../../resources/maps/PangaeaThumb.webp"; import southAmerica from "../../../resources/maps/SouthAmericaThumb.webp"; -import twoSeas from "../../../resources/maps/TwoSeasThumb.webp"; import world from "../../../resources/maps/WorldMapThumb.webp"; import { GameMapType } from "../../core/game/Game"; @@ -53,8 +53,8 @@ export function getMapsImage(map: GameMapType): string { return iceland; case GameMapType.Japan: return japan; - case GameMapType.TwoSeas: - return twoSeas; + case GameMapType.BetweenTwoSeas: + return betweenTwoSeas; case GameMapType.KnownWorld: return knownworld; default: diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index b11aa63ec..0fa8f52b1 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -95,9 +95,11 @@ export abstract class DefaultServerConfig implements ServerConfig { } // Maps smaller than ~2 mil pixels if ( - [GameMapType.TwoSeas, GameMapType.BlackSea, GameMapType.Pangaea].includes( - map, - ) + [ + GameMapType.BetweenTwoSeas, + GameMapType.BlackSea, + GameMapType.Pangaea, + ].includes(map) ) { return Math.random() < 0.2 ? 60 : 35; } diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 002aeff18..f5e51c15c 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -60,7 +60,7 @@ export enum GameMapType { Australia = "Australia", Iceland = "Iceland", Japan = "Japan", - TwoSeas = "Between Two Seas", + BetweenTwoSeas = "Between Two Seas", KnownWorld = "Known World", } diff --git a/src/core/game/TerrainMapFileLoader.ts b/src/core/game/TerrainMapFileLoader.ts index db730de11..636709968 100644 --- a/src/core/game/TerrainMapFileLoader.ts +++ b/src/core/game/TerrainMapFileLoader.ts @@ -39,7 +39,7 @@ const MAP_FILE_NAMES: Record = { [GameMapType.Australia]: "Australia", [GameMapType.Iceland]: "Iceland", [GameMapType.Japan]: "Japan", - [GameMapType.TwoSeas]: "TwoSeas", + [GameMapType.BetweenTwoSeas]: "BetweenTwoSeas", [GameMapType.KnownWorld]: "KnownWorld", }; diff --git a/src/scripts/generateTerrainMaps.ts b/src/scripts/generateTerrainMaps.ts index a63dae382..7f83de7d1 100644 --- a/src/scripts/generateTerrainMaps.ts +++ b/src/scripts/generateTerrainMaps.ts @@ -19,7 +19,7 @@ const maps = [ "Australia", "Pangaea", "Iceland", - "TwoSeas", + "BetweenTwoSeas", "Japan", "KnownWorld", ]; diff --git a/src/server/MapPlaylist.ts b/src/server/MapPlaylist.ts index 488d48aea..5fdd442f8 100644 --- a/src/server/MapPlaylist.ts +++ b/src/server/MapPlaylist.ts @@ -88,7 +88,7 @@ export class MapPlaylist { Pangaea: 1, Asia: 1, Mars: 1, - TwoSeas: 3, + BetweenTwoSeas: 3, Japan: 3, BlackSea: 1, }; From 924ca2c69eaafdc5d72f74b4b7add8fc5cf4bdd1 Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 4 Apr 2025 10:03:00 -0700 Subject: [PATCH 08/16] bugfix: humans start with zero troops on teams, also removed manpower argument to addPlayer() --- src/core/execution/SpawnExecution.ts | 5 +---- src/core/game/Game.ts | 2 +- src/core/game/GameImpl.ts | 14 ++++---------- tests/Attack.test.ts | 4 ++-- tests/MissileSilo.test.ts | 2 +- tests/SAM.test.ts | 4 ++-- tests/TerritoryCapture.test.ts | 1 - tests/Warship.test.ts | 4 ++-- 8 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/core/execution/SpawnExecution.ts b/src/core/execution/SpawnExecution.ts index 695096c9f..74e1df782 100644 --- a/src/core/execution/SpawnExecution.ts +++ b/src/core/execution/SpawnExecution.ts @@ -29,10 +29,7 @@ export class SpawnExecution implements Execution { if (this.mg.hasPlayer(this.playerInfo.id)) { player = this.mg.player(this.playerInfo.id); } else { - player = this.mg.addPlayer( - this.playerInfo, - this.mg.config().startManpower(this.playerInfo), - ); + player = this.mg.addPlayer(this.playerInfo); } player.tiles().forEach((t) => player.relinquish(t)); diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index f5e51c15c..5dd3087dc 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -433,7 +433,7 @@ export interface Game extends GameMap { playerByClientID(id: ClientID): Player | null; playerBySmallID(id: number): Player | TerraNullius; hasPlayer(id: PlayerID): boolean; - addPlayer(playerInfo: PlayerInfo, manpower: number): Player; + addPlayer(playerInfo: PlayerInfo): Player; terraNullius(): TerraNullius; owner(ref: TileRef): Player | TerraNullius; diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index dc4807ca6..bd13ceeac 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -107,9 +107,7 @@ export class GameImpl implements Game { private addHumans() { if (this.config().gameConfig().gameMode != GameMode.Team) { - this._humans.forEach((p) => - this.addPlayer(p, this.config().startManpower(p)), - ); + this._humans.forEach((p) => this.addPlayer(p)); return; } const playerToTeam = assignTeams(this._humans); @@ -118,7 +116,7 @@ export class GameImpl implements Game { console.warn(`Player ${playerInfo.name} was kicked from team`); continue; } - this.addPlayer(playerInfo, 0, team); + this.addPlayer(playerInfo, team); } } @@ -348,16 +346,12 @@ export class GameImpl implements Game { return this.player(id); } - addPlayer( - playerInfo: PlayerInfo, - manpower: number, - team: Team = null, - ): Player { + addPlayer(playerInfo: PlayerInfo, team: Team = null): Player { const player = new PlayerImpl( this, this.nextPlayerID, playerInfo, - manpower, + this.config().startManpower(playerInfo), team ?? this.maybeAssignTeam(playerInfo), ); this._playersBySmallID.push(player); diff --git a/tests/Attack.test.ts b/tests/Attack.test.ts index 1fa3d1b41..f9c46bcc3 100644 --- a/tests/Attack.test.ts +++ b/tests/Attack.test.ts @@ -39,7 +39,7 @@ describe("Attack", () => { null, "attacker_id", ); - game.addPlayer(attackerInfo, 1000); + game.addPlayer(attackerInfo); const defenderInfo = new PlayerInfo( "us", "defender dude", @@ -47,7 +47,7 @@ describe("Attack", () => { null, "defender_id", ); - game.addPlayer(defenderInfo, 1000); + game.addPlayer(defenderInfo); defenderSpawn = game.ref(0, 15); attackerSpawn = game.ref(0, 10); diff --git a/tests/MissileSilo.test.ts b/tests/MissileSilo.test.ts index 1c7dd44b3..e8e926b3d 100644 --- a/tests/MissileSilo.test.ts +++ b/tests/MissileSilo.test.ts @@ -38,7 +38,7 @@ describe("MissileSilo", () => { null, "attacker_id", ); - game.addPlayer(attacker_info, 1000); + game.addPlayer(attacker_info); game.addExecution( new SpawnExecution(game.player(attacker_info.id).info(), game.ref(1, 1)), diff --git a/tests/SAM.test.ts b/tests/SAM.test.ts index 83a59ef4f..4f63f9274 100644 --- a/tests/SAM.test.ts +++ b/tests/SAM.test.ts @@ -50,8 +50,8 @@ describe("SAM", () => { null, "attacker_id", ); - game.addPlayer(defender_info, 1000); - game.addPlayer(attacker_info, 1000); + game.addPlayer(defender_info); + game.addPlayer(attacker_info); game.addExecution( new SpawnExecution(game.player(defender_info.id).info(), game.ref(1, 1)), diff --git a/tests/TerritoryCapture.test.ts b/tests/TerritoryCapture.test.ts index a32aeb6b7..aee96aec6 100644 --- a/tests/TerritoryCapture.test.ts +++ b/tests/TerritoryCapture.test.ts @@ -7,7 +7,6 @@ describe("Territory management", () => { const game = await setup("Plains"); game.addPlayer( new PlayerInfo("us", "test_player", PlayerType.Human, null, "test_id"), - 1000, ); const spawnTile = game.map().ref(50, 50); game.addExecution( diff --git a/tests/Warship.test.ts b/tests/Warship.test.ts index e7adcd47e..c1ca61ecf 100644 --- a/tests/Warship.test.ts +++ b/tests/Warship.test.ts @@ -27,7 +27,7 @@ describe("Warship", () => { null, "player_1_id", ); - game.addPlayer(player_1_info, 1000); + game.addPlayer(player_1_info); const player_2_info = new PlayerInfo( "us", "boat dude", @@ -35,7 +35,7 @@ describe("Warship", () => { null, "player_2_id", ); - game.addPlayer(player_2_info, 1000); + game.addPlayer(player_2_info); game.addExecution( new SpawnExecution( From a97a608dce441e7472b06cf22c38448bc135fd24 Mon Sep 17 00:00:00 2001 From: kanekane0448 Date: Fri, 4 Apr 2025 19:15:59 +0200 Subject: [PATCH 09/16] Add Alt + Click hotkey for sending emotes (#408) ## Description: Allows the player to Alt + Click to send emotes to other players or themself in order to ease communication. Of course, the hotkey can be something different. Alt + Click is just a suggestion. ![image](https://github.com/user-attachments/assets/9762c4f0-f62d-4c39-ba35-468ac3f5ddaa) ## 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: kanekane0448 PS: The new emoji table looks really good! --------- Co-authored-by: kanekane0448 --- resources/lang/en.json | 1 + src/client/HelpModal.ts | 4 +++ src/client/InputHandler.ts | 10 ++++++ src/client/Main.ts | 2 +- src/client/graphics/GameRenderer.ts | 7 ++++- src/client/graphics/layers/EmojiTable.ts | 40 ++++++++++++++++++++++++ 6 files changed, 62 insertions(+), 2 deletions(-) diff --git a/resources/lang/en.json b/resources/lang/en.json index cedf01779..024878fad 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -22,6 +22,7 @@ "action_alt_view": "Alternate view (terrain/countries)", "action_attack_altclick": "Attack (when left click is set to open menu)", "action_build": "Open build menu", + "action_emote": "Open emote menu", "action_center": "Center camera on player", "action_zoom": "Zoom out/in", "action_move_camera": "Move camera", diff --git a/src/client/HelpModal.ts b/src/client/HelpModal.ts index bbf842eb9..8de819377 100644 --- a/src/client/HelpModal.ts +++ b/src/client/HelpModal.ts @@ -46,6 +46,10 @@ export class HelpModal extends LitElement { Ctrl + left click ${translateText("help_modal.action_build")} + + Alt + left click + ${translateText("help_modal.action_emote")} + C ${translateText("help_modal.action_center")} diff --git a/src/client/InputHandler.ts b/src/client/InputHandler.ts index 806bc6cf8..ae495e9fe 100644 --- a/src/client/InputHandler.ts +++ b/src/client/InputHandler.ts @@ -69,6 +69,12 @@ export class ShowBuildMenuEvent implements GameEvent { public readonly y: number, ) {} } +export class ShowEmojiMenuEvent implements GameEvent { + constructor( + public readonly x: number, + public readonly y: number, + ) {} +} export class AttackRatioEvent implements GameEvent { constructor(public readonly attackRatio: number) {} @@ -292,6 +298,10 @@ export class InputHandler { this.eventBus.emit(new ShowBuildMenuEvent(event.clientX, event.clientY)); return; } + if (event.altKey) { + this.eventBus.emit(new ShowEmojiMenuEvent(event.clientX, event.clientY)); + return; + } const dist = Math.abs(event.x - this.lastPointerDownX) + diff --git a/src/client/Main.ts b/src/client/Main.ts index 6b4b623c9..e2637e4ae 100644 --- a/src/client/Main.ts +++ b/src/client/Main.ts @@ -10,7 +10,6 @@ import "./DarkModeButton"; import { DarkModeButton } from "./DarkModeButton"; import "./FlagInput"; import { FlagInput } from "./FlagInput"; -import { GameStartingModal } from "./GameStartingModal"; import "./GoogleAdElement"; import GoogleAdElement from "./GoogleAdElement"; import { HelpModal } from "./HelpModal"; @@ -27,6 +26,7 @@ import { UsernameInput } from "./UsernameInput"; import { generateCryptoRandomUUID } from "./Utils"; import "./components/baseComponents/Button"; import "./components/baseComponents/Modal"; +import { GameStartingModal } from "./gameStartingModal"; import "./styles.css"; export interface JoinLobbyEvent { diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index 5f76b79e7..9207067b4 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -2,8 +2,8 @@ import { consolex } from "../../core/Consolex"; import { EventBus } from "../../core/EventBus"; import { ClientID } from "../../core/Schemas"; import { GameView } from "../../core/game/GameView"; -import { GameStartingModal } from "../GameStartingModal"; import { RefreshGraphicsEvent as RedrawGraphicsEvent } from "../InputHandler"; +import { GameStartingModal } from "../gameStartingModal"; import { TransformHandler } from "./TransformHandler"; import { UIState } from "./UIState"; import { BuildMenu } from "./layers/BuildMenu"; @@ -47,6 +47,11 @@ export function createRenderer( if (!emojiTable || !(emojiTable instanceof EmojiTable)) { consolex.error("EmojiTable element not found in the DOM"); } + emojiTable.eventBus = eventBus; + emojiTable.transformHandler = transformHandler; + emojiTable.game = game; + emojiTable.initEventBus(); + const buildMenu = document.querySelector("build-menu") as BuildMenu; if (!buildMenu || !(buildMenu instanceof BuildMenu)) { consolex.error("BuildMenu element not found in the DOM"); diff --git a/src/client/graphics/layers/EmojiTable.ts b/src/client/graphics/layers/EmojiTable.ts index 06edeecb0..5db92c866 100644 --- a/src/client/graphics/layers/EmojiTable.ts +++ b/src/client/graphics/layers/EmojiTable.ts @@ -1,5 +1,12 @@ import { LitElement, css, html } from "lit"; import { customElement, state } from "lit/decorators.js"; +import { EventBus } from "../../../core/EventBus"; +import { AllPlayers } from "../../../core/game/Game"; +import { GameView, PlayerView } from "../../../core/game/GameView"; +import { TerraNulliusImpl } from "../../../core/game/TerraNulliusImpl"; +import { ShowEmojiMenuEvent } from "../../InputHandler"; +import { SendEmojiIntentEvent } from "../../Transport"; +import { TransformHandler } from "../TransformHandler"; const emojiTable: string[][] = [ ["😀", "😊", "🥰", "😇", "😎"], @@ -17,6 +24,10 @@ const emojiTable: string[][] = [ @customElement("emoji-table") export class EmojiTable extends LitElement { + public eventBus: EventBus; + public transformHandler: TransformHandler; + public game: GameView; + static styles = css` :host { display: block; @@ -96,6 +107,35 @@ export class EmojiTable extends LitElement { @state() private _hidden = true; + initEventBus() { + this.eventBus.on(ShowEmojiMenuEvent, (e) => { + const cell = this.transformHandler.screenToWorldCoordinates(e.x, e.y); + if (!this.game.isValidCoord(cell.x, cell.y)) { + return; + } + + const tile = this.game.ref(cell.x, cell.y); + if (!this.game.hasOwner(tile)) { + return; + } + + const targetPlayer = this.game.owner(tile); + // maybe redundant due to owner check but better safe than sorry + if (targetPlayer instanceof TerraNulliusImpl) { + return; + } + + this.showTable((emoji) => { + const recipient = + targetPlayer == this.game.myPlayer() + ? AllPlayers + : (targetPlayer as PlayerView); + this.eventBus.emit(new SendEmojiIntentEvent(recipient, emoji)); + this.hideTable(); + }); + }); + } + private onEmojiClicked: (emoji: string) => void = () => {}; render() { From 9fe64c15a8884035da31358e2411b8b795f4c000 Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 4 Apr 2025 10:19:54 -0700 Subject: [PATCH 10/16] update GameStartingModal imports to fix build --- src/client/Main.ts | 2 +- src/client/graphics/GameRenderer.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/Main.ts b/src/client/Main.ts index e2637e4ae..6b4b623c9 100644 --- a/src/client/Main.ts +++ b/src/client/Main.ts @@ -10,6 +10,7 @@ import "./DarkModeButton"; import { DarkModeButton } from "./DarkModeButton"; import "./FlagInput"; import { FlagInput } from "./FlagInput"; +import { GameStartingModal } from "./GameStartingModal"; import "./GoogleAdElement"; import GoogleAdElement from "./GoogleAdElement"; import { HelpModal } from "./HelpModal"; @@ -26,7 +27,6 @@ import { UsernameInput } from "./UsernameInput"; import { generateCryptoRandomUUID } from "./Utils"; import "./components/baseComponents/Button"; import "./components/baseComponents/Modal"; -import { GameStartingModal } from "./gameStartingModal"; import "./styles.css"; export interface JoinLobbyEvent { diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index 9207067b4..fe743ef54 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -2,8 +2,8 @@ import { consolex } from "../../core/Consolex"; import { EventBus } from "../../core/EventBus"; import { ClientID } from "../../core/Schemas"; import { GameView } from "../../core/game/GameView"; +import { GameStartingModal } from "../GameStartingModal"; import { RefreshGraphicsEvent as RedrawGraphicsEvent } from "../InputHandler"; -import { GameStartingModal } from "../gameStartingModal"; import { TransformHandler } from "./TransformHandler"; import { UIState } from "./UIState"; import { BuildMenu } from "./layers/BuildMenu"; From 190a67b72aa3ca348962882f0be6657fbb951d36 Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 4 Apr 2025 12:11:10 -0700 Subject: [PATCH 11/16] delete tradeship if path not found --- src/core/execution/TradeShipExecution.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/execution/TradeShipExecution.ts b/src/core/execution/TradeShipExecution.ts index 34e0f6a56..99663faad 100644 --- a/src/core/execution/TradeShipExecution.ts +++ b/src/core/execution/TradeShipExecution.ts @@ -111,6 +111,9 @@ export class TradeShipExecution implements Execution { break; case PathFindResultType.PathNotFound: consolex.warn("captured trade ship cannot find route"); + if (this.tradeShip.isActive()) { + this.tradeShip.delete(false); + } this.active = false; break; } From 2cb8c239cf0763f3971d99914be5b60682a554de Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 4 Apr 2025 16:04:57 -0700 Subject: [PATCH 12/16] make sendBoat() more efficient: find random tiles instead of doing a breadth first search --- src/core/execution/FakeHumanExecution.ts | 99 ++++++++++-------------- 1 file changed, 43 insertions(+), 56 deletions(-) diff --git a/src/core/execution/FakeHumanExecution.ts b/src/core/execution/FakeHumanExecution.ts index 74ce32f6b..12356920e 100644 --- a/src/core/execution/FakeHumanExecution.ts +++ b/src/core/execution/FakeHumanExecution.ts @@ -15,7 +15,7 @@ import { Tick, UnitType, } from "../game/Game"; -import { andFN, manhattanDistFN, TileRef } from "../game/GameMap"; +import { manhattanDistFN, TileRef } from "../game/GameMap"; import { PseudoRandom } from "../PseudoRandom"; import { GameID } from "../Schemas"; import { calculateBoundingBox, simpleHash } from "../Util"; @@ -151,12 +151,12 @@ export class FakeHumanExecution implements Execution { if (enemyborder.length == 0) { if (this.random.chance(5)) { - this.sendBoat(); + this.sendBoatRandomly(); } return; } if (this.random.chance(10)) { - this.sendBoat(); + this.sendBoatRandomly(); return; } @@ -503,58 +503,30 @@ export class FakeHumanExecution implements Execution { ); } - sendBoat(tries: number = 0, oceanShore: TileRef[] = null) { - if (tries > 10) { - return; - } - - if (oceanShore == null) { - oceanShore = Array.from(this.player.borderTiles()).filter((t) => - this.mg.isOceanShore(t), - ); - } + sendBoatRandomly() { + const oceanShore = Array.from(this.player.borderTiles()).filter((t) => + this.mg.isOceanShore(t), + ); if (oceanShore.length == 0) { return; } const src = this.random.randElement(oceanShore); - const otherShore = Array.from( - this.mg.bfs( - src, - andFN( - (gm, t) => gm.isOcean(t) || gm.isOceanShore(t), - manhattanDistFN(src, 200), - ), + + const dst = this.randOceanShoreTile(src, 250); + if (dst == null) { + return; + } + + this.mg.addExecution( + new TransportShipExecution( + this.player.id(), + this.mg.owner(dst).id(), + dst, + this.player.troops() / 5, ), - ).filter((t) => this.mg.isOceanShore(t) && this.mg.owner(t) != this.player); - - if (otherShore.length == 0) { - return; - } - - for (let i = 0; i < 20; i++) { - const dst = this.random.randElement(otherShore); - if (this.isSmallIsland(dst)) { - continue; - } - if ( - this.mg.owner(dst).isPlayer() && - this.player.isFriendly(this.mg.owner(dst) as Player) - ) { - continue; - } - - this.mg.addExecution( - new TransportShipExecution( - this.player.id(), - this.mg.hasOwner(dst) ? this.mg.owner(dst).id() : null, - dst, - this.player.troops() / 5, - ), - ); - return; - } - this.sendBoat(tries + 1, oceanShore); + ); + return; } randomLand(): TileRef | null { @@ -593,13 +565,28 @@ export class FakeHumanExecution implements Execution { ); } - isSmallIsland(tile: TileRef): boolean { - return ( - this.mg.bfs( - tile, - andFN((gm, t) => gm.isLand(t), manhattanDistFN(tile, 10)), - ).size < 50 - ); + private randOceanShoreTile(tile: TileRef, dist: number): TileRef | null { + const x = this.mg.x(tile); + const y = this.mg.y(tile); + for (let i = 0; i < 500; i++) { + const randX = this.random.nextInt(x - dist, x + dist); + const randY = this.random.nextInt(y - dist, y + dist); + if (!this.mg.isValidCoord(randX, randY)) { + continue; + } + const randTile = this.mg.ref(randX, randY); + if (!this.mg.isOceanShore(randTile)) { + continue; + } + const owner = this.mg.owner(randTile); + if (!owner.isPlayer()) { + return randTile; + } + if (!owner.isFriendly(this.player)) { + return randTile; + } + } + return null; } owner(): Player { From b947b764b7f8f930a9c3d6ee9a3fa86ce8d0c41a Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 4 Apr 2025 16:10:16 -0700 Subject: [PATCH 13/16] bugfix: npe when winner is null --- src/server/Archive.ts | 6 +++--- src/server/GameServer.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/server/Archive.ts b/src/server/Archive.ts index b9f280879..b466fd929 100644 --- a/src/server/Archive.ts +++ b/src/server/Archive.ts @@ -10,10 +10,10 @@ const log = logger.child({ component: "Archive" }); // R2 client configuration const r2 = new S3({ region: "auto", // R2 ignores region, but it's required by the SDK - endpoint: config.r2Endpoint(), // You'll need to add this to your config + endpoint: config.r2Endpoint(), credentials: { - accessKeyId: config.r2AccessKey(), // You'll need to add these - secretAccessKey: config.r2SecretKey(), // credential methods to your config + accessKeyId: config.r2AccessKey(), + secretAccessKey: config.r2SecretKey(), }, }); diff --git a/src/server/GameServer.ts b/src/server/GameServer.ts index bf3db8566..54cf9a329 100644 --- a/src/server/GameServer.ts +++ b/src/server/GameServer.ts @@ -369,8 +369,8 @@ export class GameServer { this.turns, this._startTime, Date.now(), - this.winner.winner, - this.winner.winnerType, + this.winner?.winner, + this.winner?.winnerType, this.allPlayersStats, ), ); From e9c885644b37e25c9f3f92544be3a5efdde24505 Mon Sep 17 00:00:00 2001 From: Duwibi <86431918+Duwibi@users.noreply.github.com> Date: Sat, 5 Apr 2025 01:44:32 +0200 Subject: [PATCH 14/16] Add English flag (#418) ## Description: This PR adds a 50/50 English/US flag that represents the English language in the language selection menu. ## Please put your Discord username so you can be contacted if a bug or regression is found: --- resources/flags/uk_us_flag.svg | 112 +++++++++++++++++++++++++++++++++ resources/lang/en.json | 2 +- 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 resources/flags/uk_us_flag.svg diff --git a/resources/flags/uk_us_flag.svg b/resources/flags/uk_us_flag.svg new file mode 100644 index 000000000..a4f09a725 --- /dev/null +++ b/resources/flags/uk_us_flag.svg @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/lang/en.json b/resources/lang/en.json index 024878fad..06ae51508 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -2,7 +2,7 @@ "lang": { "en": "English", "native": "English", - "svg": "xx", + "svg": "uk_us_flag", "lang_code": "en" }, "main": { From 1435be00c57c850c67c2d3b4157aec0ae41768b0 Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 4 Apr 2025 16:56:13 -0700 Subject: [PATCH 15/16] bugfix: bots slider invisible on firefox --- src/client/styles.css | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/client/styles.css b/src/client/styles.css index 9f93d919e..b7c531151 100644 --- a/src/client/styles.css +++ b/src/client/styles.css @@ -240,28 +240,53 @@ label.option-card:hover { #private-lobby-bots-count { width: 80%; height: 16px; + -webkit-appearance: none; + -moz-appearance: none; appearance: none; } +/* Firefox */ +#bots-count::-moz-range-track, +#private-lobby-bots-count::-moz-range-track { + height: 8px; + background: white; +} + +#bots-count::-moz-range-progress, +#private-lobby-bots-count::-moz-range-progress { + height: 8px; + background-color: #0075ff; +} + +#bots-count::-moz-range-thumb, +#private-lobby-bots-count::-moz-range-thumb { + height: 16px; + width: 16px; + background: #0075ff; + border: none; + border-radius: 50%; +} + +/* Chrome */ #bots-count::-webkit-slider-runnable-track, #private-lobby-bots-count::-webkit-slider-runnable-track { - appearance: none; + height: 8px; background: linear-gradient( to right, #0075ff var(--progress, 0%), white var(--progress, 0%) ); - height: 8px; } #bots-count::-webkit-slider-thumb, #private-lobby-bots-count::-webkit-slider-thumb { -webkit-appearance: none; - appearance: none; + height: 16px; + width: 16px; background: #0075ff; - border-color: #0075ff; - position: relative; - top: -3px; + border: none; + border-radius: 50%; + margin-top: -4px; } .random-map { From 9837401a1b435ee38b6c50588847a51a318c2e46 Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 4 Apr 2025 17:02:19 -0700 Subject: [PATCH 16/16] make atom bombs 500k --- 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 0fa8f52b1..7d2d7b99c 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -268,7 +268,7 @@ export class DefaultConfig implements Config { case UnitType.AtomBomb: return { cost: (p: Player) => - p.type() == PlayerType.Human && this.infiniteGold() ? 0 : 750_000, + p.type() == PlayerType.Human && this.infiniteGold() ? 0 : 500_000, territoryBound: false, }; case UnitType.HydrogenBomb: