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
This commit is contained in:
Zixer1
2026-07-13 11:10:06 -07:00
committed by GitHub
parent d07f7d46dd
commit a2321eb824
4 changed files with 106 additions and 68 deletions
+7 -4
View File
@@ -138,15 +138,18 @@ export class DoomsdayClockPanel extends LitElement {
let statusClass = "";
let detail = zoneDetail;
if (live && draining && me) {
// Drain is a % of max-troop capacity, capped at current troops; show the
// actual per-second loss (renderTroops handles the /10 display unit).
// Drain is a % of max-troop capacity that stops at the floor
// (drainFloorPercent of max); show the actual per-second loss, i.e. only
// what sits above the floor (renderTroops handles the /10 display unit).
const maxTroops = this.game.config().maxTroops(me);
const floor = Math.floor((maxTroops * sd.drainFloorPercent) / 100);
const chunk = doomsdayClockDrain(
this.game.config().maxTroops(me),
maxTroops,
secondsUnder - sd.warnSeconds,
sd,
);
status = translateText("doomsday_clock.collapsing", {
rate: renderTroops(Math.min(me.troops(), chunk)),
rate: renderTroops(Math.max(0, Math.min(me.troops() - floor, chunk))),
});
statusClass = "text-red-400 font-bold";
} else if (live && flagged) {