From b91d9d41482c7a029a221f3e9c000f255b2d4051 Mon Sep 17 00:00:00 2001 From: Evan Date: Sat, 18 Jan 2025 20:45:06 -0800 Subject: [PATCH] fix bugs from using tilerefs --- src/client/graphics/NameBoxCalculator.ts | 2 +- src/client/graphics/layers/EventsDisplay.ts | 24 ++++++++++---------- src/client/graphics/layers/TerritoryLayer.ts | 11 +++++---- src/core/GameView.ts | 13 +++++++---- src/core/execution/AttackExecution.ts | 1 - src/core/execution/BotExecution.ts | 2 +- src/core/execution/CityExecution.ts | 2 +- src/core/execution/FakeHumanExecution.ts | 2 +- src/core/execution/PlayerExecution.ts | 6 ++--- src/core/game/Game.ts | 1 - src/core/game/GameImpl.ts | 21 ++++------------- src/core/game/GameMap.ts | 14 ++---------- src/core/game/PlayerImpl.ts | 2 +- src/core/pathfinding/MiniAStar.ts | 2 +- 14 files changed, 41 insertions(+), 62 deletions(-) diff --git a/src/client/graphics/NameBoxCalculator.ts b/src/client/graphics/NameBoxCalculator.ts index 4e085486d..a79cb0d58 100644 --- a/src/client/graphics/NameBoxCalculator.ts +++ b/src/client/graphics/NameBoxCalculator.ts @@ -67,7 +67,7 @@ export function createGrid(game: Game, player: Player, boundingBox: { min: Point const cell = new Cell(x * scalingFactor, y * scalingFactor); if (game.isOnMap(cell)) { const tile = game.ref(cell.x, cell.y); - grid[x - scaledBoundingBox.min.x][y - scaledBoundingBox.min.y] = this.game.isLake(tile) || this.game.owner(tile) === player; // TODO: okay if lake + grid[x - scaledBoundingBox.min.x][y - scaledBoundingBox.min.y] = game.isLake(tile) || game.owner(tile) === player; // TODO: okay if lake } } } diff --git a/src/client/graphics/layers/EventsDisplay.ts b/src/client/graphics/layers/EventsDisplay.ts index db2f00234..4ba94c9e2 100644 --- a/src/client/graphics/layers/EventsDisplay.ts +++ b/src/client/graphics/layers/EventsDisplay.ts @@ -18,7 +18,7 @@ import { Layer } from "./Layer"; import { SendAllianceReplyIntentEvent } from "../../Transport"; import { unsafeHTML } from 'lit/directives/unsafe-html.js'; import { onlyImages, sanitize } from '../../../core/Util'; -import { GameView } from '../../../core/GameView'; +import { GameView, PlayerView } from '../../../core/GameView'; interface Event { description: string; @@ -222,8 +222,8 @@ export class EventsDisplay extends LitElement implements Layer { return; } - const requestor = this.game.playerBySmallID(update.requestorID) - const recipient = this.game.playerBySmallID(update.recipientID) + const requestor = this.game.playerBySmallID(update.requestorID) as PlayerView + const recipient = this.game.playerBySmallID(update.recipientID) as PlayerView this.addEvent({ description: `${requestor.name()} requests an alliance!`, @@ -258,7 +258,7 @@ export class EventsDisplay extends LitElement implements Layer { return; } - const recipient = this.game.playerBySmallID(update.request.recipientID) + const recipient = this.game.playerBySmallID(update.request.recipientID) as PlayerView this.addEvent({ description: `${recipient.name()} ${update.accepted ? "accepted" : "rejected"} your alliance request`, @@ -272,8 +272,8 @@ export class EventsDisplay extends LitElement implements Layer { const myPlayer = this.game.playerByClientID(this.clientID); if (!myPlayer) return; - const betrayed = this.game.playerBySmallID(update.betrayedID) - const traitor = this.game.playerBySmallID(update.traitorID) + const betrayed = this.game.playerBySmallID(update.betrayedID) as PlayerView + const traitor = this.game.playerBySmallID(update.traitorID) as PlayerView if (!betrayed.isTraitor() && traitor === myPlayer) { this.addEvent({ @@ -297,7 +297,7 @@ export class EventsDisplay extends LitElement implements Layer { if (!myPlayer) return; const otherID = update.player1ID === myPlayer.smallID() ? update.player2ID : update.player2ID === myPlayer.smallID() ? update.player1ID : null; - const other = this.game.playerBySmallID(otherID) + const other = this.game.playerBySmallID(otherID) as PlayerView if (!other || !myPlayer.isAlive() || !other.isAlive()) return; this.addEvent({ @@ -309,11 +309,11 @@ export class EventsDisplay extends LitElement implements Layer { } onTargetPlayerEvent(event: TargetPlayerUpdate) { - const other = this.game.playerBySmallID(event.playerID) - const myPlayer = this.game.playerByClientID(this.clientID); + const other = this.game.playerBySmallID(event.playerID) as PlayerView + const myPlayer = this.game.playerByClientID(this.clientID) as PlayerView if (!myPlayer || !myPlayer.isAlliedWith(other)) return; - const target = this.game.playerBySmallID(event.targetID) + const target = this.game.playerBySmallID(event.targetID) as PlayerView this.addEvent({ description: `${other.name()} requests you attack ${target.name()}`, @@ -328,7 +328,7 @@ export class EventsDisplay extends LitElement implements Layer { if (!myPlayer) return; const recipient = update.recipientID == AllPlayers ? AllPlayers : this.game.playerBySmallID(update.recipientID) - const sender = this.game.playerBySmallID(update.senderID) + const sender = this.game.playerBySmallID(update.senderID) as PlayerView if (recipient == myPlayer) { this.addEvent({ @@ -340,7 +340,7 @@ export class EventsDisplay extends LitElement implements Layer { }); } else if (sender === myPlayer && recipient !== AllPlayers) { this.addEvent({ - description: `Sent ${recipient.displayName()}: ${update.message}`, + description: `Sent ${(recipient as PlayerView).displayName()}: ${update.message}`, unsafeDescription: true, type: MessageType.INFO, highlight: true, diff --git a/src/client/graphics/layers/TerritoryLayer.ts b/src/client/graphics/layers/TerritoryLayer.ts index 7f4b27ff1..6e4223e70 100644 --- a/src/client/graphics/layers/TerritoryLayer.ts +++ b/src/client/graphics/layers/TerritoryLayer.ts @@ -161,7 +161,8 @@ export class TerritoryLayer implements Layer { if (!this.game.hasOwner(tile)) { if (this.game.hasFallout(tile)) { this.paintCell( - new Cell(this.game.x(tile), this.game.y(tile)), + this.game.x(tile), + this.game.y(tile), this.theme.falloutColor(), 150 ); @@ -173,21 +174,21 @@ export class TerritoryLayer implements Layer { const owner = this.game.owner(tile) as Player; if (this.game.isBorder(tile)) { this.paintCell( - new Cell(this.game.x(tile), this.game.y(tile)), + this.game.x(tile), this.game.y(tile), this.theme.borderColor(owner.info()), 255 ); } else { this.paintCell( - new Cell(this.game.x(tile), this.game.y(tile)), + this.game.x(tile), this.game.y(tile), this.theme.territoryColor(owner.info()), 150 ); } } - paintCell(cell: Cell, color: Colord, alpha: number) { - const index = (cell.y * this.game.width()) + cell.x; + paintCell(x: number, y: number, color: Colord, alpha: number) { + const index = (y * this.game.width()) + x; const offset = index * 4; this.imageData.data[offset] = color.rgba.r; this.imageData.data[offset + 1] = color.rgba.g; diff --git a/src/core/GameView.ts b/src/core/GameView.ts index cbe7d1671..1ece68000 100644 --- a/src/core/GameView.ts +++ b/src/core/GameView.ts @@ -49,7 +49,7 @@ export class UnitView implements Unit { return this.gameView.ref(this.data.pos.x, this.data.pos.y) } owner(): PlayerView { - return this.gameView.playerBySmallID(this.data.ownerID) + return this.gameView.playerBySmallID(this.data.ownerID) as PlayerView } isActive(): boolean { return this.data.isActive @@ -222,6 +222,7 @@ export class GameView implements GameMap { public update(gu: GameUpdateViewData) { this.lastUpdate = gu + this.updatedTiles = [] this.lastUpdate.packedTileUpdates.forEach(tu => { this.updatedTiles.push(this.updateTile(tu)) }) @@ -259,7 +260,10 @@ export class GameView implements GameMap { throw Error(`player id ${id} not found`) } - playerBySmallID(id: number): PlayerView { + playerBySmallID(id: number): PlayerView | TerraNullius { + if (id == 0) { + return new TerraNulliusImpl() + } if (!this.smallIDToID.has(id)) { throw new Error(`small id ${id} not found`) } @@ -284,7 +288,7 @@ export class GameView implements GameMap { return [] } - owner(tile: TileRef): PlayerView { + owner(tile: TileRef): PlayerView | TerraNullius { return this.playerBySmallID(this.ownerID(tile)) } @@ -312,7 +316,7 @@ export class GameView implements GameMap { height(): number { return this._map.height() } numLandTiles(): number { return this._map.numLandTiles() } isValidCoord(x: number, y: number): boolean { return this._map.isValidCoord(x, y) } - isLand(ref: TileRef): boolean { return this._map.isLake(ref) } + isLand(ref: TileRef): boolean { return this._map.isLand(ref) } isOceanShore(ref: TileRef): boolean { return this._map.isOceanShore(ref) } isOcean(ref: TileRef): boolean { return this._map.isOcean(ref) } isShoreline(ref: TileRef): boolean { return this._map.isShoreline(ref) } @@ -323,7 +327,6 @@ export class GameView implements GameMap { hasFallout(ref: TileRef): boolean { return this._map.hasFallout(ref) } setFallout(ref: TileRef, value: boolean): void { return this._map.setFallout(ref, value) } isBorder(ref: TileRef): boolean { return this._map.isBorder(ref) } - setBorder(ref: TileRef, value: boolean): void { return this._map.setBorder(ref, value) } neighbors(ref: TileRef): TileRef[] { return this._map.neighbors(ref) } isWater(ref: TileRef): boolean { return this._map.isWater(ref) } isLake(ref: TileRef): boolean { return this._map.isLake(ref) } diff --git a/src/core/execution/AttackExecution.ts b/src/core/execution/AttackExecution.ts index bbe8a8939..42ac4b8a7 100644 --- a/src/core/execution/AttackExecution.ts +++ b/src/core/execution/AttackExecution.ts @@ -175,7 +175,6 @@ export class AttackExecution implements Execution { } this.border.add(neighbor) let numOwnedByMe = this.mg.neighbors(neighbor) - .filter(t => this.mg.isLake(t)) .filter(t => this.mg.owner(t) == this._owner) .length let dist = 0 diff --git a/src/core/execution/BotExecution.ts b/src/core/execution/BotExecution.ts index 7e17523c8..8cadf9b22 100644 --- a/src/core/execution/BotExecution.ts +++ b/src/core/execution/BotExecution.ts @@ -56,7 +56,7 @@ export class BotExecution implements Execution { if (this.neighborsTerraNullius) { for (const b of this.bot.borderTiles()) { for (const n of this.mg.neighbors(b)) { - if (!this.mg.hasOwner(n) && this.mg.isLake(n)) { + if (!this.mg.hasOwner(n) && this.mg.isLand(n)) { this.sendAttack(this.mg.terraNullius()) return } diff --git a/src/core/execution/CityExecution.ts b/src/core/execution/CityExecution.ts index aa8526a3e..0eec45dd9 100644 --- a/src/core/execution/CityExecution.ts +++ b/src/core/execution/CityExecution.ts @@ -20,7 +20,7 @@ export class CityExecution implements Execution { if (this.city == null) { const spawnTile = this.player.canBuild(UnitType.City, this.tile) if (spawnTile == false) { - consolex.warn('cannot build Defense Post') + consolex.warn('cannot build city') this.active = false return } diff --git a/src/core/execution/FakeHumanExecution.ts b/src/core/execution/FakeHumanExecution.ts index cddf3b520..0decfd387 100644 --- a/src/core/execution/FakeHumanExecution.ts +++ b/src/core/execution/FakeHumanExecution.ts @@ -92,7 +92,7 @@ export class FakeHumanExecution implements Execution { const enemyborder = Array.from(this.player.borderTiles()) .flatMap(t => this.mg.neighbors(t)) - .filter(t => this.mg.isLake(t) && this.mg.ownerID(t) != this.player.smallID()) + .filter(t => this.mg.isLand(t) && this.mg.ownerID(t) != this.player.smallID()) if (enemyborder.length == 0) { if (this.random.chance(5)) { diff --git a/src/core/execution/PlayerExecution.ts b/src/core/execution/PlayerExecution.ts index fec9ee955..1cfc4d6af 100644 --- a/src/core/execution/PlayerExecution.ts +++ b/src/core/execution/PlayerExecution.ts @@ -150,9 +150,9 @@ export class PlayerExecution implements Execution { private removeCluster(cluster: Set) { const arr = Array.from(cluster) const mode = getMode( - arr. - flatMap(t => this.mg.neighbors(t)) - .filter(t => this.mg.ownerID(t) != this.player.smallID()) + arr + .flatMap(t => this.mg.neighbors(t)) + .filter(t => this.mg.hasOwner(t) && this.mg.ownerID(t) != this.player.smallID()) .map(t => this.mg.ownerID(t)) ) if (!this.mg.playerBySmallID(mode).isPlayer()) { diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 5ca1ac95c..c97524993 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -337,7 +337,6 @@ export interface MutableGame extends Game { units(...types: UnitType[]): MutableUnit[] addTileDefenseBonus(tile: TileRef, unit: Unit, amount: number): DefenseBonus removeTileDefenseBonus(bonus: DefenseBonus): void - addFallout(tile: TileRef): void setWinner(winner: Player): void } diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index 3eab4883b..08316047d 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -84,11 +84,11 @@ export class GameImpl implements MutableGame { return old } - addFallout(tile: TileRef) { - if (this.hasOwner(tile)) { + setFallout(tile: TileRef, value: boolean) { + if (value && this.hasOwner(tile)) { throw Error(`cannot set fallout, tile ${tile} has owner`) } - this._map.setFallout(tile, true) + this._map.setFallout(tile, value) this.addUpdate({ type: GameUpdateType.Tile, update: this.toTileUpdate(tile) @@ -315,12 +315,6 @@ export class GameImpl implements MutableGame { return ns } - private assertIsOnMap(cell: Cell) { - if (!this.isOnMap(cell)) { - throw new Error(`cell ${cell.toString()} is not on map`) - } - } - conquer(owner: PlayerImpl, tile: TileRef): void { if (!this.isLand(tile)) { throw Error(`cannot conquer water`) @@ -330,7 +324,6 @@ export class GameImpl implements MutableGame { previousOwner._lastTileChange = this._ticks previousOwner._tiles.delete(tile) previousOwner._borderTiles.delete(tile) - this._map.setBorder(tile, false) } this._map.setOwnerID(tile, owner.smallID()) owner._tiles.add(tile) @@ -355,7 +348,6 @@ export class GameImpl implements MutableGame { previousOwner._lastTileChange = this._ticks previousOwner._tiles.delete(tile) previousOwner._borderTiles.delete(tile) - this._map.setBorder(tile, false) this._map.setOwnerID(tile, 0) this.updateBorders(tile) @@ -373,15 +365,12 @@ export class GameImpl implements MutableGame { for (const t of tiles) { if (!this.hasOwner(t)) { - this._map.setBorder(t, false) continue } if (this.calcIsBorder(t)) { (this.owner(t) as PlayerImpl)._borderTiles.add(t); - this._map.setBorder(t, true) } else { (this.owner(t) as PlayerImpl)._borderTiles.delete(t); - this._map.setBorder(t, false) } // this.updates.push(t.toUpdate()) } @@ -494,7 +483,7 @@ export class GameImpl implements MutableGame { height(): number { return this._map.height() } numLandTiles(): number { return this._map.numLandTiles() } isValidCoord(x: number, y: number): boolean { return this._map.isValidCoord(x, y) } - isLand(ref: TileRef): boolean { return this._map.isLake(ref) } + isLand(ref: TileRef): boolean { return this._map.isLand(ref) } isOceanShore(ref: TileRef): boolean { return this._map.isOceanShore(ref) } isOcean(ref: TileRef): boolean { return this._map.isOcean(ref) } isShoreline(ref: TileRef): boolean { return this._map.isShoreline(ref) } @@ -503,9 +492,7 @@ export class GameImpl implements MutableGame { hasOwner(ref: TileRef): boolean { return this._map.hasOwner(ref) } setOwnerID(ref: TileRef, playerId: number): void { return this._map.setOwnerID(ref, playerId) } hasFallout(ref: TileRef): boolean { return this._map.hasFallout(ref) } - setFallout(ref: TileRef, value: boolean): void { return this._map.setFallout(ref, value) } isBorder(ref: TileRef): boolean { return this._map.isBorder(ref) } - setBorder(ref: TileRef, value: boolean): void { return this._map.setBorder(ref, value) } neighbors(ref: TileRef): TileRef[] { return this._map.neighbors(ref) } isWater(ref: TileRef): boolean { return this._map.isWater(ref) } isLake(ref: TileRef): boolean { return this._map.isLake(ref) } diff --git a/src/core/game/GameMap.ts b/src/core/game/GameMap.ts index 41cbf6b52..f2653bb3d 100644 --- a/src/core/game/GameMap.ts +++ b/src/core/game/GameMap.ts @@ -28,7 +28,6 @@ export interface GameMap { hasFallout(ref: TileRef): boolean setFallout(ref: TileRef, value: boolean): void isBorder(ref: TileRef): boolean - setBorder(ref: TileRef, value: boolean): void neighbors(ref: TileRef): TileRef[] isWater(ref: TileRef): boolean isLake(ref: TileRef): boolean @@ -62,8 +61,7 @@ export class GameMapImpl implements GameMap { // State bits (Uint16Array) private static readonly PLAYER_ID_OFFSET = 0; // Uses bits 0-11 (12 bits) private static readonly PLAYER_ID_MASK = 0xFFF; - private static readonly FALLOUT_BIT = 12; - private static readonly BORDER_BIT = 13; + private static readonly FALLOUT_BIT = 13; private static readonly DEFENSE_BONUS_BIT = 14; // Bit 15 still reserved @@ -155,15 +153,7 @@ export class GameMapImpl implements GameMap { } isBorder(ref: TileRef): boolean { - return Boolean(this.state[ref] & (1 << GameMapImpl.BORDER_BIT)); - } - - setBorder(ref: TileRef, value: boolean): void { - if (value) { - this.state[ref] |= 1 << GameMapImpl.BORDER_BIT; - } else { - this.state[ref] &= ~(1 << GameMapImpl.BORDER_BIT); - } + return this.neighbors(ref).some(tr => this.ownerID(tr) != this.ownerID(ref)) } hasDefenseBonus(ref: TileRef): boolean { diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index 8c38fd072..67a72b849 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -31,7 +31,7 @@ export class PlayerImpl implements MutablePlayer { public _borderTiles: Set = new Set(); public _units: UnitImpl[] = []; - public _tiles: Set + public _tiles: Set = new Set() private _name: string; private _displayName: string; diff --git a/src/core/pathfinding/MiniAStar.ts b/src/core/pathfinding/MiniAStar.ts index d7b03fddb..52319750f 100644 --- a/src/core/pathfinding/MiniAStar.ts +++ b/src/core/pathfinding/MiniAStar.ts @@ -40,7 +40,7 @@ export class MiniAStar implements AStar { } reconstructPath(): TileRef[] { - const upscaled = upscalePath(this.aStar.reconstructPath().map(tr => new Cell(this.gameMap.x(tr), this.gameMap.y(tr)))) + const upscaled = upscalePath(this.aStar.reconstructPath().map(tr => new Cell(this.miniMap.x(tr), this.miniMap.y(tr)))) upscaled.push(new Cell(this.gameMap.x(this.dst), this.gameMap.y(this.dst))) return upscaled.map(c => this.gameMap.ref(c.x, c.y)) }