mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:30:45 +00:00
fix(2388): troop penalty applied to boat retreat (#2389)
Description: There is a boating exploit where players could repeatedly send and retreat boats to effectively increase troop regeneration and maintain almost double the max troop cap. This PR fixes #2388. Summary This pr adds a troop penalty to the boat retreats in src/core/execution/TransportShipExecution.ts of 25 percent (currently the same as land attacks, but may require fine tuning). this prevents, to some degree, the ability to stockpile large amounts of troops above your max cap. Testing I tested the change locally and confirmed that the troop penalty is applied at the correct times and under the correct conditions, i.e. it is only applied on successful retreat (applying on reteat initiation may be confusing if you are told you have lost troops if the transport ship never arrives anyway). The boating exploit is far less effective with this fix in place. Notes - This is a exploit and does not introduce any new features. - Existing game behavior outside the retreat penalty remains unchanged. - The attack message is the same as the land attack, so the translation should already be present. Checklist - [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 - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced --------- Co-authored-by: Evan <evanpelle@gmail.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { renderTroops } from "../../client/Utils";
|
||||
import {
|
||||
Execution,
|
||||
Game,
|
||||
@@ -14,6 +15,7 @@ import { PathFindResultType } from "../pathfinding/AStar";
|
||||
import { PathFinder } from "../pathfinding/PathFinding";
|
||||
import { AttackExecution } from "./AttackExecution";
|
||||
|
||||
const malusForRetreat = 25;
|
||||
export class TransportShipExecution implements Execution {
|
||||
private lastMove: number;
|
||||
|
||||
@@ -222,14 +224,23 @@ export class TransportShipExecution implements Execution {
|
||||
switch (result.type) {
|
||||
case PathFindResultType.Completed:
|
||||
if (this.mg.owner(this.dst) === this.attacker) {
|
||||
this.attacker.addTroops(this.boat.troops());
|
||||
const deaths = this.boat.troops() * (malusForRetreat / 100);
|
||||
const survivors = this.boat.troops() - deaths;
|
||||
this.attacker.addTroops(survivors);
|
||||
this.boat.delete(false);
|
||||
this.active = false;
|
||||
|
||||
// Record stats
|
||||
this.mg
|
||||
.stats()
|
||||
.boatArriveTroops(this.attacker, this.target, this.boat.troops());
|
||||
.boatArriveTroops(this.attacker, this.target, survivors);
|
||||
if (deaths) {
|
||||
this.mg.displayMessage(
|
||||
`Attack cancelled, ${renderTroops(deaths)} soldiers killed during retreat.`,
|
||||
MessageType.ATTACK_CANCELLED,
|
||||
this.attacker.id(),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.attacker.conquer(this.dst);
|
||||
|
||||
@@ -111,6 +111,26 @@ describe("Attack", () => {
|
||||
expect(nuke.isActive()).toBe(false);
|
||||
expect(defender.units(UnitType.TransportShip)[0].troops()).toBeLessThan(90);
|
||||
});
|
||||
|
||||
test("Boat penalty on retreat Transport Ship arrival", async () => {
|
||||
const player_start_troops = defender.troops();
|
||||
const boat_troops = player_start_troops * 0.5;
|
||||
|
||||
sendBoat(game.ref(15, 8), game.ref(10, 5), boat_troops);
|
||||
|
||||
game.executeNextTick();
|
||||
|
||||
const ship = defender.units(UnitType.TransportShip)[0];
|
||||
expect(ship.troops()).toBe(boat_troops);
|
||||
expect(ship.isActive()).toBe(true);
|
||||
|
||||
ship.orderBoatRetreat();
|
||||
game.executeNextTick();
|
||||
|
||||
expect(ship.isActive()).toBe(false);
|
||||
expect(boat_troops).toBeLessThan(defender.troops());
|
||||
expect(defender.troops()).toBeLessThan(player_start_troops);
|
||||
});
|
||||
});
|
||||
|
||||
let playerA: Player;
|
||||
|
||||
Reference in New Issue
Block a user