Improve "Better troop management for nations 🤖" (#4278)

## Description:

**Allow Hard/Impossible nations to retaliate and expand freely**

Previously, nations on Hard/Impossible difficulty could be stuck unable
to fight back if their `troopSendCap` or `isAttackTooWeak` checks
blocked them from sending enough troops. **@legan320** on the main
discord noticed it. Now:

- `troopSendCap` raises the cap to at least the total incoming attack
troops, so nations can match the force being used against them
- `isAttackTooWeak` bypasses the 20% minimum check entirely when under
attack
- `troopSendCap` no longer applies when attacking Terra Nullius, so
nations can always expand into unowned land with full troops

All checks still apply normally for unprovoked attacks against other
players.

## 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:

FloPinguin
This commit is contained in:
FloPinguin
2026-06-15 03:53:01 +02:00
committed by GitHub
parent 5161d78d84
commit 094aa766ce
2 changed files with 63 additions and 11 deletions
+34
View File
@@ -1,3 +1,4 @@
import { AttackExecution } from "../src/core/execution/AttackExecution";
import { NationEmojiBehavior } from "../src/core/execution/nation/NationEmojiBehavior";
import { AiAttackBehavior } from "../src/core/execution/utils/AiAttackBehavior";
import {
@@ -567,4 +568,37 @@ describe("Hard/Impossible troop floor", () => {
expect(exec).toBeDefined();
expect(exec.startTroops).toBeGreaterThan(0);
});
it("Hard: nation under attack bypasses troopSendCap and isAttackTooWeak", async () => {
const { testGame, attacker, neighbor, behavior } =
await setupTroopFloorTest(Difficulty.Hard);
// Neighbor has far more troops, so the normal cap would be 0
attacker.addTroops(100_000);
neighbor.addTroops(200_000);
// Normal cap = max(0, 100k - ceil(200k * 0.75)) = max(0, 100k - 150k) = 0
// Without the bypass, the nation couldn't attack at all.
const normalCap = Math.max(
0,
attacker.troops() - Math.ceil(neighbor.troops() * 0.75),
);
expect(normalCap).toBe(0);
// Simulate the neighbor attacking with 50k troops
testGame.addExecution(new AttackExecution(50_000, neighbor, attacker.id()));
testGame.executeNextTick();
expect(attacker.incomingAttacks().length).toBeGreaterThan(0);
// With incoming attacks, troopSendCap raises to at least totalIncoming
const addExecSpy = vi.spyOn(testGame, "addExecution");
const result = behavior.sendAttack(neighbor);
expect(result).toBe(true);
const exec = addExecSpy.mock.calls.find(
(c) => c[0].constructor.name === "AttackExecution",
)?.[0] as any;
expect(exec).toBeDefined();
// The bypass allows retaliation with at least the incoming 50k
expect(exec.startTroops).toBeGreaterThanOrEqual(50_000);
});
});