Added a public game modifier system 😮 For more variety (#2801)

## Description:

Added a public game modifier system. It causes that

5% of public games are played on the compact version of the map
10% of public games have "Random Spawn" activated

Percentages can easily get changed via `DefaultConfig`.
We can also easily add more modifiers.
Modifiers can stack, so in rare cases you will play on a compact map
with random spawn 😄
More variety!

### "Compact Map" modifier implementation

- With the "Compact Map" modifier the lobby max player count gets
reduced to 25% and only 25% of the regular bots and only 25% of the
regular nations will spawn (because the map has only 25% of its regular
size)
- In private lobbies and singleplayer the nation reduction happens too
(When "Compact Map" is enabled).

### Restrictions

- Duos/Trios/Quads team modes do not get Random Spawn (defeats the
purpose)
- Maps with smallest player count < 50 do not get Compact Map in team
games (not enough players after the reduction to 25%). I have calculated
all the possible max player counts.

### How it looks like

Random Spawn modifier:

<img width="528" height="183" alt="Screenshot 2026-01-06 194959"
src="https://github.com/user-attachments/assets/2f729da9-80c3-4548-8205-71129da2a76a"
/>

Very rare case: Two modifiers at the same time and only 10 max players
have been chosen from `[GameMapType.FaroeIslands]: [20, 15, 10]`.
Because of the 75% reduction in player count only 3 players are allowed
(3 is the minimum). I think its funny that you can play a 1v1v1 in rare
occasions 😄

<img width="526" height="184" alt="Screenshot 2026-01-06 194938"
src="https://github.com/user-attachments/assets/834326eb-df03-41b7-b1db-1efa3f1013b5"
/>

### Funny side-effect

Team games with random spawn. That will be interesting. No more "Who is
better in donating troops to the frontline". Instead you have to heavily
coordinate with your teammates.

## 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
This commit is contained in:
FloPinguin
2026-01-07 04:37:58 +01:00
committed by GitHub
parent 387190b916
commit ebcb654825
10 changed files with 143 additions and 19 deletions
+3 -1
View File
@@ -14,6 +14,7 @@ import {
UnitType,
mapCategories,
} from "../core/game/Game";
import { getCompactMapNationCount } from "../core/game/NationCreation";
import { UserSettings } from "../core/game/UserSettings";
import {
ClientInfo,
@@ -944,6 +945,7 @@ export class HostLobbyModal extends LitElement {
/**
* 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 {
@@ -953,7 +955,7 @@ export class HostLobbyModal extends LitElement {
if (this.gameMode === GameMode.Team && this.teamCount === HumansVsNations) {
return this.clients.length;
}
return this.nationCount;
return getCompactMapNationCount(this.nationCount, this.compactMap);
}
}
+44 -11
View File
@@ -7,6 +7,7 @@ import {
GameMode,
hasUnusualThumbnailSize,
HumansVsNations,
PublicGameModifiers,
Quads,
Trios,
} from "../core/game/Game";
@@ -114,6 +115,10 @@ export class PublicLobby extends LitElement {
: `${modeLabel} ${teamDetailLabel}`;
}
const modifierLabel = this.getModifierLabels(
lobby.gameConfig.publicGameModifiers,
);
const mapImageSrc = this.mapImages.get(lobby.gameID);
const isUnusualThumbnailSize = hasUnusualThumbnailSize(
lobby.gameConfig.gameMap,
@@ -156,17 +161,29 @@ export class PublicLobby extends LitElement {
.join("")}`
: translateText("public_lobby.join")}
</div>
<div class="text-md font-medium text-white-400">
<span class="text-sm text-blue-600 bg-white rounded-xs px-1 mr-1">
${fullModeLabel}
</span>
<span>
${translateText(
`map.${lobby.gameConfig.gameMap
.toLowerCase()
.replace(/[\s.]+/g, "")}`,
)}
</span>
<div
class="text-md font-medium text-white-400 flex flex-wrap justify-end items-center gap-1"
>
<span
class="text-sm whitespace-nowrap ${this.isLobbyHighlighted
? "text-green-600"
: "text-blue-600"} bg-white rounded-xs px-1"
>${fullModeLabel}</span
>
${modifierLabel.map(
(label) =>
html`<span
class="text-sm whitespace-nowrap ${this.isLobbyHighlighted
? "text-green-600"
: "text-blue-600"} bg-white rounded-xs px-1"
>${label}</span
>`,
)}
<span class="whitespace-nowrap"
>${translateText(
`map.${lobby.gameConfig.gameMap.toLowerCase().replace(/[\s.]+/g, "")}`,
)}</span
>
</div>
</div>
@@ -293,6 +310,22 @@ export class PublicLobby extends LitElement {
return { label: null, isFullLabel: false };
}
private getModifierLabels(
publicGameModifiers: PublicGameModifiers | undefined,
): string[] {
if (!publicGameModifiers) {
return [];
}
const labels: string[] = [];
if (publicGameModifiers.isRandomSpawn) {
labels.push(translateText("public_game_modifier.random_spawn"));
}
if (publicGameModifiers.isCompact) {
labels.push(translateText("public_game_modifier.compact_map"));
}
return labels;
}
private lobbyClicked(lobby: GameInfo) {
if (this.isButtonDebounced) return;