feat(playlist): add the Doomsday Clock to the public modifier rotation (#4589)

## Description:

Adds the anti-stall **Doomsday Clock** to the public "special" modifier
rotation, so it rolls into public games like the other spice modifiers,
with a lobby badge.

- `MapPlaylist`: new `isDoomsdayClock` ticket in `SPECIAL_MODIFIER_POOL`
(weight 4, ~20% of special games). When rolled, enables `doomsdayClock`
at a speed picked per game (slow/normal/fast/veryfast).
- `PublicGameModifiers` (type + schema): `isDoomsdayClock` flag drives
the badge.
- `getActiveModifiers` + `en.json`: a "Doomsday Clock" badge/label.
- Test covering the badge wiring.

## 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
This commit is contained in:
Zixer1
2026-07-13 11:11:11 -07:00
committed by GitHub
parent a2321eb824
commit 290d6922eb
6 changed files with 59 additions and 2 deletions
+6
View File
@@ -220,6 +220,12 @@ export function getActiveModifiers(
badgeKey: "public_game_modifier.water_nukes",
});
}
if (modifiers.isDoomsdayClock) {
result.push({
labelKey: "public_game_modifier.doomsday_clock_label",
badgeKey: "public_game_modifier.doomsday_clock",
});
}
return result;
}
+1
View File
@@ -328,6 +328,7 @@ export const GameConfigSchema = z.object({
isSAMsDisabled: z.boolean().optional(),
isPeaceTime: z.boolean().optional(),
isWaterNukes: z.boolean().optional(),
isDoomsdayClock: z.boolean().optional(),
})
.optional(),
nations: z
+1
View File
@@ -148,6 +148,7 @@ export interface PublicGameModifiers {
isSAMsDisabled?: boolean;
isPeaceTime?: boolean;
isWaterNukes?: boolean;
isDoomsdayClock?: boolean;
}
export interface UnitInfo {
+30 -2
View File
@@ -78,7 +78,8 @@ type ModifierKey =
| "isNukesDisabled"
| "isSAMsDisabled"
| "isPeaceTime"
| "isWaterNukes";
| "isWaterNukes"
| "isDoomsdayClock";
// Each entry represents one "ticket" in the pool. More tickets = higher chance of selection.
// Weights are roughly informed by the community "favorite modifier" poll.
@@ -96,8 +97,19 @@ const SPECIAL_MODIFIER_POOL: ModifierKey[] = [
...Array<ModifierKey>(1).fill("isSAMsDisabled"),
...Array<ModifierKey>(1).fill("isPeaceTime"),
...Array<ModifierKey>(4).fill("isWaterNukes"),
...Array<ModifierKey>(4).fill("isDoomsdayClock"),
];
// Speeds the Doomsday Clock can roll at when it lands in the rotation. Picked
// per game (see getSpecialConfig) so the pacing varies instead of always being
// the same preset.
const DOOMSDAY_ROTATION_SPEEDS = [
"slow",
"normal",
"fast",
"veryfast",
] as const;
// Maps where water nukes have a higher chance on top of the normal pool
// Water nukes are especially fun here
const WATER_NUKES_BOOSTED_MAPS: ReadonlySet<GameMapType> = new Set([
@@ -258,6 +270,7 @@ export class MapPlaylist {
isSAMsDisabled,
isPeaceTime,
isWaterNukes,
isDoomsdayClock,
} = poolResult;
if (boostWaterNukes) {
isWaterNukes = true;
@@ -284,7 +297,8 @@ export class MapPlaylist {
!isNukesDisabled &&
!isSAMsDisabled &&
!isPeaceTime &&
!isWaterNukes
!isWaterNukes &&
!isDoomsdayClock
) {
excludedModifiers.push("isCrowded");
const fallback = this.getRandomSpecialGameModifiers(
@@ -301,6 +315,7 @@ export class MapPlaylist {
isSAMsDisabled,
isPeaceTime,
isWaterNukes,
isDoomsdayClock,
} = fallback);
({ isHardNations } = fallback);
}
@@ -357,7 +372,19 @@ export class MapPlaylist {
isSAMsDisabled,
isPeaceTime,
isWaterNukes,
isDoomsdayClock,
},
// Rolled into the rotation: enable the anti-stall clock at a speed picked
// per game so the pacing varies across the presets.
doomsdayClock: isDoomsdayClock
? {
enabled: true,
speed:
DOOMSDAY_ROTATION_SPEEDS[
Math.floor(Math.random() * DOOMSDAY_ROTATION_SPEEDS.length)
],
}
: undefined,
startingGold,
goldMultiplier,
disableAlliances: isAlliancesDisabled ? true : undefined,
@@ -561,6 +588,7 @@ export class MapPlaylist {
isSAMsDisabled: selected.has("isSAMsDisabled") || undefined,
isPeaceTime: selected.has("isPeaceTime") || undefined,
isWaterNukes: selected.has("isWaterNukes") || undefined,
isDoomsdayClock: selected.has("isDoomsdayClock") || undefined,
};
}