mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-24 04:02:37 +00:00
feat(doomsday-clock): battle-royale style zone gamemode (#4469)
Resolves Issue #4463 ## Description: An optional game mode that (almost) guarantees a finish instead of letting late-game stalemates drag on. Originally called sudden death, renamed to Doomsday clock Once enabled, every side (each player in FFA, each whole team in team modes) must hold a rising share of the map. A side below the bar is skulled; after a short warn its troops bleed to zero, forcing consolidation to a winner. ### How it works - **Rising zone:** a grace period, then the required share ramps up linearly to each level with 30s pauses between (a battle-royale "zone"). Levels track the ofstats FFA territory median (3/5/10/20/30%). - **Four speed presets** (slow / normal / fast / very fast) change only the pace: normal ends ~30 min, very fast ~15. - **Troop decay:** a linear ramp as a % of max capacity, ~50s from caught to zero (10s warn + ~50s ≈ 1 min total). - **UI:** a HUD panel (live share vs target, wave/decay countdowns, red/orange cues) and an on-map skull above flagged players (blinks in danger, steady while draining). ### Notes for review - Off by default; no effect on existing games. However, as discussed we can add it to the modifier pool for public games to see how popular the gamemode is vs normal play. - Sim is deterministic (integer-only, in `src/core`), covered by unit + integration tests. - One-line addition to `GameServer.updateGameConfig` so the setting survives the host → server → client round-trip. - Status is packed into the existing name-pass data slot (`pd4.w`: 0/1/2 = none/danger/draining); the skull is composited into the icon atlas at load. ### Testing `npm test`, `npm run lint`, `npx prettier --check .`, `npm run build-prod` all pass. ### UI: <img width="243" height="100" alt="Image" src="https://github.com/user-attachments/assets/c4c9eeb0-4feb-437d-9aac-b2786a841b74" /> Dropdown between slow, normal, fast, very fast Before zone: <img width="302" height="175" alt="Image" src="https://github.com/user-attachments/assets/7359a1ea-4951-446d-a23c-0711fe06cc5d" /> Zone started, player not affected the pannel also blinks orange for 10s: <img width="297" height="175" alt="Image" src="https://github.com/user-attachments/assets/fcc565a5-d5d0-47a7-97ea-d0ba9d9ad899" /> Player affected, grace period (Danger): <img width="314" height="170" alt="Image" src="https://github.com/user-attachments/assets/ff96d21e-96f3-4ef9-8190-48eecc7aac0f" /> Skull icon blinking over player (everyone sees it) - older screenshot, the clipping has been fixed <img width="462" height="145" alt="Image" src="https://github.com/user-attachments/assets/53899211-33b1-40e1-83f2-77f2096f0cad" /> Player affected, grace period ended (Draining): <img width="360" height="159" alt="Image" src="https://github.com/user-attachments/assets/4b226d57-da4d-4866-ab5f-db48e4ed1ea2" /> Skull icon no longer blinking, everyone can see you are in a state of decay, and troops are draining: <img width="732" height="146" alt="image" src="https://github.com/user-attachments/assets/cd10fedb-6e87-4dfc-9fbf-55d3945a7901" /> Skull is visible like alliances icon also on player tab <img width="558" height="81" alt="Image" src="https://github.com/user-attachments/assets/6acdbe91-bdd0-40c7-942b-3990d4dae87f" /> (just UI example, best way to see it is to hop on a solo game and play against AI) ## 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 ## Please put your Discord username so you can be contacted if a bug or regression is found: zixer._
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
import { PlayerView } from "../../client/view";
|
||||
import { AssetManifest } from "../AssetUrls";
|
||||
import { DoomsdayClockSpeed } from "../game/DoomsdayClock";
|
||||
import {
|
||||
Difficulty,
|
||||
Game,
|
||||
@@ -80,6 +81,21 @@ export const JwksSchema = z.object({
|
||||
/** SAM launcher construction duration in ticks (non-instant-build). */
|
||||
export const SAM_CONSTRUCTION_TICKS = 30 * 10;
|
||||
|
||||
// Doomsday Clock tunables (anti-stall). Off unless enabled in GameConfig.
|
||||
// Times in seconds. The required map share rises in waves (levels + times in
|
||||
// DoomsdayClock.ts, chosen by `speed`). A side caught below the bar gets a
|
||||
// warnSeconds cooldown ("Danger, decay in Xs"), then troops bleed to zero: the
|
||||
// warn (10s) + the linear drain (~55s from full troops, sooner with fewer troops
|
||||
// or a shrinking territory) make ~1 minute from caught to wiped out.
|
||||
const DOOMSDAY_CLOCK_DEFAULTS = {
|
||||
enabled: false,
|
||||
speed: "normal" as DoomsdayClockSpeed,
|
||||
warnSeconds: 10, // cooldown before decay after the bar catches you
|
||||
drainStartPercent: 2, // starts bleeding at once (already beats troop income)
|
||||
drainMaxPercent: 6,
|
||||
drainRampSeconds: 50, // ramps LINEARLY to the max over this long
|
||||
};
|
||||
|
||||
export class Config {
|
||||
private unitInfoCache = new Map<UnitType, UnitInfo>();
|
||||
constructor(
|
||||
@@ -101,6 +117,21 @@ export class Config {
|
||||
traitorDuration(): number {
|
||||
return 30 * 10; // 30 seconds
|
||||
}
|
||||
|
||||
// Doomsday Clock config, resolved against defaults. One read per tick.
|
||||
doomsdayClockConfig(): typeof DOOMSDAY_CLOCK_DEFAULTS {
|
||||
const c = this._gameConfig.doomsdayClock;
|
||||
const d = DOOMSDAY_CLOCK_DEFAULTS;
|
||||
return {
|
||||
enabled: c?.enabled ?? d.enabled,
|
||||
speed: c?.speed ?? d.speed,
|
||||
// Drain/warn tuning is internal (not wire-configurable): always defaults.
|
||||
warnSeconds: d.warnSeconds,
|
||||
drainStartPercent: d.drainStartPercent,
|
||||
drainMaxPercent: d.drainMaxPercent,
|
||||
drainRampSeconds: d.drainRampSeconds,
|
||||
};
|
||||
}
|
||||
spawnImmunityDuration(): Tick {
|
||||
return (
|
||||
this._gameConfig.spawnImmunityDuration ?? DEFAULT_SPAWN_IMMUNITY_TICKS
|
||||
|
||||
Reference in New Issue
Block a user