mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-17 15:39:15 +00:00
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:
@@ -1147,6 +1147,8 @@
|
||||
"crowded": "Crowded",
|
||||
"disable_alliances": "Alliances Disabled",
|
||||
"disable_alliances_label": "Alliances",
|
||||
"doomsday_clock": "Doomsday Clock",
|
||||
"doomsday_clock_label": "Doomsday Clock",
|
||||
"gold_multiplier": "x{amount} Gold Multiplier",
|
||||
"hard_nations": "Hard Nations",
|
||||
"nukes_disabled": "Nukes Disabled",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -281,6 +281,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
|
||||
|
||||
@@ -143,6 +143,7 @@ export interface PublicGameModifiers {
|
||||
isSAMsDisabled?: boolean;
|
||||
isPeaceTime?: boolean;
|
||||
isWaterNukes?: boolean;
|
||||
isDoomsdayClock?: boolean;
|
||||
}
|
||||
|
||||
export interface UnitInfo {
|
||||
|
||||
@@ -74,7 +74,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.
|
||||
@@ -92,8 +93,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([
|
||||
@@ -254,6 +266,7 @@ export class MapPlaylist {
|
||||
isSAMsDisabled,
|
||||
isPeaceTime,
|
||||
isWaterNukes,
|
||||
isDoomsdayClock,
|
||||
} = poolResult;
|
||||
if (boostWaterNukes) {
|
||||
isWaterNukes = true;
|
||||
@@ -280,7 +293,8 @@ export class MapPlaylist {
|
||||
!isNukesDisabled &&
|
||||
!isSAMsDisabled &&
|
||||
!isPeaceTime &&
|
||||
!isWaterNukes
|
||||
!isWaterNukes &&
|
||||
!isDoomsdayClock
|
||||
) {
|
||||
excludedModifiers.push("isCrowded");
|
||||
const fallback = this.getRandomSpecialGameModifiers(
|
||||
@@ -297,6 +311,7 @@ export class MapPlaylist {
|
||||
isSAMsDisabled,
|
||||
isPeaceTime,
|
||||
isWaterNukes,
|
||||
isDoomsdayClock,
|
||||
} = fallback);
|
||||
({ isHardNations } = fallback);
|
||||
}
|
||||
@@ -353,7 +368,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,
|
||||
@@ -557,6 +584,7 @@ export class MapPlaylist {
|
||||
isSAMsDisabled: selected.has("isSAMsDisabled") || undefined,
|
||||
isPeaceTime: selected.has("isPeaceTime") || undefined,
|
||||
isWaterNukes: selected.has("isWaterNukes") || undefined,
|
||||
isDoomsdayClock: selected.has("isDoomsdayClock") || undefined,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { getActiveModifiers } from "../../src/client/Utils";
|
||||
|
||||
// The doomsday clock is part of the public "special" modifier rotation, so an
|
||||
// active isDoomsdayClock modifier must surface a lobby badge like every other
|
||||
// rotation modifier.
|
||||
describe("doomsday clock public modifier", () => {
|
||||
it("surfaces a doomsday-clock badge when the modifier is active", () => {
|
||||
const mods = getActiveModifiers({ isDoomsdayClock: true });
|
||||
expect(mods).toHaveLength(1);
|
||||
expect(mods[0].badgeKey).toBe("public_game_modifier.doomsday_clock");
|
||||
expect(mods[0].labelKey).toBe("public_game_modifier.doomsday_clock_label");
|
||||
});
|
||||
|
||||
it("omits the badge when the modifier is absent", () => {
|
||||
expect(getActiveModifiers({})).toHaveLength(0);
|
||||
expect(getActiveModifiers(undefined)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user