For v30: Add new modifiers (Hard nations and 25M Starting Gold) 🙂 (#3316)

## Description:

Adds two new public game modifiers for variety and improves compact map
eligibility for team games.

### New Modifiers

**Hard Nations (`isHardNations`)**
- We need this modifier for HvN, because medium nations are easier now
(will result in a much higher human winrate)
- In a discord discussion we concluded that HvN should generally be
easier (higher winrate than 50%, so players are less frustated)
- Thats why only 20% of HvN games have the hard nations modifier (for
now)
- For PvPvE enjoyers, the modifier is also active in FFA games => (Only
2.5% chance, and 1 ticket in `SPECIAL_MODIFIER_POOL`)

**25M Starting Gold (`startingGoldHigh`)**
- Some people in the main discord wanted this modifier, and it will
result in crazy games
- Rare special-only modifier (1 ticket in pool); mutually exclusive with
5M starting gold via `MUTUALLY_EXCLUSIVE_MODIFIERS`
- Disables nations (they lack PVP immunity, so 25M gold doesn't work
well with them)
- Excluded from HumansVsNations games (since it disables nations)
- Spawn immunity set to **2 minutes 30 seconds** (vs 30s for 5M gold),
so people can spend the gold and prepare

### Other Changes

- **Improved `supportsCompactMapForTeams`**: Replaced the hard `smallest
>= 50` land-tile cutoff with a per-team-config calculation that
simulates worst-case compact player count and checks every team gets at
least 2 players.
- **HvN spawn immunity**: Always 5 seconds in both regular and special
lobbies (to get rid of a confusing PVP immunity HeadsUpMessage in 5M
starting gold games)
- **Regular public lobby random spawn modifier probabilty**: Reduced
from 10% to 5% (Because of the new modifier, so there aren't too many
modifiers in non-special-lobbies, should only occur sometimes there)
- Rebalanced `SPECIAL_MODIFIER_POOL` a bit

## 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-03-02 05:12:38 +01:00
committed by GitHub
parent e1125e0c37
commit 417fa0fe09
7 changed files with 207 additions and 57 deletions
+7 -13
View File
@@ -5,7 +5,6 @@ import {
GameMapType,
GameMode,
HumansVsNations,
PublicGameModifiers,
Quads,
Trios,
} from "../core/game/Game";
@@ -16,7 +15,12 @@ import { PublicLobbySocket } from "./LobbySocket";
import { JoinLobbyEvent } from "./Main";
import { SinglePlayerModal } from "./SinglePlayerModal";
import { terrainMapFileLoader } from "./TerrainMapFileLoader";
import { getMapName, renderDuration, translateText } from "./Utils";
import {
getMapName,
getModifierLabels,
renderDuration,
translateText,
} from "./Utils";
const CARD_BG = "bg-[color-mix(in_oklab,var(--frenchBlue)_70%,black)]";
@@ -198,7 +202,7 @@ export class GameModeSelector extends LitElement {
const mapName = getMapName(lobby.gameConfig?.gameMap);
const modifierLabels = this.getModifierLabels(
const modifierLabels = getModifierLabels(
lobby.gameConfig?.publicGameModifiers,
);
// Sort by length for visual consistency (shorter labels first)
@@ -283,16 +287,6 @@ export class GameModeSelector extends LitElement {
);
}
private getModifierLabels(mods: PublicGameModifiers | undefined): string[] {
if (!mods) return [];
return [
mods.isRandomSpawn && translateText("public_game_modifier.random_spawn"),
mods.isCompact && translateText("public_game_modifier.compact_map"),
mods.isCrowded && translateText("public_game_modifier.crowded"),
mods.startingGold && translateText("public_game_modifier.starting_gold"),
].filter((x): x is string => !!x);
}
private getLobbyTitle(lobby: PublicGameInfo): string {
const config = lobby.gameConfig!;
if (config.gameMode === GameMode.FFA) {
+14 -1
View File
@@ -109,6 +109,8 @@ export interface ModifierInfo {
labelKey: string;
/** Translation key for badge/short label (e.g. "public_game_modifier.random_spawn") */
badgeKey: string;
/** Parameters to pass to translateText for the badge key */
badgeParams?: Record<string, string | number>;
/** The raw value if applicable (e.g. startingGold amount) */
value?: number;
}
@@ -139,10 +141,19 @@ export function getActiveModifiers(
badgeKey: "public_game_modifier.crowded",
});
}
if (modifiers.isHardNations) {
result.push({
labelKey: "host_modal.hard_nations",
badgeKey: "public_game_modifier.hard_nations",
});
}
if (modifiers.startingGold) {
result.push({
labelKey: "host_modal.starting_gold",
badgeKey: "public_game_modifier.starting_gold",
badgeParams: {
amount: Math.round(modifiers.startingGold / 1_000_000),
},
value: modifiers.startingGold,
});
}
@@ -155,7 +166,9 @@ export function getActiveModifiers(
export function getModifierLabels(
modifiers: PublicGameModifiers | undefined,
): string[] {
return getActiveModifiers(modifiers).map((m) => translateText(m.badgeKey));
return getActiveModifiers(modifiers).map((m) =>
translateText(m.badgeKey, m.badgeParams),
);
}
export function renderDuration(totalSeconds: number): string {