From a17ae48cd3ae278020cdf2ad53d78349ccd14074 Mon Sep 17 00:00:00 2001 From: evanpelle Date: Wed, 15 Jan 2025 12:57:09 -0800 Subject: [PATCH] use TileRef instead of tile --- src/client/graphics/NameBoxCalculator.ts | 7 ++ src/core/GameView.ts | 9 ++- src/core/Util.ts | 23 +++---- src/core/configuration/DefaultConfig.ts | 10 ++- src/core/execution/AttackExecution.ts | 7 +- src/core/execution/BotExecution.ts | 8 +-- src/core/execution/FakeHumanExecution.ts | 2 +- src/core/execution/PlayerExecution.ts | 84 ++++++++++++------------ src/core/game/Game.ts | 7 +- src/core/game/GameImpl.ts | 49 ++++++++------ src/core/game/GameMap.ts | 13 +++- src/core/game/PlayerImpl.ts | 24 ++++--- src/core/game/TileImpl.ts | 4 +- 13 files changed, 149 insertions(+), 98 deletions(-) diff --git a/src/client/graphics/NameBoxCalculator.ts b/src/client/graphics/NameBoxCalculator.ts index 9703fa9ec..73d1f8806 100644 --- a/src/client/graphics/NameBoxCalculator.ts +++ b/src/client/graphics/NameBoxCalculator.ts @@ -15,9 +15,16 @@ export interface Rectangle { } + export function placeName(game: Game, player: Player): NameViewData { + return { + x: 0, + y: 0, + size: 0 + } const boundingBox = calculateBoundingBox(player.borderTiles()); + const rawScalingFactor = (boundingBox.max.x - boundingBox.min.x) / 100 const scalingFactor = within(Math.floor(rawScalingFactor), 1, 1000) diff --git a/src/core/GameView.ts b/src/core/GameView.ts index b9a6c9ed0..f30b17831 100644 --- a/src/core/GameView.ts +++ b/src/core/GameView.ts @@ -14,7 +14,9 @@ export class TileView { constructor(private game: GameView, public data: TileUpdate, private _terrain: TerrainTile) { } ref(): TileRef { - throw new Error('uh oh') + if (!this.data) { return 0 } + + return this.data.pos.x * this.game.width() + this.data.pos.y } type(): TerrainType { return this._terrain.type() @@ -123,6 +125,9 @@ export class UnitView implements Unit { export class PlayerView implements Player { constructor(private game: GameView, public data: PlayerUpdate, public nameData: NameViewData) { } + borderTiles(): ReadonlySet { + throw new Error('Method not implemented.'); + } async actions(tile: Tile): Promise { return this.game.worker.playerInteraction(this.id(), tile) @@ -187,7 +192,7 @@ export class PlayerView implements Player { allianceWith(other: Player): Alliance | null { return null } - borderTiles(): ReadonlySet { + borderTileRefs(): ReadonlySet { return new Set() } units(...types: UnitType[]): Unit[] { diff --git a/src/core/Util.ts b/src/core/Util.ts index 728152f2e..4666aa2bc 100644 --- a/src/core/Util.ts +++ b/src/core/Util.ts @@ -8,6 +8,7 @@ import { number } from 'zod'; import { GameConfig, GameID, GameRecord, PlayerRecord, Turn } from './Schemas'; import { customAlphabet, nanoid } from 'nanoid'; import { GameView } from './GameView'; +import { TileRef } from './game/GameMap'; @@ -110,19 +111,19 @@ function closestOceanShoreTN(tile: Tile, searchDist: number): Tile { } export function bfs(tile: Tile, filter: (tile: Tile) => boolean): Set { - const seen = new Set + const seen = new Map() const q: Tile[] = [] q.push(tile) while (q.length > 0) { const curr = q.pop() - seen.add(curr) + seen.set(curr.ref(), curr) for (const n of curr.neighbors()) { - if (!seen.has(n) && filter(n)) { + if (!seen.has(n.ref()) && filter(n)) { q.push(n) } } } - return seen + return new Set(seen.values()) } export function simpleHash(str: string): number { @@ -166,20 +167,20 @@ export function inscribed(outer: { min: Cell; max: Cell }, inner: { min: Cell; m ); } -export function getMode(list: string[]): string { +export function getMode(list: number[]): number { // Count occurrences - const counts: { [key: string]: number } = {}; + const counts = new Map() for (const item of list) { - counts[item] = (counts[item] || 0) + 1; + counts.set(item, (counts.get(item) || 0) + 1); } // Find the item with the highest count - let mode = ''; + let mode = 0; let maxCount = 0; - for (const item in counts) { - if (counts[item] > maxCount) { - maxCount = counts[item]; + for (const [item, count] of counts) { + if (count > maxCount) { + maxCount = count mode = item; } } diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index 8aeeb76ff..894c1434d 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -189,7 +189,8 @@ export class DefaultConfig implements Config { attackLogic(attackTroops: number, attacker: Player, defender: Player | TerraNullius, tileToConquer: MutableTile): { attackerTroopLoss: number; defenderTroopLoss: number; tilesPerTickUsed: number } { let mag = 0 let speed = 0 - switch (tileToConquer.terrain().type()) { + const type = tileToConquer.terrain().type() + switch (type) { case TerrainType.Plains: mag = 80 speed = 15 @@ -202,9 +203,12 @@ export class DefaultConfig implements Config { mag = 120 speed = 25 break + default: + throw new Error(`terrain type ${type} not supported`) } - mag *= tileToConquer.defenseBonus(attacker) - speed *= tileToConquer.defenseBonus(attacker) + // TODO + // mag *= tileToConquer.defenseBonus(attacker) + // speed *= tileToConquer.defenseBonus(attacker) if (tileToConquer.hasFallout()) { mag *= this.falloutDefenseModifier() speed *= this.falloutDefenseModifier() diff --git a/src/core/execution/AttackExecution.ts b/src/core/execution/AttackExecution.ts index 07ff5c9ee..6535bbc3e 100644 --- a/src/core/execution/AttackExecution.ts +++ b/src/core/execution/AttackExecution.ts @@ -4,6 +4,7 @@ import { PseudoRandom } from "../PseudoRandom"; import { manhattanDist } from "../Util"; import { MessageType } from '../game/Game'; import { renderNumber } from "../../client/Utils"; +import { TileRef } from "../game/GameMap"; export class AttackExecution implements Execution { private breakAlliance = false @@ -25,7 +26,7 @@ export class AttackExecution implements Execution { private mg: MutableGame - private border = new Set() + private border = new Set() constructor( private troops: number | null, @@ -153,7 +154,7 @@ export class AttackExecution implements Execution { } const tileToConquer = this.toConquer.dequeue().tile - this.border.delete(tileToConquer) + this.border.delete(tileToConquer.ref()) const onBorder = tileToConquer.neighbors().filter(t => t.owner() == this._owner).length > 0 if (tileToConquer.owner() != this.target || !onBorder) { @@ -176,7 +177,7 @@ export class AttackExecution implements Execution { if (neighbor.terrain().isWater() || neighbor.owner() != this.target) { continue } - this.border.add(neighbor) + this.border.add(neighbor.ref()) let numOwnedByMe = neighbor.neighbors() .filter(t => t.terrain().isLand()) .filter(t => t.owner() == this._owner) diff --git a/src/core/execution/BotExecution.ts b/src/core/execution/BotExecution.ts index 26bd6b082..c3f0b837d 100644 --- a/src/core/execution/BotExecution.ts +++ b/src/core/execution/BotExecution.ts @@ -1,7 +1,7 @@ -import {Cell, Execution, MutableGame, MutablePlayer, Player, PlayerID, PlayerInfo, PlayerType, TerraNullius} from "../game/Game" -import {PseudoRandom} from "../PseudoRandom" -import {simpleHash} from "../Util"; -import {AttackExecution} from "./AttackExecution"; +import { Cell, Execution, MutableGame, MutablePlayer, Player, PlayerID, PlayerInfo, PlayerType, TerraNullius } from "../game/Game" +import { PseudoRandom } from "../PseudoRandom" +import { simpleHash } from "../Util"; +import { AttackExecution } from "./AttackExecution"; export class BotExecution implements Execution { diff --git a/src/core/execution/FakeHumanExecution.ts b/src/core/execution/FakeHumanExecution.ts index a1c88462a..3001de81f 100644 --- a/src/core/execution/FakeHumanExecution.ts +++ b/src/core/execution/FakeHumanExecution.ts @@ -400,7 +400,7 @@ export class FakeHumanExecution implements Execution { } if (oceanShore == null) { - oceanShore = Array.from(this.player.borderTiles()).filter(t => t.terrain().isOceanShore()) + oceanShore = Array.from(this.player.borderTileRefs()).filter(t => this.mg.M.isOceanShore(t)).map(tr => this.mg.fromRef(tr)) } if (oceanShore.length == 0) { return diff --git a/src/core/execution/PlayerExecution.ts b/src/core/execution/PlayerExecution.ts index 4ca2c8018..13fb0934b 100644 --- a/src/core/execution/PlayerExecution.ts +++ b/src/core/execution/PlayerExecution.ts @@ -3,6 +3,7 @@ import { Execution, MutableGame, MutablePlayer, Player, PlayerID, TerraNullius, import { bfs, calculateBoundingBox, getMode, inscribed, simpleHash } from "../Util" import { GameImpl } from "../game/GameImpl" import { consolex } from "../Consolex" +import { TileRef } from "../game/GameMap" export class PlayerExecution implements Execution { @@ -79,7 +80,8 @@ export class PlayerExecution implements Execution { if (this.player.lastTileChange() > this.lastCalc) { this.lastCalc = ticks const start = performance.now() - this.removeClusters() + // TODO + // this.removeClusters() const end = performance.now() if (end - start > 1000) { consolex.log(`player ${this.player.name()}, took ${end - start}ms`) @@ -109,15 +111,15 @@ export class PlayerExecution implements Execution { } } - private surroundedBySamePlayer(cluster: Set): false | Player { - const enemies = new Set() - for (const tile of cluster) { - if (tile.terrain().isOceanShore() || tile.neighbors().find(n => !n.hasOwner())) { + private surroundedBySamePlayer(cluster: Set): false | Player { + const enemies = new Set() + for (const ref of cluster) { + if (this.mg.M.isOceanShore(ref) || this.mg.M.neighbors(ref).some(n => !this.mg.M.hasOwner(n))) { return false } - tile.neighbors() - .filter(n => n.hasOwner() && n.owner() != this.player) - .forEach(p => enemies.add(p.owner() as Player)) + this.mg.M.neighbors(ref) + .filter(n => this.mg.M.ownerID(n) != this.player.smallID()) + .forEach(p => enemies.add(this.mg.M.ownerID(p))) if (enemies.size != 1) { return false } @@ -125,63 +127,63 @@ export class PlayerExecution implements Execution { if (enemies.size != 1) { return false } - return Array.from(enemies)[0] + return this.mg.playerBySmallID(Array.from(enemies)[0]) as Player } - private isSurrounded(cluster: Set): boolean { - let enemyTiles = new Set() - for (const tile of cluster) { - if (tile.terrain().isOceanShore()) { + private isSurrounded(cluster: Set): boolean { + let enemyTiles = new Set() + for (const tr of cluster) { + if (this.mg.M.isOceanShore(tr)) { return false } - tile.neighbors() - .filter(n => n.hasOwner() && n.owner() != this.player) + this.mg.M.neighbors(tr) + .filter(n => this.mg.M.ownerID(n) != this.player.smallID()) .forEach(n => enemyTiles.add(n)) } if (enemyTiles.size == 0) { return false } - const enemyBox = calculateBoundingBox(enemyTiles) - const clusterBox = calculateBoundingBox(cluster) + const enemyBox = calculateBoundingBox(new Set(Array.from(enemyTiles).map(tr => this.mg.fromRef(tr)))) + const clusterBox = calculateBoundingBox(new Set(Array.from(cluster).map(tr => this.mg.fromRef(tr)))) return inscribed(enemyBox, clusterBox) } - private removeCluster(cluster: Set) { + private removeCluster(cluster: Set) { const arr = Array.from(cluster) - if (arr.some(t => t.owner() != this.player)) { - // Other removeCluster operations could change tile owners, - // so double check. - return - } - const mode = getMode(arr.flatMap(t => t.neighbors()).filter(t => t.hasOwner() && t.owner() != this.player).map(t => t.owner().id())) - if (!this.mg.hasPlayer(mode)) { + const mode = getMode( + arr. + flatMap(t => this.mg.M.neighbors(t)) + .filter(t => this.mg.M.ownerID(t) != this.player.smallID()) + .map(t => this.mg.M.ownerID(t)) + ) + if (!this.mg.playerBySmallID(mode).isPlayer()) { consolex.warn('mode is not found') return } const firstTile = arr[0] - const filter = (n: Tile): boolean => n.owner() == firstTile.owner() - const tiles = bfs(firstTile, filter) + const filter = (n: Tile): boolean => n.owner().smallID() == this.mg.M.ownerID(firstTile) + const tiles = bfs(this.mg.fromRef(firstTile), filter) - const modePlayer = this.mg.player(mode) - if (modePlayer == null) { + const modePlayer = this.mg.playerBySmallID(mode) + if (!modePlayer.isPlayer()) { consolex.warn('mode player is null') } for (const tile of tiles) { - modePlayer.conquer(tile) + (modePlayer as MutablePlayer).conquer(tile) } } - private calculateClusters(): Set[] { - const seen = new Set() - const border = this.player.borderTiles() - const clusters: Set[] = [] + private calculateClusters(): Set[] { + const seen = new Set() + const border = this.player.borderTileRefs() + const clusters: Set[] = [] for (const tile of border) { if (seen.has(tile)) { continue } - const cluster = new Set() - const queue: Tile[] = [tile] + const cluster = new Set() + const queue: TileRef[] = [tile] seen.add(tile) let loops = 0; while (queue.length > 0) { @@ -189,12 +191,12 @@ export class PlayerExecution implements Execution { const curr = queue.shift() cluster.add(curr) - const neighbors = (this.mg as GameImpl).neighborsWithDiag(curr) + const neighbors = (this.mg as GameImpl).neighborsWithDiag(this.mg.fromRef(curr)) for (const neighbor of neighbors) { - if (neighbor.isBorder() && border.has(neighbor)) { - if (!seen.has(neighbor)) { - queue.push(neighbor) - seen.add(neighbor) + if (neighbor.isBorder() && border.has(neighbor.ref())) { + if (!seen.has(neighbor.ref())) { + queue.push(neighbor.ref()) + seen.add(neighbor.ref()) } } } diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 9895a80cd..ca66c9e8c 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -272,6 +272,7 @@ export interface Player { type(): PlayerType units(...types: UnitType[]): Unit[] isAlive(): boolean + borderTileRefs(): ReadonlySet borderTiles(): ReadonlySet isPlayer(): this is Player numTilesOwned(): number @@ -316,7 +317,7 @@ export interface MutablePlayer extends Player { neighbors(): (Player | TerraNullius)[] tiles(): ReadonlySet ownsTile(cell: Cell): boolean - tiles(): ReadonlySet + tiles(): ReadonlySet conquer(tile: Tile): void relinquish(tile: Tile): void executions(): Execution[] @@ -354,6 +355,7 @@ export interface MutablePlayer extends Player { } export interface Game { + M: GameMap // Throws exception is player not found player(id: PlayerID): Player playerByClientID(id: ClientID): Player | null @@ -376,7 +378,8 @@ export interface Game { displayMessage(message: string, type: MessageType, playerID: PlayerID | null): void units(...types: UnitType[]): Unit[] unitInfo(type: UnitType): UnitInfo - + playerBySmallID(id: number): Player | TerraNullius + fromRef(ref: TileRef): Tile map(): GameMap miniMap(): GameMap } diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index efa2bb7fc..a170fe780 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -27,6 +27,8 @@ export class GameImpl implements MutableGame { private nations_: Nation[] = [] _players: Map = new Map + _playersBySmallID = [] + private execs: Execution[] = [] private _width: number private _height: number @@ -41,14 +43,14 @@ export class GameImpl implements MutableGame { private updates: GameUpdates = createGameUpdatesMap() constructor( - private gameMap: GameMap, + public readonly M: GameMap, private miniGameMap: GameMap, nationMap: NationMap, private _config: Config, ) { this._terraNullius = new TerraNulliusImpl() - this._width = gameMap.width(); - this._height = gameMap.height(); + this._width = M.width(); + this._height = M.height(); this.nations_ = nationMap.nations .map(n => new Nation( n.name, @@ -56,8 +58,14 @@ export class GameImpl implements MutableGame { n.strength )) } + playerBySmallID(id: number): Player | TerraNullius { + if (id == 0) { + return this.terraNullius() + } + return this._playersBySmallID[id - 1] + } map(): GameMap { - return this.gameMap + return this.M } miniMap(): GameMap { return this.miniGameMap @@ -79,7 +87,7 @@ export class GameImpl implements MutableGame { if (tile.hasOwner()) { throw Error(`cannot set fallout, tile ${tile} has owner`) } - this.gameMap.setFallout(tile.ref(), true) + this.M.setFallout(tile.ref(), true) this.addUpdate(ti.toUpdate()) } @@ -271,6 +279,7 @@ export class GameImpl implements MutableGame { addPlayer(playerInfo: PlayerInfo, manpower: number): MutablePlayer { let player = new PlayerImpl(this, this.nextPlayerID, playerInfo, manpower) + this._playersBySmallID.push(player) this.nextPlayerID++ this._players.set(playerInfo.id, player) return player @@ -295,7 +304,7 @@ export class GameImpl implements MutableGame { tile(cell: Cell): MutableTile { this.assertIsOnMap(cell) - return new TileImpl(this, this.gameMap.ref(cell.x, cell.y)) + return new TileImpl(this, this.M.ref(cell.x, cell.y)) } isOnMap(cell: Cell): boolean { @@ -310,7 +319,7 @@ export class GameImpl implements MutableGame { } neighbors(tile: Tile): Tile[] { - return this.gameMap.neighbors(tile.ref()).map(tr => new TileImpl(this, tr)) + return this.M.neighbors(tile.ref()).map(tr => new TileImpl(this, tr)) } neighborsWithDiag(tile: Tile): Tile[] { @@ -323,7 +332,7 @@ export class GameImpl implements MutableGame { const newX = x + dx const newY = y + dy if (newX >= 0 && newX < this._width && newY >= 0 && newY < this._height) { - ns.push(this.fromRef(this.gameMap.ref(newX, newY))) + ns.push(this.fromRef(this.M.ref(newX, newY))) } } } @@ -345,14 +354,14 @@ export class GameImpl implements MutableGame { if (previousOwner.isPlayer()) { previousOwner._lastTileChange = this._ticks previousOwner._tiles.delete(tile.cell().toString()) - previousOwner._borderTiles.delete(tileImpl) - this.gameMap.setBorder(tileImpl.ref(), false) + previousOwner._borderTiles.delete(tileImpl.ref()) + this.M.setBorder(tileImpl.ref(), false) } - this.gameMap.setPlayerId(tileImpl.ref(), owner.smallID()) + this.M.setOwnerID(tileImpl.ref(), owner.smallID()) owner._tiles.set(tile.cell().toString(), tile) owner._lastTileChange = this._ticks this.updateBorders(tile) - this.gameMap.setFallout(tileImpl.ref(), false) + this.M.setFallout(tileImpl.ref(), false) this.addUpdate((tile as TileImpl).toUpdate()) } @@ -368,10 +377,10 @@ export class GameImpl implements MutableGame { let previousOwner = tileImpl.owner() as PlayerImpl previousOwner._lastTileChange = this._ticks previousOwner._tiles.delete(tile.cell().toString()) - previousOwner._borderTiles.delete(tileImpl) - this.gameMap.setBorder(tileImpl.ref(), false) + previousOwner._borderTiles.delete(tileImpl.ref()) + this.M.setBorder(tileImpl.ref(), false) - this.gameMap.setPlayerId(tileImpl.ref(), 0) + this.M.setOwnerID(tileImpl.ref(), 0) this.updateBorders(tile) this.addUpdate( (tile as TileImpl).toUpdate() @@ -385,15 +394,15 @@ export class GameImpl implements MutableGame { for (const t of tiles) { if (!t.hasOwner()) { - this.gameMap.setBorder(t.ref(), false) + this.M.setBorder(t.ref(), false) continue } if (this.isBorder(t)) { - (t.owner() as PlayerImpl)._borderTiles.add(t); - this.gameMap.setBorder(t.ref(), true) + (t.owner() as PlayerImpl)._borderTiles.add(t.ref()); + this.M.setBorder(t.ref(), true) } else { - (t.owner() as PlayerImpl)._borderTiles.delete(t); - this.gameMap.setBorder(t.ref(), false) + (t.owner() as PlayerImpl)._borderTiles.delete(t.ref()); + this.M.setBorder(t.ref(), false) } // this.updates.push(t.toUpdate()) } diff --git a/src/core/game/GameMap.ts b/src/core/game/GameMap.ts index 323ff2e3e..f8cca188f 100644 --- a/src/core/game/GameMap.ts +++ b/src/core/game/GameMap.ts @@ -65,6 +65,10 @@ export class GameMap { return Boolean(this.terrain[ref] & (1 << GameMap.IS_LAND_BIT)); } + isOceanShore(ref: TileRef): boolean { + return this.isLand(ref) && this.neighbors(ref).some(tr => this.isOcean(tr)) + } + isOcean(ref: TileRef): boolean { return Boolean(this.terrain[ref] & (1 << GameMap.OCEAN_BIT)); } @@ -78,11 +82,16 @@ export class GameMap { } // State getters and setters (mutable) - playerId(ref: TileRef): number { + ownerID(ref: TileRef): number { return this.state[ref] & GameMap.PLAYER_ID_MASK; } - setPlayerId(ref: TileRef, playerId: number): void { + hasOwner(ref: TileRef): boolean { + return this.ownerID(ref) != 0 + } + + + setOwnerID(ref: TileRef, playerId: number): void { if (playerId > GameMap.PLAYER_ID_MASK) { throw new Error(`Player ID ${playerId} exceeds maximum value ${GameMap.PLAYER_ID_MASK}`); } diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index 3538fb039..bfc4f790c 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -7,6 +7,7 @@ import { TileImpl } from "./TileImpl"; import { MessageType } from './Game'; import { renderTroops } from "../../client/Utils"; import { TerraNulliusImpl } from "./TerraNulliusImpl"; +import { TileRef } from "./GameMap"; interface Target { tick: Tick @@ -28,7 +29,7 @@ export class PlayerImpl implements MutablePlayer { isTraitor_ = false - public _borderTiles: Set = new Set(); + public _borderTiles: Set = new Set(); public _units: UnitImpl[] = []; public _tiles: Map = new Map(); @@ -111,8 +112,8 @@ export class PlayerImpl implements MutablePlayer { sharesBorderWith(other: Player | TerraNullius): boolean { for (const border of this._borderTiles) { - for (const neighbor of border.neighbors()) { - if (neighbor.owner() == other) { + for (const neighbor of this.gs.map().neighbors(border)) { + if (this.gs.map().ownerID(neighbor) == other.smallID()) { return true; } } @@ -127,16 +128,23 @@ export class PlayerImpl implements MutablePlayer { return new Set(this._tiles.values()) as Set; } - borderTiles(): ReadonlySet { + borderTileRefs(): ReadonlySet { return this._borderTiles; } + borderTiles(): ReadonlySet { + return new Set(Array.from(this._borderTiles).map(tr => this.gs.fromRef(tr))) + } + neighbors(): (MutablePlayer | TerraNullius)[] { const ns: Set<(MutablePlayer | TerraNullius)> = new Set(); - for (const border of this.borderTiles()) { - for (const neighbor of border.neighbors()) { - if (neighbor.terrain().isLand() && neighbor.owner() != this) { - ns.add(neighbor.owner() as PlayerImpl | TerraNulliusImpl); + for (const border of this.borderTileRefs()) { + for (const neighbor of this.gs.map().neighbors(border)) { + if (this.gs.map().isLake(neighbor)) { + const owner = this.gs.map().ownerID(neighbor) + if (owner != this.smallID()) { + ns.add(this.gs.playerBySmallID(owner) as PlayerImpl | TerraNulliusImpl); + } } } } diff --git a/src/core/game/TileImpl.ts b/src/core/game/TileImpl.ts index 469d147a3..b3967fe89 100644 --- a/src/core/game/TileImpl.ts +++ b/src/core/game/TileImpl.ts @@ -82,11 +82,13 @@ export class TileImpl implements MutableTile { } hasOwner(): boolean { return this.owner().smallID() != 0 } + owner(): MutablePlayer | TerraNullius { - const ownerID = this.gs.map().playerId(this.ref_) + const ownerID = this.gs.map().ownerID(this.ref_) if (ownerID == 0) { return this.gs.terraNullius() } + return this.gs.playerBySmallID(ownerID) as MutablePlayer } isBorder(): boolean { return this.gs.map().isBorder(this.ref_); }