From 526cb723d6ad8b147e2891648fbc53bd181ec08b Mon Sep 17 00:00:00 2001 From: FloPinguin <25036848+FloPinguin@users.noreply.github.com> Date: Sat, 7 Mar 2026 21:57:16 +0100 Subject: [PATCH] =?UTF-8?q?Fix=202=20HvN=20UI=20bugs=20=F0=9F=94=A7=20(#33?= =?UTF-8?q?78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description: I noticed two HvN bugs. 1. Private lobbies don't set `maxPlayers` in `GameConfig`, causing `getGameModeLabel()` to render "0 Humans vs 0 Nations". Fall back to the simple "Humans vs Nations" label when `maxPlayers` is unavailable. Screenshot 2026-03-07 034150 2. In public HumansVsNations games, the server matches the nation count to the human player count at game start. The lobby team size preview wasn't reflecting this - it displayed the raw config value instead. Added `isPublicGame` prop to `LobbyPlayerView` and an `effectiveNationCount` getter that overrides the displayed nation count to match `clients.length` only for public HvN games. Private lobby hosts retain full slider control. (This bug got introduced with my "Configurable nation count" PR) ## 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/JoinLobbyModal.ts | 2 ++ src/client/Utils.ts | 9 ++++--- src/client/components/LobbyPlayerView.ts | 32 ++++++++++++++++++------ 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/client/JoinLobbyModal.ts b/src/client/JoinLobbyModal.ts index 7834045b7..b6771e5f0 100644 --- a/src/client/JoinLobbyModal.ts +++ b/src/client/JoinLobbyModal.ts @@ -130,6 +130,8 @@ export class JoinLobbyModal extends BaseModal { .lobbyCreatorClientID=${hostClientID} .currentClientID=${this.currentClientID} .teamCount=${this.gameConfig?.playerTeams ?? 2} + .isPublicGame=${this.gameConfig?.gameType === + GameType.Public} .nationCount=${nationsConfigToSlider( this.gameConfig?.nations ?? "default", this.nationCount, diff --git a/src/client/Utils.ts b/src/client/Utils.ts index 7f394b564..159e8cfb3 100644 --- a/src/client/Utils.ts +++ b/src/client/Utils.ts @@ -36,9 +36,12 @@ export function getGameModeLabel(gameConfig: GameConfig): string { // Humans vs Nations if (playerTeams === HumansVsNations) { - return translateText("public_lobby.teams_hvn_detailed", { - num: maxPlayers ?? 0, - }); + if (maxPlayers) { + return translateText("public_lobby.teams_hvn_detailed", { + num: maxPlayers, + }); + } + return translateText("public_lobby.teams_hvn"); } // Named team types (Duos, Trios, Quads) diff --git a/src/client/components/LobbyPlayerView.ts b/src/client/components/LobbyPlayerView.ts index 9c0366f9e..7ea87bab1 100644 --- a/src/client/components/LobbyPlayerView.ts +++ b/src/client/components/LobbyPlayerView.ts @@ -35,11 +35,24 @@ 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 }) isPublicGame: boolean = false; private theme: PastelTheme = new PastelTheme(); @state() private showTeamColors: boolean = false; private userSettings: UserSettings = new UserSettings(); + /** + * For public HumansVsNations games, nation count always matches human count + * (server enforces this in NationCreation). For private games, the host + * controls the nation count via the slider. + */ + private get effectiveNationCount(): number { + if (this.isPublicGame && this.teamCount === HumansVsNations) { + return this.clients.length; + } + return this.nationCount; + } + willUpdate(changedProperties: Map) { // Recompute team preview when relevant properties change // clients is updated from WebSocket lobby_info events @@ -47,7 +60,8 @@ export class LobbyTeamView extends LitElement { changedProperties.has("gameMode") || changedProperties.has("clients") || changedProperties.has("teamCount") || - changedProperties.has("nationCount") + changedProperties.has("nationCount") || + changedProperties.has("isPublicGame") ) { const teamsList = this.getTeamList(); this.computeTeamPreview(teamsList); @@ -67,8 +81,8 @@ export class LobbyTeamView extends LitElement { ? translateText("host_modal.player") : translateText("host_modal.players")} - ${this.nationCount} - ${this.nationCount === 1 + ${this.effectiveNationCount} + ${this.effectiveNationCount === 1 ? translateText("host_modal.nation_player") : translateText("host_modal.nation_players")} @@ -179,12 +193,12 @@ export class LobbyTeamView extends LitElement { private renderTeamCard(preview: TeamPreviewData, isEmpty: boolean = false) { const displayCount = preview.team === ColoredTeams.Nations - ? this.nationCount + ? this.effectiveNationCount : preview.players.length; const maxTeamSize = preview.team === ColoredTeams.Nations - ? this.nationCount + ? this.effectiveNationCount : this.teamMaxSize; const teamLabel = getTranslatedPlayerTeamLabel(preview.team); @@ -245,7 +259,7 @@ export class LobbyTeamView extends LitElement { private getTeamList(): Team[] { if (this.gameMode !== GameMode.Team) return []; - const playerCount = this.clients.length + this.nationCount; + const playerCount = this.clients.length + this.effectiveNationCount; const config = this.teamCount; if (config === HumansVsNations) { @@ -309,7 +323,7 @@ export class LobbyTeamView extends LitElement { const assignment = assignTeamsLobbyPreview( players, teams, - this.nationCount, + this.effectiveNationCount, ); const buckets = new Map(); for (const t of teams) buckets.set(t, []); @@ -333,7 +347,9 @@ export class LobbyTeamView extends LitElement { // Fallback: divide players across teams; guard against 0 and empty lobbies this.teamMaxSize = Math.max( 1, - Math.ceil((this.clients.length + this.nationCount) / teams.length), + Math.ceil( + (this.clients.length + this.effectiveNationCount) / teams.length, + ), ); } this.teamPreview = teams.map((t) => ({