From 4a0ce7128e80fe9671577038efb22284ac04420e Mon Sep 17 00:00:00 2001 From: FloPinguin <25036848+FloPinguin@users.noreply.github.com> Date: Sat, 17 Jan 2026 16:02:32 +0100 Subject: [PATCH] Fix for v29: Add nation count loading for JoinPrivateLobbyModal; change HvN difficulty (#2941) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description: 1. In JoinPrivateLobbyModal the nation count loading was missing. That caused the team preview UI to show different player counts compared to the HostLobbyModal. For example it showed 0/0 nations for the HumansVsNations team mode (instead of 2/2): Screenshot 2026-01-16 211337 2. Turn down HvN difficulty from Impossible to Hard. We steamrolled over Hard nations in the playtest (at least in two of the three games) because we donated lots of troops to each other. But after some API data research I noticed that only 33% of players in public team games ever use the donate functionality. And we probably have less skilled players in public games than in the playtest. So its probably better to use the Hard difficulty to ensure balanced gameplay. I know, I'm overthinking this 😂 ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin --- src/client/HostLobbyModal.ts | 69 ++++++------------- src/client/JoinPrivateLobbyModal.ts | 66 ++++++++++++------ .../{LobbyTeamView.ts => LobbyPlayerView.ts} | 57 ++++++++++++--- src/client/styles.css | 1 - src/server/MapPlaylist.ts | 4 +- 5 files changed, 115 insertions(+), 82 deletions(-) rename src/client/components/{LobbyTeamView.ts => LobbyPlayerView.ts} (83%) diff --git a/src/client/HostLobbyModal.ts b/src/client/HostLobbyModal.ts index 2ca75b207..03e0a6ed0 100644 --- a/src/client/HostLobbyModal.ts +++ b/src/client/HostLobbyModal.ts @@ -14,7 +14,6 @@ import { UnitType, mapCategories, } from "../core/game/Game"; -import { getCompactMapNationCount } from "../core/game/NationCreation"; import { ClientInfo, GameConfig, @@ -28,7 +27,7 @@ import { BaseModal } from "./components/BaseModal"; import "./components/CopyButton"; import "./components/Difficulties"; import "./components/FluentSlider"; -import "./components/LobbyTeamView"; +import "./components/LobbyPlayerView"; import "./components/Maps"; import { modalHeader } from "./components/ui/ModalHeader"; import { crazyGamesSDK } from "./CrazyGamesSDK"; @@ -851,33 +850,16 @@ export class HostLobbyModal extends BaseModal { -
-
-
- ${this.clients.length} - ${this.clients.length === 1 - ? translateText("host_modal.player") - : translateText("host_modal.players")} - • - ${this.getEffectiveNationCount()} - ${this.getEffectiveNationCount() === 1 - ? translateText("host_modal.nation_player") - : translateText("host_modal.nation_players")} -
-
- - this.kickPlayer(clientID)} - > -
+ this.kickPlayer(clientID)} + > @@ -1340,31 +1322,22 @@ export class HostLobbyModal extends BaseModal { } private async loadNationCount() { + const currentMap = this.selectedMap; try { - const mapData = this.mapLoader.getMapData(this.selectedMap); + const mapData = this.mapLoader.getMapData(currentMap); const manifest = await mapData.manifest(); - this.nationCount = manifest.nations.length; + // Only update if the map hasn't changed + if (this.selectedMap === currentMap) { + this.nationCount = manifest.nations.length; + } } catch (error) { console.warn("Failed to load nation count", error); - this.nationCount = 0; + // Only update if the map hasn't changed + if (this.selectedMap === currentMap) { + this.nationCount = 0; + } } } - - /** - * Returns the effective nation count for display purposes. - * In HumansVsNations mode, this equals the number of human players. - * For compact maps, only 25% of nations are used. - * Otherwise, it uses the manifest nation count (or 0 if nations are disabled). - */ - private getEffectiveNationCount(): number { - if (this.disableNations) { - return 0; - } - if (this.gameMode === GameMode.Team && this.teamCount === HumansVsNations) { - return this.clients.length; - } - return getCompactMapNationCount(this.nationCount, this.compactMap); - } } async function createLobby(creatorClientID: string): Promise { diff --git a/src/client/JoinPrivateLobbyModal.ts b/src/client/JoinPrivateLobbyModal.ts index 117e6d753..c04b8fa93 100644 --- a/src/client/JoinPrivateLobbyModal.ts +++ b/src/client/JoinPrivateLobbyModal.ts @@ -10,13 +10,14 @@ import { } from "../core/Schemas"; import { generateID } from "../core/Util"; import { getServerConfigFromClient } from "../core/configuration/ConfigLoader"; -import { GameMode } from "../core/game/Game"; +import { GameMapSize, GameMode } from "../core/game/Game"; import { getApiBase } from "./Api"; import { JoinLobbyEvent } from "./Main"; +import { terrainMapFileLoader } from "./TerrainMapFileLoader"; import { BaseModal } from "./components/BaseModal"; import "./components/CopyButton"; import "./components/Difficulties"; -import "./components/LobbyTeamView"; +import "./components/LobbyPlayerView"; import { modalHeader } from "./components/ui/ModalHeader"; @customElement("join-private-lobby-modal") export class JoinPrivateLobbyModal extends BaseModal { @@ -27,8 +28,10 @@ export class JoinPrivateLobbyModal extends BaseModal { @state() private gameConfig: GameConfig | null = null; @state() private lobbyCreatorClientID: string | null = null; @state() private currentLobbyId: string = ""; + @state() private nationCount: number = 0; private playersInterval: NodeJS.Timeout | null = null; + private mapLoader = terrainMapFileLoader; private leaveLobbyOnClose = true; @@ -93,26 +96,17 @@ export class JoinPrivateLobbyModal extends BaseModal { ${this.renderGameConfig()} ${this.hasJoined && this.players.length > 0 ? html` -
-
-
- ${this.players.length} - ${this.players.length === 1 - ? translateText("private_lobby.player") - : translateText("private_lobby.players")} -
-
- - -
+ ` : ""} @@ -296,6 +290,7 @@ export class JoinPrivateLobbyModal extends BaseModal { this.hasJoined = false; this.message = ""; this.currentLobbyId = ""; + this.nationCount = 0; this.leaveLobbyOnClose = true; } @@ -512,11 +507,38 @@ export class JoinPrivateLobbyModal extends BaseModal { this.lobbyCreatorClientID = data.clients?.[0]?.clientID ?? null; this.players = data.clients ?? []; if (data.gameConfig) { + const mapChanged = + this.gameConfig?.gameMap !== data.gameConfig.gameMap; this.gameConfig = data.gameConfig; + if (mapChanged) { + this.loadNationCount(); + } } }) .catch((error) => { console.error("Error polling players:", error); }); } + + private async loadNationCount() { + if (!this.gameConfig) { + this.nationCount = 0; + return; + } + const currentMap = this.gameConfig.gameMap; + try { + const mapData = this.mapLoader.getMapData(currentMap); + const manifest = await mapData.manifest(); + // Only update if the map hasn't changed + if (this.gameConfig?.gameMap === currentMap) { + this.nationCount = manifest.nations.length; + } + } catch (error) { + console.warn("Failed to load nation count", error); + // Only update if the map hasn't changed + if (this.gameConfig?.gameMap === currentMap) { + this.nationCount = 0; + } + } + } } diff --git a/src/client/components/LobbyTeamView.ts b/src/client/components/LobbyPlayerView.ts similarity index 83% rename from src/client/components/LobbyTeamView.ts rename to src/client/components/LobbyPlayerView.ts index d85105c4b..2bcac3108 100644 --- a/src/client/components/LobbyTeamView.ts +++ b/src/client/components/LobbyPlayerView.ts @@ -13,6 +13,7 @@ import { Team, Trios, } from "../../core/game/Game"; +import { getCompactMapNationCount } from "../../core/game/NationCreation"; import { assignTeamsLobbyPreview } from "../../core/game/TeamAssignment"; import { ClientInfo, TeamCountConfig } from "../../core/Schemas"; import { translateText } from "../Utils"; @@ -22,7 +23,7 @@ export interface TeamPreviewData { players: ClientInfo[]; } -@customElement("lobby-team-view") +@customElement("lobby-player-view") export class LobbyTeamView extends LitElement { @property({ type: String }) gameMode: GameMode = GameMode.FFA; @property({ type: Array }) clients: ClientInfo[] = []; @@ -32,6 +33,8 @@ export class LobbyTeamView extends LitElement { @property({ attribute: "team-count" }) teamCount: TeamCountConfig = 2; @property({ type: Function }) onKickPlayer?: (clientID: string) => void; @property({ type: Number }) nationCount: number = 0; + @property({ type: Boolean }) disableNations: boolean = false; + @property({ type: Boolean }) isCompactMap: boolean = false; private theme: PastelTheme = new PastelTheme(); @state() private showTeamColors: boolean = false; @@ -52,11 +55,32 @@ export class LobbyTeamView extends LitElement { } render() { - return html`
- ${this.gameMode === GameMode.Team - ? this.renderTeamMode() - : this.renderFreeForAll()} -
`; + return html` +
+
+
+ ${this.clients.length} + ${this.clients.length === 1 + ? translateText("host_modal.player") + : translateText("host_modal.players")} + • + ${this.getEffectiveNationCount()} + ${this.getEffectiveNationCount() === 1 + ? translateText("host_modal.nation_player") + : translateText("host_modal.nation_players")} +
+
+
+ ${this.gameMode === GameMode.Team + ? this.renderTeamMode() + : this.renderFreeForAll()} +
+
+ `; } createRenderRoot() { @@ -148,14 +172,15 @@ export class LobbyTeamView extends LitElement { } private renderTeamCard(preview: TeamPreviewData, isEmpty: boolean = false) { + const effectiveNationCount = this.getEffectiveNationCount(); const displayCount = preview.team === ColoredTeams.Nations - ? this.nationCount + ? effectiveNationCount : preview.players.length; const maxTeamSize = preview.team === ColoredTeams.Nations - ? this.nationCount + ? effectiveNationCount : this.teamMaxSize; return html` @@ -308,4 +333,20 @@ export class LobbyTeamView extends LitElement { players: buckets.get(t) ?? [], })); } + + /** + * Returns the effective nation count for display purposes. + * In HumansVsNations mode, this equals the number of human players. + * For compact maps, only 25% of nations are used. + * Otherwise, it uses the manifest nation count (or 0 if nations are disabled). + */ + private getEffectiveNationCount(): number { + if (this.disableNations) { + return 0; + } + if (this.gameMode === GameMode.Team && this.teamCount === HumansVsNations) { + return this.clients.length; + } + return getCompactMapNationCount(this.nationCount, this.isCompactMap); + } } diff --git a/src/client/styles.css b/src/client/styles.css index 9873cd4f0..7023676e8 100644 --- a/src/client/styles.css +++ b/src/client/styles.css @@ -546,7 +546,6 @@ label.option-card:hover { flex-wrap: wrap; gap: 8px; justify-content: center; - padding: 0 16px; } /* News Button Notification */ diff --git a/src/server/MapPlaylist.ts b/src/server/MapPlaylist.ts index c4430e332..97b23f0e4 100644 --- a/src/server/MapPlaylist.ts +++ b/src/server/MapPlaylist.ts @@ -127,9 +127,7 @@ export class MapPlaylist { publicGameModifiers: { isCompact, isRandomSpawn, startingGold }, startingGold, difficulty: - playerTeams === HumansVsNations - ? Difficulty.Impossible - : Difficulty.Easy, + playerTeams === HumansVsNations ? Difficulty.Hard : Difficulty.Easy, infiniteGold: false, infiniteTroops: false, maxTimerValue: undefined,