From 3e8517363f79b76feb0057faf3ccc14f5e340f83 Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 3 Jan 2025 10:18:25 -0800 Subject: [PATCH] use GameView in renderers --- src/client/ClientGameRunner.ts | 87 ++++++------------- src/client/graphics/GameRenderer.ts | 9 +- src/client/graphics/NameBoxCalculator.ts | 5 +- src/client/graphics/TransformHandler.ts | 3 +- src/client/graphics/layers/ControlPanel.ts | 6 +- src/client/graphics/layers/EventsDisplay.ts | 3 +- src/client/graphics/layers/Layer.ts | 2 +- src/client/graphics/layers/Leaderboard.ts | 6 +- src/client/graphics/layers/NameLayer.ts | 5 +- .../graphics/layers/PlayerInfoOverlay.ts | 6 +- src/client/graphics/layers/StructureLayer.ts | 5 +- src/client/graphics/layers/TerrainLayer.ts | 5 +- src/client/graphics/layers/TerritoryLayer.ts | 5 +- src/client/graphics/layers/UILayer.ts | 5 +- src/client/graphics/layers/UnitLayer.ts | 5 +- .../graphics/layers/radial/BuildMenu.ts | 3 +- .../graphics/layers/radial/RadialMenu.ts | 5 +- src/core/GameView.ts | 28 +++--- src/core/Util.ts | 7 +- src/core/execution/TransportShipExecution.ts | 2 +- src/core/worker/WorkerClient.ts | 21 +++-- 21 files changed, 101 insertions(+), 122 deletions(-) diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index d14b112bf..6074970db 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -7,7 +7,6 @@ import { InputHandler, MouseUpEvent, ZoomEvent, DragEvent, MouseDownEvent } from import { ClientID, ClientIntentMessageSchema, ClientJoinMessageSchema, ClientMessageSchema, GameConfig, GameID, Intent, ServerMessage, ServerMessageSchema, ServerSyncMessage, Turn } from "../core/Schemas"; import { loadTerrainFromFile, loadTerrainMap, TerrainMapImpl } from "../core/game/TerrainMapLoader"; import { and, bfs, dist, generateID, manhattanDist } from "../core/Util"; -import { WinCheckExecution } from "../core/execution/WinCheckExecution"; import { SendAttackIntentEvent, SendSpawnIntentEvent, Transport } from "./Transport"; import { createCanvas } from "./Utils"; import { MessageType } from '../core/game/Game'; @@ -15,7 +14,7 @@ import { DisplayMessageEvent } from '../core/game/Game'; import { WorkerClient } from "../core/worker/WorkerClient"; import { consolex, initRemoteSender } from "../core/Consolex"; import { getConfig, getServerConfig } from "../core/configuration/Config"; -import { GameUpdateViewData } from "../core/GameView"; +import { GameUpdateViewData, GameView } from "../core/GameView"; export interface LobbyConfig { playerName: () => string @@ -75,17 +74,15 @@ export async function createClientGame(lobbyConfig: LobbyConfig, gameConfig: Gam const config = getConfig(gameConfig) const terrainMap = await loadTerrainMap(gameConfig.gameMap); + const gameView = new GameView(terrainMap.map) - let game = createGame(terrainMap.map, terrainMap.miniMap, eventBus, config) const worker = new WorkerClient(lobbyConfig.gameID, gameConfig) - await worker.initialize((gu: GameUpdateViewData) => { - console.log('got update!') - }) + await worker.initialize() consolex.log('going to init path finder') consolex.log('inited path finder') const canvas = createCanvas() - let gameRenderer = createRenderer(canvas, game, eventBus, lobbyConfig.clientID) + let gameRenderer = createRenderer(canvas, gameView, eventBus, lobbyConfig.clientID) consolex.log(`creating private game got difficulty: ${gameConfig.difficulty}`) @@ -93,36 +90,29 @@ export async function createClientGame(lobbyConfig: LobbyConfig, gameConfig: Gam return new ClientGameRunner( lobbyConfig.clientID, eventBus, - game, gameRenderer, new InputHandler(canvas, eventBus), - new Executor(game, lobbyConfig.gameID), transport, worker, + gameView ) } export class ClientGameRunner { private myPlayer: Player - private turns: Turn[] = [] private isActive = false - private currTurn = 0 - - private intervalID: NodeJS.Timeout - - private isProcessingTurn = false + private turnsSeen = 0 private hasJoined = false constructor( private clientID: ClientID, private eventBus: EventBus, - private gs: Game, private renderer: GameRenderer, private input: InputHandler, - private executor: Executor, private transport: Transport, - private worker: WorkerClient + private worker: WorkerClient, + private gameView: GameView ) { } public start() { @@ -133,27 +123,25 @@ export class ClientGameRunner { this.renderer.initialize() this.input.initialize() - this.gs.addExecution(...this.executor.spawnBots(this.gs.config().numBots())) - if (this.gs.config().spawnNPCs()) { - this.gs.addExecution(...this.executor.fakeHumanExecutions()) - } - this.gs.addExecution(new WinCheckExecution(this.eventBus)) - - this.intervalID = setInterval(() => this.tick(), 10); + this.worker.start((gu: GameUpdateViewData) => { + this.gameView.update(gu) + this.renderer.tick() + }) const onconnect = () => { consolex.log('Connected to game server!'); - this.transport.joinGame(this.turns.length) + this.transport.joinGame(this.turnsSeen) }; const onmessage = (message: ServerMessage) => { if (message.type == "start") { this.hasJoined = true consolex.log("starting game!") for (const turn of message.turns) { - if (turn.turnNumber < this.turns.length) { + if (turn.turnNumber < this.turnsSeen) { continue } - this.turns.push(turn) + this.worker.sendTurn(turn) + this.turnsSeen++ } } if (message.type == "turn") { @@ -161,48 +149,23 @@ export class ClientGameRunner { this.transport.joinGame(0) return } - if (this.turns.length != message.turn.turnNumber) { - consolex.error(`got wrong turn have turns ${this.turns.length}, received turn ${message.turn.turnNumber}`) + if (this.turnsSeen != message.turn.turnNumber) { + consolex.error(`got wrong turn have turns ${this.turnsSeen}, received turn ${message.turn.turnNumber}`) } else { - this.turns.push(message.turn) + this.worker.sendTurn(message.turn) + this.turnsSeen++ } } }; this.transport.connect(onconnect, onmessage) - } public stop() { - clearInterval(this.intervalID) + this.worker.cleanup() this.isActive = false this.transport.leaveGame() } - public tick() { - if (this.currTurn >= this.turns.length || this.isProcessingTurn) { - return - } - this.isProcessingTurn = true - this.worker.sendTurn(this.turns[this.currTurn]) - this.gs.addExecution(...this.executor.createExecs(this.turns[this.currTurn])) - try { - const start = performance.now() - this.gs.executeNextTick() - const duration = performance.now() - start - if (duration > 200) { - console.warn(`tick ${this.gs.ticks() - 1} took ${duration}ms to execute`) - } - } catch (error) { - showErrorModal(error, this.clientID) - this.stop() - const errorText = `Error: ${error.message}\nStack: ${error.stack}`; - consolex.error(errorText) - } - this.renderer.tick() - this.currTurn++ - this.isProcessingTurn = false - } - private playerEvent(event: PlayerEvent) { if (event.player.clientID() == this.clientID) { consolex.log('setting name') @@ -215,16 +178,16 @@ export class ClientGameRunner { return } const cell = this.renderer.transformHandler.screenToWorldCoordinates(event.x, event.y) - if (!this.gs.isOnMap(cell)) { + if (!this.gameView.isOnMap(cell)) { return } consolex.log(`clicked cell ${cell}`) - const tile = this.gs.tile(cell) - if (tile.terrain().isLand() && !tile.hasOwner() && this.gs.inSpawnPhase()) { + const tile = this.gameView.tile(cell) + if (tile.terrain().isLand() && !tile.hasOwner() && this.gameView.inSpawnPhase()) { this.eventBus.emit(new SendSpawnIntentEvent(cell)) return } - if (this.gs.inSpawnPhase()) { + if (this.gameView.inSpawnPhase()) { return } if (this.myPlayer == null) { diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index 2865f9d17..7a1cf099a 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -19,9 +19,10 @@ import { StructureLayer } from "./layers/StructureLayer"; import { PlayerInfoOverlay } from "./layers/PlayerInfoOverlay"; import { consolex } from "../../core/Consolex"; import { RefreshGraphicsEvent as RedrawGraphicsEvent } from "../InputHandler"; +import { GameView } from "../../core/GameView"; -export function createRenderer(canvas: HTMLCanvasElement, game: Game, eventBus: EventBus, clientID: ClientID): GameRenderer { +export function createRenderer(canvas: HTMLCanvasElement, game: GameView, eventBus: EventBus, clientID: ClientID): GameRenderer { const transformHandler = new TransformHandler(game, eventBus, canvas) const uiState = { attackRatio: 20 } @@ -44,6 +45,7 @@ export function createRenderer(canvas: HTMLCanvasElement, game: Game, eventBus: } leaderboard.clientID = clientID leaderboard.eventBus = eventBus + leaderboard.game = game const controlPanel = document.querySelector('control-panel') as ControlPanel; @@ -53,6 +55,7 @@ export function createRenderer(canvas: HTMLCanvasElement, game: Game, eventBus: controlPanel.clientID = clientID controlPanel.eventBus = eventBus controlPanel.uiState = uiState + controlPanel.game = game const eventsDisplay = document.querySelector('events-display') as EventsDisplay; if (!(eventsDisplay instanceof EventsDisplay)) { @@ -94,7 +97,7 @@ export class GameRenderer { private context: CanvasRenderingContext2D - constructor(private game: Game, private eventBus: EventBus, private canvas: HTMLCanvasElement, public transformHandler: TransformHandler, public uiState: UIState, private layers: Layer[]) { + constructor(private game: GameView, private eventBus: EventBus, private canvas: HTMLCanvasElement, public transformHandler: TransformHandler, public uiState: UIState, private layers: Layer[]) { this.context = canvas.getContext("2d") } @@ -107,7 +110,7 @@ export class GameRenderer { }) }) - this.layers.forEach(l => l.init(this.game)) + this.layers.forEach(l => l.init()) document.body.appendChild(this.canvas); window.addEventListener('resize', () => this.resizeCanvas()); diff --git a/src/client/graphics/NameBoxCalculator.ts b/src/client/graphics/NameBoxCalculator.ts index 5aaed12e3..0fd1702e9 100644 --- a/src/client/graphics/NameBoxCalculator.ts +++ b/src/client/graphics/NameBoxCalculator.ts @@ -1,4 +1,5 @@ import { Game, Player, Tile, Cell } from '../../core/game/Game'; +import { GameView } from '../../core/GameView'; import { calculateBoundingBox, within } from '../../core/Util'; export interface Point { @@ -14,7 +15,7 @@ export interface Rectangle { } -export function placeName(game: Game, player: Player): [position: Cell, fontSize: number] { +export function placeName(game: GameView, player: Player): [position: Cell, fontSize: number] { const boundingBox = calculateBoundingBox(player.borderTiles()); const rawScalingFactor = (boundingBox.max.x - boundingBox.min.x) / 50 @@ -38,7 +39,7 @@ export function placeName(game: Game, player: Player): [position: Cell, fontSize return [center, fontSize] } -export function createGrid(game: Game, player: Player, boundingBox: { min: Point; max: Point }, scalingFactor: number): boolean[][] { +export function createGrid(game: GameView, player: Player, boundingBox: { min: Point; max: Point }, scalingFactor: number): boolean[][] { const scaledBoundingBox: { min: Point; max: Point } = { min: { x: Math.floor(boundingBox.min.x / scalingFactor), diff --git a/src/client/graphics/TransformHandler.ts b/src/client/graphics/TransformHandler.ts index 5b59be6d1..fff819b02 100644 --- a/src/client/graphics/TransformHandler.ts +++ b/src/client/graphics/TransformHandler.ts @@ -5,6 +5,7 @@ import { calculateBoundingBox, calculateBoundingBoxCenter, manhattanDist } from import { ZoomEvent, DragEvent } from "../InputHandler"; import { GoToPlayerEvent } from "./layers/Leaderboard"; import { placeName } from "./NameBoxCalculator"; +import { GameView } from "../../core/GameView"; export class TransformHandler { public scale: number = 1.8 @@ -14,7 +15,7 @@ export class TransformHandler { private target: Cell private intervalID = null - constructor(private game: Game, private eventBus: EventBus, private canvas: HTMLCanvasElement) { + constructor(private game: GameView, private eventBus: EventBus, private canvas: HTMLCanvasElement) { this.eventBus.on(ZoomEvent, (e) => this.onZoom(e)) this.eventBus.on(DragEvent, (e) => this.onMove(e)) this.eventBus.on(GoToPlayerEvent, (e) => this.onGoToPlayer(e)) diff --git a/src/client/graphics/layers/ControlPanel.ts b/src/client/graphics/layers/ControlPanel.ts index c7060aa20..8ff3b6016 100644 --- a/src/client/graphics/layers/ControlPanel.ts +++ b/src/client/graphics/layers/ControlPanel.ts @@ -7,10 +7,11 @@ import { renderNumber, renderTroops } from '../../Utils'; import { EventBus } from '../../../core/EventBus'; import { UIState } from '../UIState'; import { SendSetTargetTroopRatioEvent } from '../../Transport'; +import { GameView } from '../../../core/GameView'; @customElement('control-panel') export class ControlPanel extends LitElement implements Layer { - private game: Game; + public game: GameView; public clientID: ClientID; public eventBus: EventBus; public uiState: UIState; @@ -51,8 +52,7 @@ export class ControlPanel extends LitElement implements Layer { @state() private _goldPerSecond: number; - init(game: Game) { - this.game = game; + init() { this.attackRatio = .20; this.uiState.attackRatio = this.attackRatio; this.currentTroopRatio = this.targetTroopRatio; diff --git a/src/client/graphics/layers/EventsDisplay.ts b/src/client/graphics/layers/EventsDisplay.ts index e598fc03f..14283f96b 100644 --- a/src/client/graphics/layers/EventsDisplay.ts +++ b/src/client/graphics/layers/EventsDisplay.ts @@ -17,6 +17,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'; interface Event { description: string; @@ -35,7 +36,7 @@ interface Event { @customElement('events-display') export class EventsDisplay extends LitElement implements Layer { public eventBus: EventBus; - public game: Game; + public game: GameView; public clientID: ClientID; private events: Event[] = []; diff --git a/src/client/graphics/layers/Layer.ts b/src/client/graphics/layers/Layer.ts index 9baa42744..55a3201de 100644 --- a/src/client/graphics/layers/Layer.ts +++ b/src/client/graphics/layers/Layer.ts @@ -1,7 +1,7 @@ import { Game } from "../../../core/game/Game" export interface Layer { - init(game: Game) + init() tick() renderLayer(context: CanvasRenderingContext2D) shouldTransform(): boolean diff --git a/src/client/graphics/layers/Leaderboard.ts b/src/client/graphics/layers/Leaderboard.ts index f5e44e559..062a4df4b 100644 --- a/src/client/graphics/layers/Leaderboard.ts +++ b/src/client/graphics/layers/Leaderboard.ts @@ -6,6 +6,7 @@ import { ClientID } from '../../../core/Schemas'; import { unsafeHTML } from 'lit/directives/unsafe-html.js'; import { EventBus, GameEvent } from '../../../core/EventBus'; import { renderNumber } from '../../Utils'; +import { GameView } from '../../../core/GameView'; interface Entry { name: string @@ -23,12 +24,11 @@ export class GoToPlayerEvent implements GameEvent { @customElement('leader-board') export class Leaderboard extends LitElement implements Layer { - private game: Game + public game: GameView public clientID: ClientID public eventBus: EventBus - init(game: Game) { - this.game = game + init() { } tick() { diff --git a/src/client/graphics/layers/NameLayer.ts b/src/client/graphics/layers/NameLayer.ts index 23b1fe44c..b70a1540a 100644 --- a/src/client/graphics/layers/NameLayer.ts +++ b/src/client/graphics/layers/NameLayer.ts @@ -13,6 +13,7 @@ import targetIcon from '../../../../resources/images/TargetIcon.png'; import { ClientID } from "../../../core/Schemas" import { EventBus } from "../../../core/EventBus" import { AlternateViewEvent } from "../../InputHandler" +import { GameView } from "../../../core/GameView" class RenderInfo { @@ -47,7 +48,7 @@ export class NameLayer implements Layer { private alternateView = false constructor( - private game: Game, + private game: GameView, private eventBus: EventBus, private theme: Theme, private transformHandler: TransformHandler, @@ -72,7 +73,7 @@ export class NameLayer implements Layer { return true } - public init(game: Game) { + public init() { } diff --git a/src/client/graphics/layers/PlayerInfoOverlay.ts b/src/client/graphics/layers/PlayerInfoOverlay.ts index a8c79edc6..42781bd92 100644 --- a/src/client/graphics/layers/PlayerInfoOverlay.ts +++ b/src/client/graphics/layers/PlayerInfoOverlay.ts @@ -9,11 +9,12 @@ import { MouseMoveEvent } from '../../InputHandler'; import { euclideanDist, distSortUnit } from '../../../core/Util'; import { renderNumber, renderTroops } from '../../Utils'; import { PauseGameEvent } from '../../Transport'; +import { GameView } from '../../../core/GameView'; @customElement('player-info-overlay') export class PlayerInfoOverlay extends LitElement implements Layer { @property({ type: Object }) - public game!: Game; + public game!: GameView; @property({ type: String }) public clientID!: ClientID; @@ -41,8 +42,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer { private _isActive = false - init(game: Game) { - this.game = game; + init() { this.eventBus.on(MouseMoveEvent, (e: MouseMoveEvent) => this.onMouseEvent(e)); this._isActive = true this.showPauseButton = this.game.config().gameConfig().gameType == GameType.Singleplayer diff --git a/src/client/graphics/layers/StructureLayer.ts b/src/client/graphics/layers/StructureLayer.ts index dd5c493bd..d0c5f5337 100644 --- a/src/client/graphics/layers/StructureLayer.ts +++ b/src/client/graphics/layers/StructureLayer.ts @@ -9,6 +9,7 @@ import anchorIcon from '../../../../resources/images/AnchorIcon.png'; import missileSiloIcon from '../../../../resources/images/MissileSiloUnit.png'; import shieldIcon from '../../../../resources/images/ShieldIcon.png'; import cityIcon from '../../../../resources/images/CityIcon.png'; +import { GameView } from "../../../core/GameView"; interface UnitRenderConfig { icon: string; @@ -47,7 +48,7 @@ export class StructureLayer implements Layer { } }; - constructor(private game: Game, private eventBus: EventBus) { + constructor(private game: GameView, private eventBus: EventBus) { this.theme = game.config().theme(); this.loadUnitImages(); } @@ -69,7 +70,7 @@ export class StructureLayer implements Layer { tick() { } - init(game: Game) { + init() { this.eventBus.on(UnitEvent, e => this.onUnitEvent(e)); this.redraw() } diff --git a/src/client/graphics/layers/TerrainLayer.ts b/src/client/graphics/layers/TerrainLayer.ts index 444c9adaa..4f19d2b2c 100644 --- a/src/client/graphics/layers/TerrainLayer.ts +++ b/src/client/graphics/layers/TerrainLayer.ts @@ -3,6 +3,7 @@ import { Game } from "../../../core/game/Game"; import { throws } from "assert"; import { Layer } from "./Layer"; import { TransformHandler } from "../TransformHandler"; +import { GameView } from "../../../core/GameView"; export class TerrainLayer implements Layer { private canvas: HTMLCanvasElement @@ -10,14 +11,14 @@ export class TerrainLayer implements Layer { private imageData: ImageData - constructor(private game: Game) { } + constructor(private game: GameView) { } shouldTransform(): boolean { return true } tick() { } - init(game: Game) { + init() { console.log('redrew terrain layer') this.redraw() } diff --git a/src/client/graphics/layers/TerritoryLayer.ts b/src/client/graphics/layers/TerritoryLayer.ts index 5b915ae5b..fc34ed53c 100644 --- a/src/client/graphics/layers/TerritoryLayer.ts +++ b/src/client/graphics/layers/TerritoryLayer.ts @@ -9,6 +9,7 @@ import { TransformHandler } from "../TransformHandler"; import { EventBus } from "../../../core/EventBus"; import { initRemoteSender } from "../../../core/Consolex"; import { AlternateViewEvent } from "../../InputHandler"; +import { GameView } from "../../../core/GameView"; export class TerritoryLayer implements Layer { private canvas: HTMLCanvasElement @@ -26,7 +27,7 @@ export class TerritoryLayer implements Layer { private alternativeView = false - constructor(private game: Game, private eventBus: EventBus) { + constructor(private game: GameView, private eventBus: EventBus) { this.theme = game.config().theme() } @@ -59,7 +60,7 @@ export class TerritoryLayer implements Layer { } } - init(game: Game) { + init() { this.eventBus.on(TileEvent, e => this.tileUpdate(e)) this.eventBus.on(AlternateViewEvent, e => { this.alternativeView = e.alternateView }) this.redraw() diff --git a/src/client/graphics/layers/UILayer.ts b/src/client/graphics/layers/UILayer.ts index 4d3ecbfa6..853ad7a95 100644 --- a/src/client/graphics/layers/UILayer.ts +++ b/src/client/graphics/layers/UILayer.ts @@ -5,6 +5,7 @@ import { ClientID } from "../../../core/Schemas"; import { Layer } from "./Layer"; import { TransformHandler } from "../TransformHandler"; import { consolex } from "../../../core/Consolex"; +import { GameView } from "../../../core/GameView"; interface MenuOption { label: string; @@ -20,7 +21,7 @@ export class UILayer implements Layer { constructor( private eventBus: EventBus, - private game: Game, + private game: GameView, private clientID: ClientID, private transformHandler: TransformHandler ) { @@ -52,7 +53,7 @@ export class UILayer implements Layer { tick() { } - init(game: Game) { + init() { this.createWinModal() this.initRightClickMenu() this.eventBus.on(WinEvent, (e) => this.onWinEvent(e)) diff --git a/src/client/graphics/layers/UnitLayer.ts b/src/client/graphics/layers/UnitLayer.ts index 5a2da1f8a..3d42d0e91 100644 --- a/src/client/graphics/layers/UnitLayer.ts +++ b/src/client/graphics/layers/UnitLayer.ts @@ -6,6 +6,7 @@ import { Layer } from "./Layer"; import { EventBus } from "../../../core/EventBus"; import { AlternateViewEvent } from "../../InputHandler"; import { ClientID } from "../../../core/Schemas"; +import { GameView } from "../../../core/GameView"; enum Relationship { Self, @@ -28,7 +29,7 @@ export class UnitLayer implements Layer { private oldShellTile = new Map() - constructor(private game: Game, private eventBus: EventBus, private clientID: ClientID) { + constructor(private game: GameView, private eventBus: EventBus, private clientID: ClientID) { this.theme = game.config().theme(); } @@ -43,7 +44,7 @@ export class UnitLayer implements Layer { } } - init(game: Game) { + init() { this.eventBus.on(UnitEvent, e => this.onUnitEvent(e)); this.eventBus.on(AlternateViewEvent, e => this.onAlternativeViewEvent(e)) this.redraw() diff --git a/src/client/graphics/layers/radial/BuildMenu.ts b/src/client/graphics/layers/radial/BuildMenu.ts index d3e14fcf8..347d4efe7 100644 --- a/src/client/graphics/layers/radial/BuildMenu.ts +++ b/src/client/graphics/layers/radial/BuildMenu.ts @@ -14,6 +14,7 @@ import shieldIcon from '../../../../../resources/images/ShieldIconWhite.svg'; import cityIcon from '../../../../../resources/images/CityIconWhite.svg'; import { renderNumber } from '../../../Utils'; import { ContextMenuEvent } from '../../../InputHandler'; +import { GameView } from '../../../../core/GameView'; interface BuildItemDisplay { unitType: UnitType @@ -35,7 +36,7 @@ const buildTable: BuildItemDisplay[][] = [ @customElement('build-menu') export class BuildMenu extends LitElement { - public game: Game; + public game: GameView; public eventBus: EventBus; private myPlayer: Player; private clickedCell: Cell; diff --git a/src/client/graphics/layers/radial/RadialMenu.ts b/src/client/graphics/layers/radial/RadialMenu.ts index 853fc5772..07bc8106e 100644 --- a/src/client/graphics/layers/radial/RadialMenu.ts +++ b/src/client/graphics/layers/radial/RadialMenu.ts @@ -20,6 +20,7 @@ import { EmojiTable } from "./EmojiTable"; import { UIState } from "../../UIState"; import { BuildMenu } from "./BuildMenu"; import { consolex } from "../../../../core/Consolex"; +import { GameView } from "../../../../core/GameView"; enum Slot { @@ -53,7 +54,7 @@ export class RadialMenu implements Layer { constructor( private eventBus: EventBus, - private game: Game, + private game: GameView, private transformHandler: TransformHandler, private clientID: ClientID, private emojiTable: EmojiTable, @@ -363,7 +364,7 @@ export class RadialMenu implements Layer { } if (myPlayerBordersOcean && otherPlayerBordersOcean) { - const dst = targetTransportTile(this.game, tile) + const dst = targetTransportTile(this.game.width(), tile) if (dst != null) { if (myPlayer.canBuild(UnitType.TransportShip, dst)) { this.activateMenuElement(Slot.Boat, "#3f6ab1", boatIcon, () => { diff --git a/src/core/GameView.ts b/src/core/GameView.ts index 7a8db599a..e149564e8 100644 --- a/src/core/GameView.ts +++ b/src/core/GameView.ts @@ -1,6 +1,6 @@ -import { MessageType } from './game/Game'; +import { MessageType, Player, Tile, Unit } from './game/Game'; import { Config } from "./configuration/Config"; -import { Alliance, AllianceRequest, AllPlayers, Cell, DefenseBonus, EmojiMessage, Execution, ExecutionView, Game, Gold, MutableTile, Nation, Player, PlayerID, PlayerInfo, PlayerType, Relation, TerrainMap, TerrainTile, TerrainType, TerraNullius, Tick, Tile, Unit, UnitInfo, UnitType } from "./game/Game"; +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"; export interface ViewSerializable { @@ -20,8 +20,8 @@ export interface TileViewData extends ViewData { isBorder: boolean } -export class TileView implements Tile { - constructor(private game: Game, private data: TileViewData, private _terrain: TerrainTile) { } +export class TileView { + constructor(private game: GameView, private data: TileViewData, private _terrain: TerrainTile) { } type(): TerrainType { return this._terrain.type() } @@ -44,7 +44,7 @@ export class TileView implements Tile { return this._terrain } - neighbors(): Tile[] { + neighbors(): TileView[] { throw new Error("Method not implemented."); } @@ -67,7 +67,7 @@ export interface UnitViewData extends ViewData { health?: number } -export class UnitView implements Unit { +export class UnitView { constructor(private data: UnitViewData) { } type(): UnitType { @@ -76,10 +76,10 @@ export class UnitView implements Unit { troops(): number { throw new Error("Method not implemented."); } - tile(): Tile { + tile(): TileView { throw new Error("Method not implemented."); } - owner(): Player { + owner(): PlayerView { throw new Error("Method not implemented."); } isActive(): boolean { @@ -109,7 +109,7 @@ export interface PlayerViewData extends ViewData { targetTroopRatio: number } -export class PlayerView implements Player { +export class PlayerView { constructor(private game: Game, private data: PlayerViewData) { } name(): string { return this.data.name @@ -129,7 +129,7 @@ export class PlayerView implements Player { isAlive(): boolean { return this.data.isAlive } - isPlayer(): this is Player { + isPlayer(): this is PlayerView { return true } numTilesOwned(): number { @@ -211,9 +211,6 @@ export class PlayerView implements Player { canBuild(type: UnitType, targetTile: Tile): Tile | false { return false } - lastTileChange(): Tick { - return 0 - } info(): PlayerInfo { return null } @@ -225,7 +222,7 @@ export interface GameUpdateViewData extends ViewData { tileUpdates: TileViewData[] } -export class GameView implements Game { +export class GameView { private lastGameUpdate: GameUpdateViewData private tiles: TileViewData[][] = [] @@ -266,9 +263,6 @@ export class GameView implements Game { isOnMap(cell: Cell): boolean { throw new Error("Method not implemented."); } - neighbors(cell: Cell | Tile): Tile[] { - throw new Error("Method not implemented."); - } width(): number { throw new Error("Method not implemented."); } diff --git a/src/core/Util.ts b/src/core/Util.ts index 9e5f29774..728152f2e 100644 --- a/src/core/Util.ts +++ b/src/core/Util.ts @@ -7,6 +7,7 @@ import { Cell, Game, Player, TerraNullius, Tile, Unit } from "./game/Game"; import { number } from 'zod'; import { GameConfig, GameID, GameRecord, PlayerRecord, Turn } from './Schemas'; import { customAlphabet, nanoid } from 'nanoid'; +import { GameView } from './GameView'; @@ -62,7 +63,7 @@ export function and(x: (tile: Tile) => boolean, y: (tile: Tile) => boolean): (ti } // TODO: refactor to new file -export function sourceDstOceanShore(game: Game, src: Player, tile: Tile): [Tile | null, Tile | null] { +export function sourceDstOceanShore(game: GameView, src: Player, tile: Tile): [Tile | null, Tile | null] { const dst = tile.owner() let srcTile = closestOceanShoreFromPlayer(src, tile, game.width()) let dstTile: Tile | null = null @@ -74,11 +75,11 @@ export function sourceDstOceanShore(game: Game, src: Player, tile: Tile): [Tile return [srcTile, dstTile] } -export function targetTransportTile(game: Game, tile: Tile): Tile | null { +export function targetTransportTile(gameWidth: number, tile: Tile): Tile | null { const dst = tile.owner() let dstTile: Tile | null = null if (dst.isPlayer()) { - dstTile = closestOceanShoreFromPlayer(dst as Player, tile, game.width()) + dstTile = closestOceanShoreFromPlayer(dst as Player, tile, gameWidth) } else { dstTile = closestOceanShoreTN(tile, 300) } diff --git a/src/core/execution/TransportShipExecution.ts b/src/core/execution/TransportShipExecution.ts index e9e14439f..34a0e40f1 100644 --- a/src/core/execution/TransportShipExecution.ts +++ b/src/core/execution/TransportShipExecution.ts @@ -68,7 +68,7 @@ export class TransportShipExecution implements Execution { this.troops = Math.min(this.troops, this.attacker.troops()) - this.dst = targetTransportTile(this.mg, this.mg.tile(this.cell)) + this.dst = targetTransportTile(this.mg.width(), this.mg.tile(this.cell)) if (this.dst == null) { consolex.warn(`${this.attacker} cannot send ship to ${this.target}, cannot find attack tile`) this.active = false diff --git a/src/core/worker/WorkerClient.ts b/src/core/worker/WorkerClient.ts index 6684b97ec..543e951d2 100644 --- a/src/core/worker/WorkerClient.ts +++ b/src/core/worker/WorkerClient.ts @@ -17,7 +17,7 @@ export class WorkerClient { this.worker = new Worker(new URL('./Worker.worker.ts', import.meta.url)); } - initialize(gameUpdate: (gu: GameUpdateViewData) => void): Promise { + initialize(): Promise { return new Promise((resolve, reject) => { this.worker.postMessage({ type: 'init', @@ -28,21 +28,28 @@ export class WorkerClient { const handler = (e: MessageEvent) => { if (e.data.type === 'initialized') { this.isInitialized = true; + this.worker.removeEventListener('message', handler) resolve(); return } - if (!this.isInitialized) { - reject('Failed to initialize pathfinder'); - } - if (e.data.type == "game_update") { - gameUpdate(e.data.gameUpdate) - } }; this.worker.addEventListener('message', handler); }); } + start(gameUpdate: (gu: GameUpdateViewData) => void) { + if (!this.isInitialized) { + throw new Error('Failed to initialize pathfinder'); + } + const handler = (e: MessageEvent) => { + if (e.data.type == "game_update") { + gameUpdate(e.data.gameUpdate) + } + } + this.worker.addEventListener('message', handler); + } + sendTurn(turn: Turn) { this.worker.postMessage({ type: "turn",