mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 06:10:42 +00:00
Nation ship improvements (#3724)
## Description: Nations preform poorly on large water maps, these changes aims to improve how they handle and react to water combat. Adding the ability for hard and impossible nations to retaliate against incoming transport ships. Adding the ability for ships to be moved if the nation is not able to build a new ship. ## 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 - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: babyboucher --------- Co-authored-by: iamlewis <lewismmmm@gmail.com> Co-authored-by: FloPinguin <25036848+FloPinguin@users.noreply.github.com>
This commit is contained in:
@@ -93,6 +93,7 @@ export class NationExecution implements Execution {
|
||||
this.player !== null &&
|
||||
this.player.isAlive() &&
|
||||
this.mg.config().gameConfig().difficulty !== Difficulty.Easy &&
|
||||
this.player.unitsConstructed(UnitType.Port) &&
|
||||
!this.mg.config().isUnitDisabled(UnitType.Warship)
|
||||
) {
|
||||
this.warshipBehavior.trackShipsAndRetaliate();
|
||||
|
||||
@@ -21,6 +21,10 @@ export class NationWarshipBehavior {
|
||||
private trackedTransportShips: Set<Unit> = new Set();
|
||||
// Track our trade ships we currently own
|
||||
private trackedTradeShips: Set<Unit> = new Set();
|
||||
// Track incoming transport ships
|
||||
private trackedIncomingTransportShips: Set<Unit> = new Set();
|
||||
// Track incoming transport ships we have dealt with
|
||||
private dealtWithTransportShip: Set<Unit> = new Set();
|
||||
|
||||
constructor(
|
||||
private random: PseudoRandom,
|
||||
@@ -45,7 +49,7 @@ export class NationWarshipBehavior {
|
||||
this.player.gold() > this.cost(UnitType.Warship)
|
||||
) {
|
||||
const port = this.random.randElement(ports);
|
||||
const targetTile = this.warshipSpawnTile(port.tile());
|
||||
const targetTile = this.warshipSpawnTile(port.tile(), 250);
|
||||
if (targetTile === null) {
|
||||
return false;
|
||||
}
|
||||
@@ -61,8 +65,7 @@ export class NationWarshipBehavior {
|
||||
return false;
|
||||
}
|
||||
|
||||
private warshipSpawnTile(portTile: TileRef): TileRef | null {
|
||||
const radius = 250;
|
||||
private warshipSpawnTile(portTile: TileRef, radius: number): TileRef | null {
|
||||
for (let attempts = 0; attempts < 50; attempts++) {
|
||||
const randX = this.random.nextInt(
|
||||
this.game.x(portTile) - radius,
|
||||
@@ -88,6 +91,7 @@ export class NationWarshipBehavior {
|
||||
trackShipsAndRetaliate(): void {
|
||||
this.trackTransportShipsAndRetaliate();
|
||||
this.trackTradeShipsAndRetaliate();
|
||||
this.trackIncomingTransportsAndRetaliate();
|
||||
}
|
||||
|
||||
// Send out a warship if our transport ship got captured
|
||||
@@ -137,6 +141,82 @@ export class NationWarshipBehavior {
|
||||
}
|
||||
}
|
||||
|
||||
private trackIncomingTransportsAndRetaliate(): void {
|
||||
// Add any transports which are targeting us to our tracking map
|
||||
this.game
|
||||
.units(UnitType.TransportShip)
|
||||
.filter((p) => {
|
||||
const target = p.targetTile();
|
||||
return (
|
||||
target &&
|
||||
p.isActive() &&
|
||||
!p.retreating() &&
|
||||
this.game.ownerID(target) === this.player?.smallID() &&
|
||||
p.owner().smallID() !== this.player?.smallID()
|
||||
);
|
||||
})
|
||||
.forEach((p) => this.trackedIncomingTransportShips.add(p));
|
||||
|
||||
for (const transport of Array.from(this.trackedIncomingTransportShips)) {
|
||||
const target = transport.targetTile();
|
||||
if (
|
||||
!transport.isActive() ||
|
||||
target === undefined ||
|
||||
transport.retreating()
|
||||
) {
|
||||
this.trackedIncomingTransportShips.delete(transport);
|
||||
this.dealtWithTransportShip.delete(transport);
|
||||
continue;
|
||||
}
|
||||
// Transport has already been dealt with
|
||||
if (this.dealtWithTransportShip.has(transport)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const distanceToTarget = this.game.manhattanDist(
|
||||
transport.tile(),
|
||||
target,
|
||||
);
|
||||
// Too close to deal with
|
||||
if (distanceToTarget < 20) {
|
||||
this.dealtWithTransportShip.add(transport);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Possible dock snipe counter? Too niche?
|
||||
if (!transport.owner().isAlliedWith(this.player)) {
|
||||
if (
|
||||
this.game.hasUnitNearby(
|
||||
target,
|
||||
90,
|
||||
UnitType.Warship,
|
||||
this.player.id(),
|
||||
true,
|
||||
) ||
|
||||
this.player.units(UnitType.Warship).filter((p) => {
|
||||
const patrolTile = p.patrolTile();
|
||||
return (
|
||||
patrolTile !== undefined &&
|
||||
this.game.manhattanDist(target, patrolTile) < 90
|
||||
);
|
||||
}).length > 0
|
||||
) {
|
||||
this.dealtWithTransportShip.add(transport);
|
||||
continue;
|
||||
}
|
||||
const oceanTiles = this.warshipSpawnTile(target, 30);
|
||||
if (oceanTiles === null) continue;
|
||||
this.maybeRetaliateWithWarship(
|
||||
oceanTiles,
|
||||
transport.owner(),
|
||||
"transport",
|
||||
);
|
||||
this.dealtWithTransportShip.add(transport);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private maybeRetaliateWithWarship(
|
||||
tile: TileRef,
|
||||
enemy: Player,
|
||||
@@ -149,6 +229,7 @@ export class NationWarshipBehavior {
|
||||
|
||||
// Don't send too many warships
|
||||
if (this.player.units(UnitType.Warship).length >= 10) {
|
||||
this.maybeMoveWarship(tile);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -161,6 +242,7 @@ export class NationWarshipBehavior {
|
||||
) {
|
||||
const canBuild = this.player.canBuild(UnitType.Warship, tile);
|
||||
if (canBuild === false) {
|
||||
this.maybeMoveWarship(tile);
|
||||
return;
|
||||
}
|
||||
this.game.addExecution(
|
||||
@@ -171,6 +253,32 @@ export class NationWarshipBehavior {
|
||||
}
|
||||
}
|
||||
|
||||
private maybeMoveWarship(tile: TileRef): void {
|
||||
// Make sure we are targeting water
|
||||
if (this.game.isWater(tile)) {
|
||||
const warship = this.player
|
||||
.units(UnitType.Warship)
|
||||
.filter((p) => {
|
||||
const patrolTile = p.patrolTile();
|
||||
return (
|
||||
patrolTile !== undefined &&
|
||||
// Dont send ships which are already traveling
|
||||
this.game.manhattanDist(p.tile(), patrolTile) < 130
|
||||
);
|
||||
})
|
||||
.sort((a, b) => {
|
||||
// Sort by distance (closest first)
|
||||
const distA = this.game.manhattanDist(a.tile(), tile);
|
||||
const distB = this.game.manhattanDist(b.tile(), tile);
|
||||
return distA - distB;
|
||||
})[0];
|
||||
|
||||
if (warship) {
|
||||
warship.setPatrolTile(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent warship infestations: if current player is one of the 3 richest and an enemy has too many warships, send a counter-warship.
|
||||
// What is a warship infestation? A player tries to dominate the entire ocean to block all trade and transport boats.
|
||||
counterWarshipInfestation(): void {
|
||||
@@ -335,6 +443,7 @@ export class NationWarshipBehavior {
|
||||
target.warship.tile(),
|
||||
);
|
||||
if (canBuild === false) {
|
||||
this.maybeMoveWarship(target.warship.tile());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user