From 108fad8096f20f919175c4f185a57284a87fff39 Mon Sep 17 00:00:00 2001 From: evanpelle Date: Sun, 17 Nov 2024 19:56:54 -0800 Subject: [PATCH] create territoryBound, have player execution check units for capture fix bug where PortExecution tries to get random element from empty list and crashes --- src/core/configuration/DefaultConfig.ts | 16 +++++++++++----- src/core/execution/MissileSiloExecution.ts | 4 ---- src/core/execution/PlayerExecution.ts | 15 +++++++++++++++ src/core/execution/PortExecution.ts | 13 +------------ src/core/game/Game.ts | 5 ++++- src/core/game/PlayerImpl.ts | 14 +++++++++++++- src/core/game/UnitImpl.ts | 8 ++++++-- 7 files changed, 50 insertions(+), 25 deletions(-) diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index f67fbeee4..32d99617e 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -12,26 +12,32 @@ export class DefaultConfig implements Config { case UnitType.TransportShip: return { cost: 0, + territoryBound: false } case UnitType.Destroyer: return { - cost: 100_000 + cost: 100_000, + territoryBound: false } case UnitType.Port: return { - cost: 300_000 + cost: 300_000, + territoryBound: true } case UnitType.Nuke: return { - cost: 1_000_000 + cost: 1_000_000, + territoryBound: false } case UnitType.TradeShip: return { - cost: 0 + cost: 0, + territoryBound: false } case UnitType.MissileSilo: return { - cost: 1_000_000 + cost: 1_000_000, + territoryBound: true } default: assertNever(type) diff --git a/src/core/execution/MissileSiloExecution.ts b/src/core/execution/MissileSiloExecution.ts index 6b580bcd7..b341c75ff 100644 --- a/src/core/execution/MissileSiloExecution.ts +++ b/src/core/execution/MissileSiloExecution.ts @@ -34,10 +34,6 @@ export class MissileSiloExecution implements Execution { this.active = false return } - if (this.silo.tile().owner() != this.silo.owner()) { - this.silo.setOwner(this.silo.tile().owner() as Player) - this.player = this.silo.owner() - } } owner(): MutablePlayer { diff --git a/src/core/execution/PlayerExecution.ts b/src/core/execution/PlayerExecution.ts index c667f9e26..f754bea90 100644 --- a/src/core/execution/PlayerExecution.ts +++ b/src/core/execution/PlayerExecution.ts @@ -46,6 +46,21 @@ export class PlayerExecution implements Execution { } } + this.player.units().forEach(u => { + const tileOwner = u.tile().owner() + if (u.info().territoryBound) { + if (tileOwner.isPlayer()) { + if (tileOwner != this.player) { + this.mg.player(tileOwner.id()).captureUnit(u) + } + } else { + u.delete() + } + } + + + }) + if (ticks - this.lastCalc > this.ticksPerClusterCalc) { this.lastCalc = ticks const start = performance.now() diff --git a/src/core/execution/PortExecution.ts b/src/core/execution/PortExecution.ts index e889556e7..24ca01d3e 100644 --- a/src/core/execution/PortExecution.ts +++ b/src/core/execution/PortExecution.ts @@ -46,17 +46,6 @@ export class PortExecution implements Execution { this.port = this.player.buildUnit(UnitType.Port, 0, spawns[0]) } - - if (!this.port.tile().hasOwner()) { - this.port.delete() - this.active = false - return - } - if (this.port.tile().owner() != this.port.owner()) { - this.port.setOwner(this.port.tile().owner() as Player) - this.player = this.port.owner() - } - const allPorts = this.mg.units(UnitType.Port) .filter(u => u.owner() != this.player) if (allPorts.length == 0) { @@ -82,7 +71,7 @@ export class PortExecution implements Execution { } } - if (this.random.chance(50)) { + if (this.portPaths.size > 0 && this.random.chance(50)) { const port = this.random.randElement( Array.from(this.portPaths.keys()) .filter(p => this.port.owner().isAlliedWith(p.owner())) diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 0340ecf17..002405b10 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -24,6 +24,8 @@ export enum GameMap { export interface UnitInfo { cost: Gold + // Determines if its owner changes when its tile is conquered. + territoryBound: boolean } export enum UnitType { @@ -155,6 +157,7 @@ export interface Unit { tile(): Tile owner(): Player isActive(): boolean + info(): UnitInfo } export interface MutableUnit extends Unit { @@ -162,7 +165,6 @@ export interface MutableUnit extends Unit { owner(): MutablePlayer setTroops(troops: number): void delete(): void - setOwner(newOwner: Player): void } export interface TerraNullius { @@ -247,6 +249,7 @@ export interface MutablePlayer extends Player { removeTroops(troops: number): number buildUnit(type: UnitType, troops: number, tile: Tile): MutableUnit + captureUnit(unit: MutableUnit): void } export interface Game { diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index 932d3a47b..742768010 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -1,4 +1,4 @@ -import { MutablePlayer, Tile, PlayerInfo, PlayerID, PlayerType, Player, TerraNullius, Cell, Execution, AllianceRequest, MutableAllianceRequest, MutableAlliance, Alliance, Tick, TargetPlayerEvent, EmojiMessage, EmojiMessageEvent, AllPlayers, Gold, UnitType, Unit } from "./Game"; +import { MutablePlayer, Tile, PlayerInfo, PlayerID, PlayerType, Player, TerraNullius, Cell, Execution, AllianceRequest, MutableAllianceRequest, MutableAlliance, Alliance, Tick, TargetPlayerEvent, EmojiMessage, EmojiMessageEvent, AllPlayers, Gold, UnitType, Unit, MutableUnit } from "./Game"; import { ClientID } from "../Schemas"; import { assertNever, bfs, closestOceanShoreFromPlayer, dist, distSortUnit, manhattanDist, manhattanDistWrapped, processName, simpleHash, sourceDstOceanShore } from "../Util"; import { CellString, GameImpl } from "./GameImpl"; @@ -81,6 +81,7 @@ export class PlayerImpl implements MutablePlayer { return this._units.filter(u => ts.has(u.type())); } + sharesBorderWith(other: Player | TerraNullius): boolean { for (const border of this._borderTiles) { for (const neighbor of border.neighbors()) { @@ -319,6 +320,16 @@ export class PlayerImpl implements MutablePlayer { return toRemove } + captureUnit(unit: MutableUnit): void { + if (unit.owner() == this) { + throw new Error(`Cannot capture unit, ${this} already owns ${unit}`) + } + const prev = unit.owner(); + (prev as PlayerImpl)._units = (prev as PlayerImpl)._units.filter(u => u != unit); + (unit as UnitImpl)._owner = this + this._units.push(unit as UnitImpl) + } + buildUnit(type: UnitType, troops: number, spawnTile: Tile): UnitImpl { const b = new UnitImpl(type, this.gs, spawnTile, troops, this); this._units.push(b); @@ -328,6 +339,7 @@ export class PlayerImpl implements MutablePlayer { return b; } + canBuild(unitType: UnitType, targetTile: Tile): Tile | false { const cost = this.gs.unitInfo(unitType).cost if (!this.isAlive() || this.gold() < cost) { diff --git a/src/core/game/UnitImpl.ts b/src/core/game/UnitImpl.ts index 5d411068a..05d65382d 100644 --- a/src/core/game/UnitImpl.ts +++ b/src/core/game/UnitImpl.ts @@ -1,4 +1,4 @@ -import { MutableUnit, Tile, TerraNullius, UnitType, Player } from "./Game"; +import { MutableUnit, Tile, TerraNullius, UnitType, Player, UnitInfo } from "./Game"; import { GameImpl } from "./GameImpl"; import { PlayerImpl } from "./PlayerImpl"; import { TerraNulliusImpl } from "./TerraNulliusImpl"; @@ -12,7 +12,7 @@ export class UnitImpl implements MutableUnit { private g: GameImpl, private _tile: Tile, private _troops: number, - private _owner: PlayerImpl, + public _owner: PlayerImpl, ) { } type(): UnitType { @@ -37,6 +37,10 @@ export class UnitImpl implements MutableUnit { return this._owner; } + info(): UnitInfo { + return this.g.unitInfo(this._type) + } + setOwner(newOwner: Player): void { this._owner = newOwner as PlayerImpl this.g.fireUnitUpdateEvent(this, this.tile())