diff --git a/src/client/graphics/layers/UnitLayer.ts b/src/client/graphics/layers/UnitLayer.ts index 3f2d8ae75..c6b93aa69 100644 --- a/src/client/graphics/layers/UnitLayer.ts +++ b/src/client/graphics/layers/UnitLayer.ts @@ -78,6 +78,8 @@ export class UnitLayer implements Layer { case UnitType.Destroyer: this.handleDestroyerEvent(event); break; + case UnitType.TradeShip: + this.handleTradeShipEvent(event) } } @@ -91,6 +93,16 @@ export class UnitLayer implements Layer { .forEach(t => this.paintCell(t.cell(), this.theme.territoryColor(event.unit.owner().info()), 180)); } + private handleTradeShipEvent(event: UnitEvent) { + bfs(event.oldTile, euclDist(event.oldTile, 1)).forEach(t => { + this.clearCell(t.cell()); + }); + if (event.unit.isActive()) { + bfs(event.unit.tile(), euclDist(event.unit.tile(), 1)) + .forEach(t => this.paintCell(t.cell(), this.theme.borderColor(event.unit.owner().info()), 255)); + } + } + private handleBoatEvent(event: UnitEvent) { if (!this.boatToTrail.has(event.unit)) { this.boatToTrail.set(event.unit, new Set()); diff --git a/src/core/execution/PortExecution.ts b/src/core/execution/PortExecution.ts index 20abd3d5a..107e80715 100644 --- a/src/core/execution/PortExecution.ts +++ b/src/core/execution/PortExecution.ts @@ -1,6 +1,9 @@ import { BuildValidator } from "../game/BuildValidator"; -import { AllPlayers, BuildItem, BuildItems, Cell, Execution, MutableGame, MutablePlayer, MutableUnit, Player, PlayerID, UnitType } from "../game/Game"; +import { AllPlayers, BuildItem, BuildItems, Cell, Execution, MutableGame, MutablePlayer, MutableUnit, Player, PlayerID, Tile, Unit, UnitType } from "../game/Game"; +import { AStar, PathFinder } from "../PathFinding"; +import { PseudoRandom } from "../PseudoRandom"; import { bfs, dist, manhattanDist } from "../Util"; +import { TradeShipExecution } from "./TradeShipExecution"; export class PortExecution implements Execution { @@ -8,6 +11,9 @@ export class PortExecution implements Execution { private mg: MutableGame private player: MutablePlayer private port: MutableUnit + private random: PseudoRandom + private portPaths = new Map() + private computingPaths = new Map() constructor( private _owner: PlayerID, @@ -18,6 +24,7 @@ export class PortExecution implements Execution { init(mg: MutableGame, ticks: number): void { this.mg = mg this.player = mg.player(this._owner) + this.random = new PseudoRandom(mg.ticks()) } tick(ticks: number): void { @@ -39,6 +46,8 @@ export class PortExecution implements Execution { } this.port = this.player.addUnit(UnitType.Port, 0, spawns[0]) } + + if (!this.port.tile().hasOwner()) { this.port.delete() this.active = false @@ -47,6 +56,31 @@ export class PortExecution implements Execution { if (this.port.tile().owner() != this.port.owner()) { this.port.setOwner(this.port.tile().owner() as Player) } + + const ports = this.mg.units(UnitType.Port) + .filter(u => u.owner() != this.player) + if (ports.length == 0) { + return + } + for (const port of ports) { + if (this.computingPaths.has(port)) { + const aStar = this.computingPaths.get(port) + if (aStar.compute(10_000)) { + this.portPaths.set(port, aStar.reconstructPath()) + this.computingPaths.delete(port) + } + continue + } + if (!this.portPaths.has(port)) { + this.computingPaths.set(port, new AStar(this.port.tile(), port.tile())) + continue + } + } + if (this.random.chance(50)) { + const port = this.random.randElement(Array.from(this.portPaths.keys())) + const path = this.portPaths.get(port) + this.mg.addExecution(new TradeShipExecution(this._owner, this.port, port, path)) + } } owner(): MutablePlayer { diff --git a/src/core/execution/TradeShipExecution.ts b/src/core/execution/TradeShipExecution.ts new file mode 100644 index 000000000..74c29cf4a --- /dev/null +++ b/src/core/execution/TradeShipExecution.ts @@ -0,0 +1,56 @@ +import { BuildValidator } from "../game/BuildValidator"; +import { AllPlayers, BuildItem, BuildItems, Cell, Execution, MutableGame, MutablePlayer, MutableUnit, Player, PlayerID, Tile, Unit, UnitType } from "../game/Game"; +import { AStar, PathFinder } from "../PathFinding"; +import { PseudoRandom } from "../PseudoRandom"; +import { bfs, dist, manhattanDist } from "../Util"; + +export class TradeShipExecution implements Execution { + + private active = true + private mg: MutableGame + private player: MutablePlayer + private tradeShip: MutableUnit + private index = 0 + + constructor( + private _owner: PlayerID, + private srcPort: MutableUnit, + private dstPort: MutableUnit, + // don't modify + private path: Tile[] + ) { } + + + init(mg: MutableGame, ticks: number): void { + this.mg = mg + this.player = mg.player(this._owner) + } + + tick(ticks: number): void { + if (this.tradeShip == null) { + this.tradeShip = this.player.addUnit(UnitType.TradeShip, 0, this.srcPort.tile()) + } + if (this.index >= this.path.length) { + this.active = false + this.tradeShip.delete() + this.srcPort.owner().addGold(10_000) + this.dstPort.owner().addGold(10_000) + return + } + this.tradeShip.move(this.path[this.index]) + this.index++ + } + + owner(): MutablePlayer { + return null + } + + isActive(): boolean { + return this.active + } + + activeDuringSpawnPhase(): boolean { + return false + } + +} \ No newline at end of file diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 5e560ca7b..a3bbe96cf 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -1,10 +1,7 @@ -import { info } from "console" import { Config } from "../configuration/Config" import { GameEvent } from "../EventBus" import { ClientID, GameID } from "../Schemas" -import { DisplayMessageEvent, MessageType } from "../../client/graphics/layers/EventsDisplay" -import { BreakAllianceExecution } from "../execution/alliance/BreakAllianceExecution" -import { DonateExecution } from "../execution/DonateExecution" +import { MessageType } from "../../client/graphics/layers/EventsDisplay" export type PlayerID = string export type Tick = number @@ -30,6 +27,7 @@ export enum UnitType { Destroyer = "Destroyer", Port = "Port", Nuke = "Nuke", + TradeShip = "Trade Ship", } export class BuildItem {