Block captured trade routes from port destination selection for 100 ticks

This commit is contained in:
scamiv
2026-04-06 20:37:12 +02:00
parent e7029564a2
commit d994db70ce
6 changed files with 217 additions and 1 deletions
+75
View File
@@ -104,4 +104,79 @@ describe("PortExecution", () => {
expect(ports.length).toBe(1);
});
test("Blocked trade route is omitted from trading ports", () => {
game.config().proximityBonusPortsNb = () => 0;
game.config().tradeShipShortRangeDebuff = () => 0;
player.conquer(game.ref(7, 10));
const spawn = player.canBuild(UnitType.Port, game.ref(7, 10));
if (spawn === false) {
throw new Error("Unable to build port for test");
}
const port = player.buildUnit(UnitType.Port, spawn, {});
const execution = new PortExecution(port);
execution.init(game, 0);
other.conquer(game.ref(0, 0));
const blockedPort = other.buildUnit(UnitType.Port, game.ref(0, 0), {});
other.conquer(game.ref(0, 1));
const openPort = other.buildUnit(UnitType.Port, game.ref(0, 1), {});
game.blockTradeRouteUntil(port.id(), blockedPort.id(), game.ticks() + 100);
const ports = execution.tradingPorts();
expect(ports).toContain(openPort);
expect(ports).not.toContain(blockedPort);
});
test("Blocked trade route becomes eligible again after expiry", () => {
game.config().proximityBonusPortsNb = () => 0;
game.config().tradeShipShortRangeDebuff = () => 0;
player.conquer(game.ref(7, 10));
const spawn = player.canBuild(UnitType.Port, game.ref(7, 10));
if (spawn === false) {
throw new Error("Unable to build port for test");
}
const port = player.buildUnit(UnitType.Port, spawn, {});
const execution = new PortExecution(port);
execution.init(game, 0);
other.conquer(game.ref(0, 0));
const blockedPort = other.buildUnit(UnitType.Port, game.ref(0, 0), {});
game.blockTradeRouteUntil(port.id(), blockedPort.id(), game.ticks() + 1);
expect(execution.tradingPorts()).not.toContain(blockedPort);
expect(
game.isTradeRouteBlocked(port.id(), blockedPort.id(), game.ticks()),
).toBe(true);
expect(
game.isTradeRouteBlocked(port.id(), blockedPort.id(), game.ticks() + 1),
).toBe(false);
expect(execution.tradingPorts()).toContain(blockedPort);
});
test("Trade route blacklist affects hash and expires cleanly", () => {
player.conquer(game.ref(7, 10));
const spawn = player.canBuild(UnitType.Port, game.ref(7, 10));
if (spawn === false) {
throw new Error("Unable to build port for test");
}
const port = player.buildUnit(UnitType.Port, spawn, {});
other.conquer(game.ref(0, 0));
const otherPort = other.buildUnit(UnitType.Port, game.ref(0, 0), {});
const baseHash = (game as any).hash();
game.blockTradeRouteUntil(port.id(), otherPort.id(), game.ticks() + 100);
const blockedHash = (game as any).hash();
(game as any)._ticks += 100;
const expiredHash = (game as any).hash();
expect(blockedHash).not.toBe(baseHash);
expect(expiredHash).toBe(baseHash);
});
});
@@ -55,6 +55,7 @@ describe("TradeShipExecution", () => {
} as any;
piratePort = {
id: vi.fn(() => 201),
tile: vi.fn(() => 56),
owner: vi.fn(() => pirate),
isActive: vi.fn(() => true),
@@ -63,6 +64,7 @@ describe("TradeShipExecution", () => {
} as any;
piratePort2 = {
id: vi.fn(() => 202),
tile: vi.fn(() => 75),
owner: vi.fn(() => pirate),
isActive: vi.fn(() => true),
@@ -71,6 +73,7 @@ describe("TradeShipExecution", () => {
} as any;
srcPort = {
id: vi.fn(() => 101),
tile: vi.fn(() => 10),
owner: vi.fn(() => origOwner),
isActive: vi.fn(() => true),
@@ -79,6 +82,7 @@ describe("TradeShipExecution", () => {
} as any;
dstPort = {
id: vi.fn(() => 102),
tile: vi.fn(() => 100),
owner: vi.fn(() => dstOwner),
isActive: vi.fn(() => true),
@@ -131,6 +135,43 @@ describe("TradeShipExecution", () => {
expect(tradeShip.setTargetUnit).toHaveBeenCalledWith(piratePort);
});
it("blacklists the original route immediately on first capture", () => {
tradeShip.owner = vi.fn(() => pirate);
tradeShipExecution.tick(1);
expect(
game.isTradeRouteBlocked(srcPort.id(), dstPort.id(), game.ticks()),
).toBe(true);
});
it("keeps the blacklist on the original route after retargeting", () => {
tradeShip.owner = vi.fn(() => pirate);
tradeShipExecution.tick(1);
expect(
game.isTradeRouteBlocked(srcPort.id(), dstPort.id(), game.ticks()),
).toBe(true);
expect(
game.isTradeRouteBlocked(srcPort.id(), piratePort.id(), game.ticks()),
).toBe(false);
});
it("does not add a second blacklist event when the ship is recaptured", () => {
const routeKey = `${srcPort.id()}:${dstPort.id()}`;
tradeShip.owner = vi.fn(() => pirate);
tradeShipExecution.tick(1);
const blockedUntil = (game as any).tradeRouteBlockedUntil.get(routeKey);
tradeShip.owner = vi.fn(() => origOwner);
tradeShipExecution.tick(2);
expect((game as any).tradeRouteBlockedUntil.get(routeKey)).toBe(
blockedUntil,
);
});
it("should complete trade and award gold", () => {
tradeShipExecution["pathFinder"] = {
next: vi.fn(() => ({ status: PathStatus.COMPLETE, node: 32 })),
@@ -141,4 +182,27 @@ describe("TradeShipExecution", () => {
expect(tradeShipExecution.isActive()).toBe(false);
expect(game.displayMessage).toHaveBeenCalled();
});
it("does not blacklist on NOT_FOUND", () => {
tradeShipExecution["pathFinder"] = {
next: vi.fn(() => ({ status: PathStatus.NOT_FOUND, node: 32 })),
findPath: vi.fn((from: number) => [from]),
} as any;
tradeShipExecution.tick(1);
expect(
game.isTradeRouteBlocked(srcPort.id(), dstPort.id(), game.ticks()),
).toBe(false);
});
it("does not blacklist when destination becomes invalid", () => {
dstPort.isActive = vi.fn(() => false);
tradeShipExecution.tick(1);
expect(
game.isTradeRouteBlocked(srcPort.id(), dstPort.id(), game.ticks()),
).toBe(false);
});
});