diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index 36c575ae6..28a245cee 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -10,7 +10,7 @@ import { and, bfs, dist, generateID, manhattanDist } from "../core/Util"; import { SendAttackIntentEvent, SendSpawnIntentEvent, Transport } from "./Transport"; import { createCanvas } from "./Utils"; import { MessageType } from '../core/game/Game'; -import { DisplayMessageEvent } from '../core/game/Game'; +import { DisplayMessageUpdate } from '../core/game/Game'; import { WorkerClient } from "../core/worker/WorkerClient"; import { consolex, initRemoteSender } from "../core/Consolex"; import { getConfig, getServerConfig } from "../core/configuration/Config"; diff --git a/src/client/Transport.ts b/src/client/Transport.ts index ae71a2e4d..461f77d87 100644 --- a/src/client/Transport.ts +++ b/src/client/Transport.ts @@ -30,7 +30,9 @@ export class SendBreakAllianceIntentEvent implements GameEvent { export class SendAllianceReplyIntentEvent implements GameEvent { constructor( - public readonly allianceRequest: AllianceRequest, + // The original alliance requestor + public readonly requestor: Player, + public readonly recipient: Player, public readonly accepted: boolean ) { } } @@ -262,8 +264,8 @@ export class Transport { this.sendIntent({ type: "allianceRequestReply", clientID: this.lobbyConfig.clientID, - requestor: event.allianceRequest.requestor().id(), - recipient: event.allianceRequest.recipient().id(), + requestor: event.requestor.id(), + recipient: event.recipient.id(), accept: event.accepted, }) } diff --git a/src/client/graphics/layers/EventsDisplay.ts b/src/client/graphics/layers/EventsDisplay.ts index a7e6e592e..5cb140ef6 100644 --- a/src/client/graphics/layers/EventsDisplay.ts +++ b/src/client/graphics/layers/EventsDisplay.ts @@ -2,8 +2,13 @@ import { LitElement, html, css } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { EventBus } from "../../../core/EventBus"; import { + AllianceRequestReplyUpdate, + AllianceRequestUpdate, AllPlayers, + DisplayMessageUpdate, + EmojiUpdate, MessageType, + TargetPlayerUpdate, } from "../../../core/game/Game"; import { ClientID } from "../../../core/Schemas"; import { Layer } from "./Layer"; @@ -140,13 +145,6 @@ export class EventsDisplay extends LitElement implements Layer { } init() { - this.eventBus.on(AllianceRequestEvent, a => this.onAllianceRequestEvent(a)); - this.eventBus.on(AllianceRequestReplyEvent, a => this.onAllianceRequestReplyEvent(a)); - this.eventBus.on(DisplayMessageEvent, e => this.onDisplayMessageEvent(e)); - this.eventBus.on(BrokeAllianceEvent, e => this.onBrokeAllianceEvent(e)); - this.eventBus.on(AllianceExpiredEvent, e => this.onAllianceExpiredEvent(e)); - this.eventBus.on(TargetPlayerEvent, e => this.onTargetPlayerEvent(e)); - this.eventBus.on(EmojiMessageEvent, e => this.onEmojiMessageEvent(e)); } tick() { @@ -186,9 +184,9 @@ export class EventsDisplay extends LitElement implements Layer { renderLayer(): void { } - onDisplayMessageEvent(event: DisplayMessageEvent) { + onDisplayMessageEvent(event: DisplayMessageUpdate) { const myPlayer = this.game.playerByClientID(this.clientID); - if (event.playerID != null && (!myPlayer || myPlayer.id() !== event.playerID)) { + if (event.playerID != null && (!myPlayer || myPlayer.smallID() !== event.playerID)) { return; } @@ -196,122 +194,137 @@ export class EventsDisplay extends LitElement implements Layer { description: event.message, createdAt: this.game.ticks(), highlight: true, - type: event.type, + type: event.messageType, unsafeDescription: true, }); } - onAllianceRequestEvent(event: AllianceRequestEvent) { + onAllianceRequestEvent(update: AllianceRequestUpdate) { const myPlayer = this.game.playerByClientID(this.clientID); - if (!myPlayer || event.allianceRequest.recipient() !== myPlayer) { + if (!myPlayer || update.recipientID !== myPlayer.smallID()) { return; } + const requestor = this.game.playerBySmallID(update.requestorID) + const recipient = this.game.playerBySmallID(update.recipientID) + this.addEvent({ - description: `${event.allianceRequest.requestor().name()} requests an alliance!`, + description: `${requestor.name()} requests an alliance!`, buttons: [ { text: "Accept", className: "btn", - action: () => this.eventBus.emit(new SendAllianceReplyIntentEvent(event.allianceRequest, true)), + action: () => this.eventBus.emit( + new SendAllianceReplyIntentEvent(requestor, recipient, true) + ), }, { text: "Reject", className: "btn btn-info", - action: () => this.eventBus.emit(new SendAllianceReplyIntentEvent(event.allianceRequest, false)), + action: () => this.eventBus.emit( + new SendAllianceReplyIntentEvent(requestor, recipient, false) + ), } ], highlight: true, type: MessageType.INFO, createdAt: this.game.ticks(), - onDelete: () => this.eventBus.emit(new SendAllianceReplyIntentEvent(event.allianceRequest, false)) + onDelete: () => this.eventBus.emit( + new SendAllianceReplyIntentEvent(requestor, recipient, false) + ) }); } - onAllianceRequestReplyEvent(event: AllianceRequestReplyEvent) { - const myPlayer = this.game.playerByClientID(this.clientID); - if (!myPlayer || event.allianceRequest.requestor() !== myPlayer) { - return; - } + // onAllianceRequestReplyEvent(event: AllianceRequestReplyUpdate) { + // const myPlayer = this.game.playerByClientID(this.clientID); + // if (!myPlayer || event.allianceRequest.requestor() !== myPlayer) { + // return; + // } - this.addEvent({ - description: `${event.allianceRequest.recipient().name()} ${event.accepted ? "accepted" : "rejected"} your alliance request`, - type: event.accepted ? MessageType.SUCCESS : MessageType.ERROR, - highlight: true, - createdAt: this.game.ticks(), - }); - } + // this.addEvent({ + // description: `${event.allianceRequest.recipient().name()} ${event.accepted ? "accepted" : "rejected"} your alliance request`, + // type: event.accepted ? MessageType.SUCCESS : MessageType.ERROR, + // highlight: true, + // createdAt: this.game.ticks(), + // }); + // } - onBrokeAllianceEvent(event: BrokeAllianceEvent) { - const myPlayer = this.game.playerByClientID(this.clientID); - if (!myPlayer) return; + // onBrokeAllianceEvent(event: BrokeAllianceEvent) { + // const myPlayer = this.game.playerByClientID(this.clientID); + // if (!myPlayer) return; - if (!event.betrayed.isTraitor() && event.traitor === myPlayer) { - this.addEvent({ - description: `You broke your alliance with ${event.betrayed.name()}, making you a TRAITOR`, - type: MessageType.ERROR, - highlight: true, - createdAt: this.game.ticks(), - }); - } else if (event.betrayed === myPlayer) { - this.addEvent({ - description: `${event.traitor.name()}, broke their alliance with you`, - type: MessageType.ERROR, - highlight: true, - createdAt: this.game.ticks(), - }); - } - } + // if (!event.betrayed.isTraitor() && event.traitor === myPlayer) { + // this.addEvent({ + // description: `You broke your alliance with ${event.betrayed.name()}, making you a TRAITOR`, + // type: MessageType.ERROR, + // highlight: true, + // createdAt: this.game.ticks(), + // }); + // } else if (event.betrayed === myPlayer) { + // this.addEvent({ + // description: `${event.traitor.name()}, broke their alliance with you`, + // type: MessageType.ERROR, + // highlight: true, + // createdAt: this.game.ticks(), + // }); + // } + // } - onAllianceExpiredEvent(event: AllianceExpiredEvent) { - const myPlayer = this.game.playerByClientID(this.clientID); - if (!myPlayer) return; + // onAllianceExpiredEvent(event: AllianceExpiredEvent) { + // const myPlayer = this.game.playerByClientID(this.clientID); + // if (!myPlayer) return; - const other = event.player1 === myPlayer ? event.player2 : event.player2 === myPlayer ? event.player1 : null; - if (!other || !myPlayer.isAlive() || !other.isAlive()) return; + // const other = event.player1 === myPlayer ? event.player2 : event.player2 === myPlayer ? event.player1 : null; + // if (!other || !myPlayer.isAlive() || !other.isAlive()) return; - this.addEvent({ - description: `Your alliance with ${other.name()} expired`, - type: MessageType.WARN, - highlight: true, - createdAt: this.game.ticks(), - }); - } + // this.addEvent({ + // description: `Your alliance with ${other.name()} expired`, + // type: MessageType.WARN, + // highlight: true, + // createdAt: this.game.ticks(), + // }); + // } - onTargetPlayerEvent(event: TargetPlayerEvent) { - const myPlayer = this.game.playerByClientID(this.clientID); - if (!myPlayer || !myPlayer.isAlliedWith(event.player)) return; + // onTargetPlayerEvent(event: TargetPlayerUpdate) { + // const other = this.game.playerBySmallID(event.playerID) + // const myPlayer = this.game.playerByClientID(this.clientID); + // if (!myPlayer || !myPlayer.isAlliedWith(other)) return; - this.addEvent({ - description: `${event.player.name()} requests you attack ${event.target.name()}`, - type: MessageType.INFO, - highlight: true, - createdAt: this.game.ticks(), - }); - } + // const target = this.game.playerBySmallID(event.targetID) - onEmojiMessageEvent(event: EmojiMessageEvent) { - const myPlayer = this.game.playerByClientID(this.clientID); - if (!myPlayer) return; + // this.addEvent({ + // description: `${other.name()} requests you attack ${target.name()}`, + // type: MessageType.INFO, + // highlight: true, + // createdAt: this.game.ticks(), + // }); + // } - if (event.message.recipient === myPlayer) { - this.addEvent({ - description: `${event.message.sender.displayName()}:${event.message.emoji}`, - unsafeDescription: true, - type: MessageType.INFO, - highlight: true, - createdAt: this.game.ticks(), - }); - } else if (event.message.sender === myPlayer && event.message.recipient !== AllPlayers) { - this.addEvent({ - description: `Sent ${event.message.recipient.displayName()}: ${event.message.emoji}`, - unsafeDescription: true, - type: MessageType.INFO, - highlight: true, - createdAt: this.game.ticks(), - }); - } - } + // onEmojiMessageEvent(update: EmojiUpdate) { + // const myPlayer = this.game.playerByClientID(this.clientID); + // if (!myPlayer) return; + + // const recipient = update.recipientID == AllPlayers ? AllPlayers : this.game.playerBySmallID(update.recipientID) + // const sender = this.game.playerBySmallID(update.senderID) + + // if (recipient == myPlayer) { + // this.addEvent({ + // description: `${sender.displayName()}:${update.message}`, + // unsafeDescription: true, + // type: MessageType.INFO, + // highlight: true, + // createdAt: this.game.ticks(), + // }); + // } else if (sender === myPlayer && recipient !== AllPlayers) { + // this.addEvent({ + // description: `Sent ${recipient.displayName()}: ${update.message}`, + // unsafeDescription: true, + // type: MessageType.INFO, + // highlight: true, + // createdAt: this.game.ticks(), + // }); + // } + // } render() { if (this.events.length === 0) { diff --git a/src/client/graphics/layers/StructureLayer.ts b/src/client/graphics/layers/StructureLayer.ts index 34f4fd319..9a594d9ea 100644 --- a/src/client/graphics/layers/StructureLayer.ts +++ b/src/client/graphics/layers/StructureLayer.ts @@ -1,6 +1,6 @@ import { colord, Colord } from "colord"; import { Theme } from "../../../core/configuration/Config"; -import { Unit, UnitEvent, Cell, Game, Tile, UnitType } from "../../../core/game/Game"; +import { Unit, Cell, Game, Tile, UnitType } from "../../../core/game/Game"; import { bfs, dist, euclDist } from "../../../core/Util"; import { Layer } from "./Layer"; import { EventBus } from "../../../core/EventBus"; @@ -70,7 +70,7 @@ export class StructureLayer implements Layer { } tick() { - this.game.units().forEach(u => this.handleUnitRendering(new UnitEvent(u, u.tile()))) + this.game.units().forEach(u => this.handleUnitRendering(u)) } init() { @@ -83,7 +83,7 @@ export class StructureLayer implements Layer { this.context = this.canvas.getContext("2d", { alpha: true }); this.canvas.width = this.game.width(); this.canvas.height = this.game.height(); - this.game.units().forEach(u => this.handleUnitRendering(new UnitEvent(u, u.tile()))) + this.game.units().forEach(u => this.handleUnitRendering(u)) } renderLayer(context: CanvasRenderingContext2D) { @@ -100,15 +100,15 @@ export class StructureLayer implements Layer { return unitType in this.unitConfigs; } - private handleUnitRendering(event: UnitEvent) { - const unitType = event.unit.type(); + private handleUnitRendering(unit: Unit) { + const unitType = unit.type(); if (!this.isUnitTypeSupported(unitType)) return; - if (event.unit.isActive() && this.seenUnits.has(event.unit)) { + if (unit.isActive() && this.seenUnits.has(unit)) { // Already rendered, so don't do anything. return } - if (!event.unit.isActive() && !this.seenUnits.has(event.unit)) { + if (!unit.isActive() && !this.seenUnits.has(unit)) { // Has been deleted and render is cleared so don't do anything. return } @@ -119,14 +119,14 @@ export class StructureLayer implements Layer { if (!config || !unitImage) return; // Clear previous rendering - bfs(event.unit.tile(), euclDist(event.unit.tile(), config.borderRadius)) + bfs(unit.tile(), euclDist(unit.tile(), config.borderRadius)) .forEach(t => this.clearCell(t.cell())); - if (!event.unit.isActive()) { - this.seenUnits.delete(event.unit) + if (!unit.isActive()) { + this.seenUnits.delete(unit) return; } - this.seenUnits.add(event.unit) + this.seenUnits.add(unit) // Create temporary canvas for icon processing const tempCanvas = document.createElement('canvas'); @@ -138,19 +138,19 @@ export class StructureLayer implements Layer { tempContext.drawImage(unitImage, 0, 0); const iconData = tempContext.getImageData(0, 0, tempCanvas.width, tempCanvas.height); - const cell = event.unit.tile().cell(); + const cell = unit.tile().cell(); const startX = cell.x - Math.floor(tempCanvas.width / 2); const startY = cell.y - Math.floor(tempCanvas.height / 2); // Draw border and territory - bfs(event.unit.tile(), euclDist(event.unit.tile(), config.borderRadius)) - .forEach(t => this.paintCell(t.cell(), this.theme.borderColor(event.unit.owner().info()), 255)); + bfs(unit.tile(), euclDist(unit.tile(), config.borderRadius)) + .forEach(t => this.paintCell(t.cell(), this.theme.borderColor(unit.owner().info()), 255)); - bfs(event.unit.tile(), euclDist(event.unit.tile(), config.territoryRadius)) - .forEach(t => this.paintCell(t.cell(), this.theme.territoryColor(event.unit.owner().info()), 130)); + bfs(unit.tile(), euclDist(unit.tile(), config.territoryRadius)) + .forEach(t => this.paintCell(t.cell(), this.theme.territoryColor(unit.owner().info()), 130)); // Draw the icon - this.renderIcon(iconData, startX, startY, tempCanvas.width, tempCanvas.height, event.unit); + this.renderIcon(iconData, startX, startY, tempCanvas.width, tempCanvas.height, unit); } private renderIcon( diff --git a/src/client/graphics/layers/TerritoryLayer.ts b/src/client/graphics/layers/TerritoryLayer.ts index 9431fe654..2ce82baf7 100644 --- a/src/client/graphics/layers/TerritoryLayer.ts +++ b/src/client/graphics/layers/TerritoryLayer.ts @@ -1,5 +1,5 @@ import { PriorityQueue } from "@datastructures-js/priority-queue"; -import { Cell, Game, Player, PlayerType, Tile, TileEvent, UnitEvent, UnitType } from "../../../core/game/Game"; +import { Cell, Game, Player, PlayerType, Tile, Unit, UnitType, UnitUpdate } from "../../../core/game/Game"; import { PseudoRandom } from "../../../core/PseudoRandom"; import { colord, Colord } from "colord"; import { bfs, dist, euclDist, euclideanDist } from "../../../core/Util"; @@ -197,19 +197,6 @@ export class TerritoryLayer implements Layer { this.imageData.data[offset + 3] = 0; // Set alpha to 0 (fully transparent) } - unitEvent(event: UnitEvent) { - if (event.unit.type() == UnitType.DefensePost) { - bfs( - event.unit.tile(), - dist(event.unit.tile(), this.game.config().defensePostRange()) - ).forEach(t => { - if (t.isBorder()) { - this.enqueueTile(t) - } - }) - } - } - enqueueTile(tile: Tile) { this.tileToRenderQueue.push({ tile: tile, lastUpdate: this.game.ticks() + this.random.nextFloat(0, .5) }) } diff --git a/src/client/graphics/layers/UnitLayer.ts b/src/client/graphics/layers/UnitLayer.ts index 034616d1b..efc027d19 100644 --- a/src/client/graphics/layers/UnitLayer.ts +++ b/src/client/graphics/layers/UnitLayer.ts @@ -14,6 +14,10 @@ enum Relationship { Enemy } +class UnitEvent { + constructor(public unit: Unit, public oldTile: Tile) { } +} + export class UnitLayer implements Layer { private canvas: HTMLCanvasElement; private context: CanvasRenderingContext2D; @@ -176,22 +180,22 @@ export class UnitLayer implements Layer { } - private handleTradeShipEvent(unit: Unit) { - const rel = this.relationship(unit) - bfs(unit.oldTile, euclDist(unit.oldTile, 3)).forEach(t => { + private handleTradeShipEvent(event: UnitEvent) { + const rel = this.relationship(event.unit) + bfs(event.oldTile, euclDist(event.oldTile, 3)).forEach(t => { this.clearCell(t.cell()); }); - if (unit.isActive()) { - bfs(unit.tile(), dist(unit.tile(), 2)) - .forEach(t => this.paintCell(t.cell(), rel, this.theme.territoryColor(unit.owner().info()), 255)); + if (event.unit.isActive()) { + bfs(event.unit.tile(), dist(event.unit.tile(), 2)) + .forEach(t => this.paintCell(t.cell(), rel, this.theme.territoryColor(event.unit.owner().info()), 255)); } - if (unit.isActive()) { - bfs(unit.tile(), dist(unit.tile(), 1)) - .forEach(t => this.paintCell(t.cell(), rel, this.theme.borderColor(unit.owner().info()), 255)); + if (event.unit.isActive()) { + bfs(event.unit.tile(), dist(event.unit.tile(), 1)) + .forEach(t => this.paintCell(t.cell(), rel, this.theme.borderColor(event.unit.owner().info()), 255)); } } - private handleBoatEvent(event: Unit) { + private handleBoatEvent(event: UnitEvent) { const rel = this.relationship(event.unit) if (!this.boatToTrail.has(event.unit)) { this.boatToTrail.set(event.unit, new Set()); diff --git a/src/core/GameRunner.ts b/src/core/GameRunner.ts index ac406af7a..dbe1e4bc9 100644 --- a/src/core/GameRunner.ts +++ b/src/core/GameRunner.ts @@ -1,9 +1,10 @@ +import { utcDay } from "d3"; import { placeName } from "../client/graphics/NameBoxCalculator"; import { getConfig } from "./configuration/Config"; import { EventBus } from "./EventBus"; import { Executor } from "./execution/ExecutionManager"; import { WinCheckExecution } from "./execution/WinCheckExecution"; -import { Cell, DisplayMessageEvent, Game, MessageType, MutableGame, MutableTile, Player, PlayerID, Tile, UnitType } from "./game/Game"; +import { Cell, DisplayMessageUpdate, Game, GameUpdateType, MessageType, MutableGame, MutableTile, Player, PlayerID, Tile, UnitType } from "./game/Game"; import { createGame } from "./game/GameImpl"; import { loadTerrainMap } from "./game/TerrainMapLoader"; import { GameUpdateViewData, NameViewData, packTileData, PlayerActions, PlayerViewData } from "./GameView"; @@ -20,7 +21,6 @@ export async function createGameRunner(gameID: string, gameConfig: GameConfig, c } export class GameRunner { - private updatedTiles: Set = new Set() private tickInterval = null private turns: Turn[] = [] private currTurn = 0 @@ -56,12 +56,11 @@ export class GameRunner { return } this.isExecuting = true - this.updatedTiles.clear() this.game.addExecution(...this.execManager.createExecs(this.turns[this.currTurn])) this.currTurn++ - this.game.executeNextTick() + const updates = this.game.executeNextTick() if (this.game.inSpawnPhase() || this.game.ticks() % 10 == 0) { this.game.players() @@ -78,7 +77,7 @@ export class GameRunner { this.callBack({ tick: this.game.ticks(), units: this.game.units().map(u => u.toViewData()), - packedTileUpdates: Array.from(this.updatedTiles).map(t => packTileData(t.toViewData())), + packedTileUpdates: updates.filter(u => u.type == GameUpdateType.Tile).map(u => packTileData(u)), players: playerViewData }) this.isExecuting = false diff --git a/src/core/GameView.ts b/src/core/GameView.ts index abdb8b3a8..a6d14561f 100644 --- a/src/core/GameView.ts +++ b/src/core/GameView.ts @@ -1,4 +1,4 @@ -import { MessageType, Player, Tile, Unit } from './game/Game'; +import { GameUpdateType, MessageType, Player, Tile, TileUpdate, Unit } from './game/Game'; import { Config } from "./configuration/Config"; import { Alliance, AllianceRequest, AllPlayers, Cell, DefenseBonus, EmojiMessage, Execution, ExecutionView, Game, Gold, MutableTile, Nation, PlayerID, PlayerInfo, PlayerType, Relation, TerrainMap, TerrainTile, TerrainType, TerraNullius, Tick, UnitInfo, UnitType } from "./game/Game"; import { ClientID } from "./Schemas"; @@ -24,7 +24,7 @@ export interface TileViewData extends ViewData { export class TileView { - constructor(private game: GameView, public data: TileViewData, private _terrain: TerrainTile) { } + constructor(private game: GameView, public data: TileUpdate, private _terrain: TerrainTile) { } type(): TerrainType { return this._terrain.type() @@ -33,10 +33,10 @@ export class TileView { if (!this.hasOwner()) { return new TerraNulliusImpl() } - return this.game.playerBySmallID(this.data?.smallID) + return this.game.playerBySmallID(this.data?.ownerID) } hasOwner(): boolean { - return this.data?.smallID !== undefined && this.data.smallID !== 0; + return this.data?.ownerID !== undefined && this.data.ownerID !== 0; } isBorder(): boolean { return this.data?.isBorder @@ -276,7 +276,7 @@ export interface GameUpdateViewData extends ViewData { tick: number units: UnitViewData[] players: Record - tileUpdates?: TileViewData[] + tileUpdates?: TileUpdate[] packedTileUpdates: Uint16Array[] } @@ -310,7 +310,7 @@ export class GameView { this.lastUpdate = gu this.lastUpdate.tileUpdates = this.lastUpdate.packedTileUpdates.map(tu => unpackTileData(tu)) this.lastUpdate.tileUpdates.forEach(tu => { - this.tiles[tu.x][tu.y].data = tu + this.tiles[tu.pos.x][tu.pos.y].data = tu }) Object.entries(gu.players).forEach(([key, value]) => { this.smallIDToID.set(value.smallID, key); @@ -330,7 +330,7 @@ export class GameView { } recentlyUpdatedTiles(): TileView[] { - return this.lastUpdate.tileUpdates.filter(d => true).map(tu => new TileView(this, tu, this._terrainMap.terrain(new Cell(tu.x, tu.y)))) + return this.lastUpdate.tileUpdates.map(tu => new TileView(this, tu, this._terrainMap.terrain(new Cell(tu.pos.x, tu.pos.y)))) } player(id: PlayerID): PlayerView { @@ -404,11 +404,11 @@ export class GameView { } } -export function packTileData(tile: TileViewData): Uint16Array { +export function packTileData(tile: TileUpdate): Uint16Array { const packed = new Uint16Array(4); - packed[0] = tile.x; - packed[1] = tile.y; - packed[2] = tile.smallID; + packed[0] = tile.pos.x; + packed[1] = tile.pos.y; + packed[2] = tile.ownerID; // Pack booleans into bits packed[3] = (tile.hasFallout ? 1 : 0) | @@ -418,11 +418,14 @@ export function packTileData(tile: TileViewData): Uint16Array { return packed; } -export function unpackTileData(packed: Uint16Array): TileViewData { +export function unpackTileData(packed: Uint16Array): TileUpdate { return { - x: packed[0], - y: packed[1], - smallID: packed[2], + type: GameUpdateType.Tile, + pos: { + x: packed[0], + y: packed[1], + }, + ownerID: packed[2], hasFallout: !!(packed[3] & 1), hasDefenseBonus: !!(packed[3] & 2), isBorder: !!(packed[3] & 4), diff --git a/src/core/execution/TransportShipExecution.ts b/src/core/execution/TransportShipExecution.ts index 0a0183406..24970212b 100644 --- a/src/core/execution/TransportShipExecution.ts +++ b/src/core/execution/TransportShipExecution.ts @@ -1,8 +1,8 @@ -import { Unit, Cell, Execution, MutableUnit, MutableGame, MutablePlayer, Player, PlayerID, TerraNullius, Tile, UnitType, TerrainType } from "../game/Game"; +import { Unit, Cell, Execution, MutableUnit, MutableGame, MutablePlayer, Player, PlayerID, TerraNullius, Tile, UnitType, TerrainType } from "../game/Game"; import { and, bfs, manhattanDistWrapped, sourceDstOceanShore, targetTransportTile } from "../Util"; import { AttackExecution } from "./AttackExecution"; import { MessageType } from '../game/Game'; -import { DisplayMessageEvent } from '../game/Game'; +import { DisplayMessageUpdate } from '../game/Game'; import { PathFinder } from "../pathfinding/PathFinding"; import { PathFindResultType } from "../pathfinding/AStar"; import { SerialAStar } from "../pathfinding/SerialAStar"; diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 2ce9c56ca..8e25b2698 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -344,7 +344,7 @@ export interface Game { forEachTile(fn: (tile: Tile) => void): void executions(): ExecutionView[] terraNullius(): TerraNullius - executeNextTick(): void + executeNextTick(): GameUpdate[] ticks(): Tick inSpawnPhase(): boolean addExecution(...exec: Execution[]): void @@ -391,17 +391,16 @@ export type GameUpdate = TileUpdate | AllianceRequestReplyUpdate | BrokeAllianceUpdate | AllianceExpiredUpdate - | DisplayMessageEvent + | DisplayMessageUpdate | TargetPlayerUpdate | EmojiUpdate | WinUpdate export interface TileUpdate { type: GameUpdateType.Tile - owner: number + ownerID: number pos: MapPos isBorder: boolean - borderOnlyChange: boolean hasFallout: boolean hasDefenseBonus: boolean } @@ -457,7 +456,7 @@ export interface EmojiUpdate { createdAt: Tick } -export interface DisplayMessageEvent { +export interface DisplayMessageUpdate { type: GameUpdateType.DisplayEvent message: string messageType: MessageType diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index 890aab31d..127a07f53 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -79,7 +79,7 @@ export class GameImpl implements MutableGame { throw Error(`cannot set fallout, tile ${tile} has owner`) } ti._hasFallout = true - this.updates.push(ti.toUpdate(false)) + this.updates.push(ti.toUpdate()) } addTileDefenseBonus(tile: Tile, unit: Unit, amount: number): DefenseBonus { @@ -164,7 +164,8 @@ export class GameImpl implements MutableGame { return this._ticks } - executeNextTick() { + executeNextTick(): GameUpdate[] { + this.updates = [] this.execs.forEach(e => { if (e.isActive() && (!this.inSpawnPhase() || e.activeDuringSpawnPhase())) { e.tick(this._ticks) @@ -193,6 +194,7 @@ export class GameImpl implements MutableGame { }) consolex.log(`tick ${this._ticks}: hash ${hash}`) } + return this.updates } terraNullius(): TerraNullius { @@ -386,7 +388,6 @@ export class GameImpl implements MutableGame { tile.neighbors().forEach(t => tiles.push(t as TileImpl)) for (const t of tiles) { - this.updates.push(t.toUpdate(true)) if (!t.hasOwner()) { t._isBorder = false continue @@ -398,6 +399,7 @@ export class GameImpl implements MutableGame { (t.owner() as PlayerImpl)._borderTiles.delete(t); t._isBorder = false } + this.updates.push(t.toUpdate()) } } diff --git a/src/core/game/TileImpl.ts b/src/core/game/TileImpl.ts index 72bbe5383..fa0959026 100644 --- a/src/core/game/TileImpl.ts +++ b/src/core/game/TileImpl.ts @@ -27,18 +27,17 @@ export class TileImpl implements MutableTile { throw new Error("Method not implemented."); } - toUpdate(borderOnlyChange: boolean = false): TileUpdate { + toUpdate(): TileUpdate { return { type: GameUpdateType.Tile, pos: { x: this._cell.x, y: this._cell.y }, - owner: this._owner.isPlayer() ? this._owner.smallID() : 0, + ownerID: this._owner.isPlayer() ? this._owner.smallID() : 0, hasFallout: this._hasFallout, hasDefenseBonus: this.hasDefenseBonus(), isBorder: this.isBorder(), - borderOnlyChange: borderOnlyChange } }