mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-23 18:20:52 +00:00
95840a207429ff99b39d901cd4059c3ac3d981e9
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
95840a2074 |
Doomsday Clock: judge teams against the same bar as solo sides (#4624)
**Add approved & assigned issue number here:** Resolves #(to be filed — happy to open the matching issue; flagging directly since this is live in prod team lobbies) ## Description: **Bug.** In Team mode the Doomsday Clock threshold is scaled by the team's *alive* headcount (`base × members.length`, capped at the whole map). In real team lobbies this breaks the mechanic: - With ~16 players per team, wave 1 already demands `4 % × 16 = 64 %+` of the map per team while all humans combined hold ~35 % (bots own the rest). Every non-leading team is skulled ~1 minute into wave 1 and drained to the 5 % floor ~90 s later — even on **slow**, whose squeeze is designed to run to 45:00. The game is decided by whoever leads at grace-end. - The bar/forecast **drops when a teammate dies** (players see "will rise to 68 %" → "44 %" mid-game), and disconnected-but-alive teammates silently inflate it. - Past the 100 % cap, the per-capita scaling stops meaning anything anyway. **Evidence.** Prod game `JEUieLzK` (`Team, playerTeams: 4, 69 players, doomsdayClock slow, bots: 400, no timer`): ended **16:47** via the 95 % team-domination check. From the archived record: 25 deaths before 10:00, only 8 after — the drain cripples rather than kills, so the collapse is invisible in death stats but decisive in outcome. Reconstruction with the real schedule + drain constants: every non-leading team skulled ~11:00–11:30 and pinned at the floor by ~12:30; the crown-exempt leader rolled to 95 % by 16:47. **Why flat is correct.** The wave levels encode a viable-**side** count — `floor(100 / wave%)` sides fit above the bar (25 → 11 → 6 → 3 → 2 → 1). Elimination and victory happen at side granularity (a team is out when the whole team is out; the winner is a team), so the slots must be counted in sides. A team now faces exactly the bar a solo player faces, judged on its combined territory — and each speed preset means the same thing in teams as it does in FFA. **Changes:** - `DoomsdayClock.ts`: remove `doomsdayClockSideRequiredTiles`; the sim and the HUD both use `doomsdayClockRequiredTiles` (hoisted out of the per-side loop — it no longer varies per side). - `DoomsdayClockExecution.ts`: flat per-side bar; comments updated. - `DoomsdayClockPanel.ts`: drop `scalePct`/headcount from the readout — the zone forecast becomes one universal, monotonic number for every player (fixes the 68 % → 44 % whipsaw). `sideStats` → `sideTiles`. - Tests: headcount-scaling expectations replaced with flat-bar + death-invariance regression tests. (History note: an earlier commit restricted team lobbies to normal/fast presets; it was reverted in-branch — with the flat bar the presets carry the same meaning as in FFA, so the full rotation stays. Squash-merge leaves the fix only.) FFA behaviour is unchanged throughout (sides of size 1: flat ≡ current). Deterministic integer math untouched (strictly fewer ops). `npm test` fully green. Rule comparison on `JEUieLzK`'s shape (real death ticks + constants; modeled share trajectories): | rule | trailing teams skulled | all non-leaders at 5 % floor | |---|---|---| | current (× alive) | 11:01–11:27 | **12:34** ← matches the real 16:47 blowout | | flat per-side bar | 26:01–30:42 | 31:49 (late-game backstop, as designed) | ## Please complete the following: - [ ] I have added screenshots for all UI updates — _no layout/element changes; the existing readout shows unscaled values (numbers only)_ - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file — _no new strings; existing `doomsday_clock.*` keys reused with the same params_ - [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 |
||
|
|
a2321eb824 |
balance(doomsday): floor the drain at 5% of max instead of wiping to zero (#4587)
## Description: The Doomsday Clock drain wiped a caught side's troops (and warship health) to **zero**, an effective death sentence, so a brief dip below the bar was unrecoverable. Floor the drain at **5% of each max** (`drainFloorPercent`) for both troops and warships: a doomed side is crippled, not eliminated, and recovers if it climbs back above the bar. The rising territory bar still forces a finish (a 5%-strength side is trivial to conquer, and the leader is drain-exempt). - Config: `drainFloorPercent` (5), applied to both drains. - Execution: clamp troop + warship-health removal to what sits above the floor (integer-only, deterministic). - HUD: the Collapsing rate is shown net of the floor. - Tests: drain-to-zero / warship-scuttle cases updated to the floor + added coverage. ## 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 |
||
|
|
4ce57efbe2 |
Rebalance Doomsday Clock: late-game stalemate-breaker (10min grace + wave squeeze), slower troop drain, gentler-but-steeper warship attrition (#4518)
Resolves #<add your approved & assigned issue number> ## Description: Rebalances the Doomsday Clock so it acts as a **late-game stalemate-breaker** rather than an early-game culler, softens how fast it removes troops and warships, and makes the HUD countdown clearer. **Clock schedule (all presets):** - A flat **10-minute grace** at 0% required share — the early game is decided by combat, not the clock. - Then a 6-wave squeeze at accelerating levels (4 / 9 / 16 / 26 / 40 / 55%) with short pauses, reaching the final 55% at each preset's cap: **45 / 35 / 25 / 15 min** for slow / normal / fast / veryfast. - `WaveSchedule` `rampSeconds`/`pauseSeconds` are now **per-wave arrays**, so the curve can be shaped (gentle early, steeper late) instead of one uniform ramp. `requiredBasisPoints` and the HUD companion `doomsdayClockWaveState` walk the per-wave segments in lockstep. **Troop drain:** warn window `10s → 30s`, drain eased (`2%→5%` over `90s`), so a caught side takes ~2 minutes to wipe instead of ~1. **Warship attrition:** warships get their own gentler start plus a **convex** decay curve — a ship caught when its side is first doomed lasts about as long as troops, but the rate ramps up steeply so a side at full attrition still loses its fleet in ~2s. Adds a `curveExponent` argument to `doomsdayClockDrain` (1 = linear, used for troops; higher = convex, used for ships). **Determinism:** the drain curve is **integer-only** (fixed-point power, no floats), so the floored per-tick loss is bit-identical on every client in the lockstep sim. The linear troop path keeps its exact existing integer form; only the convex warship path is reshaped. **HUD countdown clarity:** the clock readout now shows a live countdown in both states — `Will reach 16% in M:SS` while the bar is actively climbing, and `Starts rising to 26% in M:SS` during a pause (previously `Next 26% in …`, which read as if it jumped there instantly). Backed by a new `secondsToTarget` field on the shared wave state so the sim and HUD stay in agreement. All display text goes through `translateText()` / `en.json`. <img width="278" height="116" alt="image" src="https://github.com/user-attachments/assets/e0be3d2c-bb88-46be-b344-34d63e4859bd" /> <img width="304" height="171" alt="image" src="https://github.com/user-attachments/assets/da74bd05-0b9a-4aec-bbf5-e7380fbda88e" /> **Danger skull:** while a side is below the bar in the warn window, its on-map skull now blinks progressively faster as the countdown runs out (accelerating to the moment the drain begins), then holds steady once it is actually draining — a clearer "you are about to be hit" cue. Rationale: the previous schedule removed players heavily in the first half of a match and could leave a drawn-out endgame. Holding the clock at 0% early keeps the opening about fighting, and concentrating its pressure in the back half reserves it for actually breaking stalemates. Values are tuning starting points and easy to adjust in `DoomsdayClock.ts` / the config defaults. ## 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: <add your Discord username> |
||
|
|
2a1381b41e |
feat(doomsday-clock): show the zone readout to spectators and replay viewers (#4497)
## Description: Follow-up to #4469. The Doomsday Clock panel hides itself unless the viewer has a living player, so spectators, replay viewers, and eliminated or not-yet-spawned players see nothing, even though the clock is the most useful thing to watch in those cases. This shows the panel whenever the mode is on and there's no winner yet: - **Live player:** unchanged. Full personal readout (your-share line, Stable/Unstable/Collapsing status, danger pulse). - **Spectator / replay / eliminated / not-spawned:** zone-only readout. The rising threshold bar and the wave countdown ("Rising to X%", "Final X%", "Next wave X% in mm:ss"), with no personal line and no false danger pulse. One gate drives it: `live = me && me.isAlive()`. The zone math was already player-independent, so nothing new there. Tested: `tsc --noEmit`, ESLint, Prettier, the `DoomsdayClockExecution` suite (28/28), and `build-prod`, all clean. |
||
|
|
78ef7b56fd |
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._ |