Merge branch 'main' into 2393-show-boat-troops-as-attacking-troops

This commit is contained in:
Rj Manhas
2025-11-11 17:34:52 -07:00
committed by GitHub
12 changed files with 198 additions and 14 deletions
+80
View File
@@ -1,5 +1,6 @@
import {
Cell,
Difficulty,
Execution,
Game,
Gold,
@@ -46,6 +47,11 @@ export class FakeHumanExecution implements Execution {
private readonly lastMIRVSent: [Tick, TileRef][] = [];
private readonly embargoMalusApplied = new Set<PlayerID>();
// Track our transport ships we currently own
private trackedTransportShips: Set<Unit> = new Set();
// Track our trade ships we currently own
private trackedTradeShips: Set<Unit> = new Set();
/** MIRV Strategy Constants */
/** Ticks until MIRV can be attempted again */
@@ -133,6 +139,16 @@ export class FakeHumanExecution implements Execution {
}
tick(ticks: number) {
// Ship tracking
if (
this.player !== null &&
this.player.isAlive() &&
this.mg.config().gameConfig().difficulty !== Difficulty.Easy
) {
this.trackTransportShipsAndRetaliate();
this.trackTradeShipsAndRetaliate();
}
if (ticks % this.attackRate !== this.attackTick) {
return;
}
@@ -901,6 +917,70 @@ export class FakeHumanExecution implements Execution {
}
}
// Send out a warship if our transport ship got captured
private trackTransportShipsAndRetaliate(): void {
if (this.player === null) return;
// Add any currently owned transport ships to our tracking set
this.player
.units(UnitType.TransportShip)
.forEach((u) => this.trackedTransportShips.add(u));
// Iterate tracked transport ships; if it got destroyed by an enemy: retaliate
for (const ship of Array.from(this.trackedTransportShips)) {
if (!ship.isActive()) {
// Distinguish between arrival/retreat and enemy destruction
if (ship.wasDestroyedByEnemy()) {
this.maybeRetaliateWithWarship(ship.tile());
}
this.trackedTransportShips.delete(ship);
}
}
}
// Send out a warship if our trade ship got captured
private trackTradeShipsAndRetaliate(): void {
if (this.player === null) return;
// Add any currently owned trade ships to our tracking map
this.player
.units(UnitType.TradeShip)
.forEach((u) => this.trackedTradeShips.add(u));
// Iterate tracked trade ships; if we no longer own it, it was captured: retaliate
for (const ship of Array.from(this.trackedTradeShips)) {
if (!ship.isActive()) {
this.trackedTradeShips.delete(ship);
continue;
}
if (ship.owner().id() !== this.player.id()) {
// Ship was ours and is now owned by someone else -> captured
this.maybeRetaliateWithWarship(ship.tile());
this.trackedTradeShips.delete(ship);
}
}
}
private maybeRetaliateWithWarship(tile: TileRef): void {
if (this.player === null) return;
const { difficulty } = this.mg.config().gameConfig();
// In Easy never retaliate. In Medium retaliate with 15% chance. Hard with 50%, Impossible with 80%.
if (
(difficulty === Difficulty.Medium && this.random.nextInt(0, 100) < 15) ||
(difficulty === Difficulty.Hard && this.random.nextInt(0, 100) < 50) ||
(difficulty === Difficulty.Impossible && this.random.nextInt(0, 100) < 80)
) {
const canBuild = this.player.canBuild(UnitType.Warship, tile);
if (canBuild === false) {
return;
}
this.mg.addExecution(
new ConstructionExecution(this.player, UnitType.Warship, tile),
);
}
}
isActive(): boolean {
return this.active;
}
+1
View File
@@ -451,6 +451,7 @@ export interface Unit {
toUpdate(): UnitUpdate;
hasTrainStation(): boolean;
setTrainStation(trainStation: boolean): void;
wasDestroyedByEnemy(): boolean;
// Train
trainType(): TrainType | undefined;
+9
View File
@@ -24,6 +24,7 @@ export class UnitImpl implements Unit {
private _retreating: boolean = false;
private _targetedBySAM = false;
private _reachedTarget = false;
private _wasDestroyedByEnemy: boolean = false;
private _lastSetSafeFromPirates: number; // Only for trade ships
private _constructionType: UnitType | undefined;
private _lastOwner: PlayerImpl | null = null;
@@ -254,6 +255,10 @@ export class UnitImpl implements Unit {
if (!this.isActive()) {
throw new Error(`cannot delete ${this} not active`);
}
// Record whether this unit was destroyed by an enemy (vs. arrived / retreated)
this._wasDestroyedByEnemy = destroyer !== undefined;
this._owner._units = this._owner._units.filter((b) => b !== this);
this._active = false;
this.mg.addUpdate(this.toUpdate());
@@ -307,6 +312,10 @@ export class UnitImpl implements Unit {
return this._active;
}
wasDestroyedByEnemy(): boolean {
return this._wasDestroyedByEnemy;
}
retreating(): boolean {
return this._retreating;
}