From 92d2b45340264c1d6fc247f958d1a15d4ee61a0a Mon Sep 17 00:00:00 2001 From: variablevince <24507472+VariableVince@users.noreply.github.com> Date: Fri, 24 Oct 2025 00:17:10 +0200 Subject: [PATCH] First commit perf canbuild transport ship --- src/client/ClientGameRunner.ts | 16 ++++-- .../graphics/layers/StructureIconsLayer.ts | 3 +- src/client/graphics/layers/UnitDisplay.ts | 10 +++- src/core/GameRunner.ts | 4 +- src/core/game/Game.ts | 11 +++- src/core/game/GameView.ts | 7 ++- src/core/game/PlayerImpl.ts | 51 +++++++++++++++---- src/core/game/TransportShipUtils.ts | 43 +++------------- src/core/worker/Worker.worker.ts | 1 + src/core/worker/WorkerClient.ts | 3 ++ src/core/worker/WorkerMessages.ts | 2 + 11 files changed, 94 insertions(+), 57 deletions(-) diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index fbb1b83aa..f4169321d 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -12,7 +12,11 @@ import { import { createPartialGameRecord, replacer } from "../core/Util"; import { ServerConfig } from "../core/configuration/Config"; import { getConfig } from "../core/configuration/ConfigLoader"; -import { PlayerActions, UnitType } from "../core/game/Game"; +import { + PlayerActions, + TransportShipFilter, + UnitType, +} from "../core/game/Game"; import { TileRef } from "../core/game/GameMap"; import { GameMapLoader } from "../core/game/GameMapLoader"; import { @@ -414,13 +418,14 @@ export class ClientGameRunner { if (myPlayer === null) return; this.myPlayer = myPlayer; } - this.myPlayer.actions(tile).then((actions) => { - if (this.myPlayer === null) return; + this.myPlayer.actions(tile, TransportShipFilter.Only).then((actions) => { + // if (this.myPlayer === null) return; //--> redundant check as set above?, but need to add ! below to stop error 'can be null' if (actions.canAttack) { + //-->maybe only get actions.canAttack + transportship from buildableunits? Not everything else? this.eventBus.emit( new SendAttackIntentEvent( this.gameView.owner(tile).id(), - this.myPlayer.troops() * this.renderer.uiState.attackRatio, + this.myPlayer!.troops() * this.renderer.uiState.attackRatio, ), ); } else if (this.canAutoBoat(actions, tile)) { @@ -511,7 +516,8 @@ export class ClientGameRunner { this.myPlayer = myPlayer; } - this.myPlayer.actions(tile).then((actions) => { + this.myPlayer.actions(tile, TransportShipFilter.Only).then((actions) => { + //-->> maybe only get transportship from buildableunits? Not everything else? if (this.canBoatAttack(actions) !== false) { this.sendBoatAttackIntent(tile); } diff --git a/src/client/graphics/layers/StructureIconsLayer.ts b/src/client/graphics/layers/StructureIconsLayer.ts index 4f86c8070..0e9050bad 100644 --- a/src/client/graphics/layers/StructureIconsLayer.ts +++ b/src/client/graphics/layers/StructureIconsLayer.ts @@ -10,6 +10,7 @@ import { Cell, PlayerActions, PlayerID, + TransportShipFilter, UnitType, } from "../../../core/game/Game"; import { TileRef } from "../../../core/game/GameMap"; @@ -249,7 +250,7 @@ export class StructureIconsLayer implements Layer { this.game ?.myPlayer() - ?.actions(tileRef) + ?.actions(tileRef, TransportShipFilter.Exclude) .then((actions) => { if (this.potentialUpgrade) { this.potentialUpgrade.iconContainer.filters = []; diff --git a/src/client/graphics/layers/UnitDisplay.ts b/src/client/graphics/layers/UnitDisplay.ts index d2ae6a93f..9b7a58133 100644 --- a/src/client/graphics/layers/UnitDisplay.ts +++ b/src/client/graphics/layers/UnitDisplay.ts @@ -11,7 +11,12 @@ import portIcon from "../../../../resources/images/PortIcon.svg"; import samLauncherIcon from "../../../../resources/images/SamLauncherIconWhite.svg"; import defensePostIcon from "../../../../resources/images/ShieldIconWhite.svg"; import { EventBus } from "../../../core/EventBus"; -import { Gold, PlayerActions, UnitType } from "../../../core/game/Game"; +import { + Gold, + PlayerActions, + TransportShipFilter, + UnitType, +} from "../../../core/game/Game"; import { GameView } from "../../../core/game/GameView"; import { ToggleStructureEvent } from "../../InputHandler"; import { renderNumber, translateText } from "../../Utils"; @@ -97,7 +102,8 @@ export class UnitDisplay extends LitElement implements Layer { tick() { const player = this.game?.myPlayer(); - player?.actions().then((actions) => { + player?.actions(undefined, TransportShipFilter.Exclude).then((actions) => { + // player?.actions(undefined, TransportShipFilter.Default).then((actions) => { this.playerActions = actions; }); if (!player) return; diff --git a/src/core/GameRunner.ts b/src/core/GameRunner.ts index 3a309359f..d65781002 100644 --- a/src/core/GameRunner.ts +++ b/src/core/GameRunner.ts @@ -17,6 +17,7 @@ import { PlayerInfo, PlayerProfile, PlayerType, + TransportShipFilter, } from "./game/Game"; import { createGame } from "./game/GameImpl"; import { TileRef } from "./game/GameMap"; @@ -181,13 +182,14 @@ export class GameRunner { playerID: PlayerID, x?: number, y?: number, + TransportShipFilter?: TransportShipFilter, ): PlayerActions { const player = this.game.player(playerID); const tile = x !== undefined && y !== undefined ? this.game.ref(x, y) : null; const actions = { canAttack: tile !== null && player.canAttack(tile), - buildableUnits: player.buildableUnits(tile), + buildableUnits: player.buildableUnits(tile, TransportShipFilter), canSendEmojiAllPlayers: player.canSendEmoji(AllPlayers), } as PlayerActions; diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index db48e0a2f..c3be10814 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -560,7 +560,10 @@ export interface Player { unitCount(type: UnitType): number; unitsConstructed(type: UnitType): number; unitsOwned(type: UnitType): number; - buildableUnits(tile: TileRef | null): BuildableUnit[]; + buildableUnits( + tile: TileRef | null, + TransportShipFilter?: TransportShipFilter, + ): BuildableUnit[]; canBuild(type: UnitType, targetTile: TileRef): TileRef | false; buildUnit( type: T, @@ -857,3 +860,9 @@ export interface NameViewData { y: number; size: number; } + +export enum TransportShipFilter { + Default = "default", // Include TransportShip with all units + Exclude = "exclude", // Skip TransportShip in the unit list + Only = "only", // Only check TransportShip +} diff --git a/src/core/game/GameView.ts b/src/core/game/GameView.ts index d6ed3730d..1acd68ea3 100644 --- a/src/core/game/GameView.ts +++ b/src/core/game/GameView.ts @@ -22,6 +22,7 @@ import { TerraNullius, Tick, TrainType, + TransportShipFilter, UnitInfo, UnitType, } from "./Game"; @@ -272,11 +273,15 @@ export class PlayerView { : this._defendedBorderColors.dark; } - async actions(tile?: TileRef): Promise { + async actions( + tile?: TileRef, + TransportShipFilter?: TransportShipFilter, + ): Promise { return this.game.worker.playerInteraction( this.id(), tile && this.game.x(tile), tile && this.game.y(tile), + TransportShipFilter, ); } diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index d9ba8ba7c..fe88cb1a3 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -33,6 +33,7 @@ import { Team, TerraNullius, Tick, + TransportShipFilter, Unit, UnitParams, UnitType, @@ -879,26 +880,58 @@ export class PlayerImpl implements Player { this.recordUnitConstructed(unit.type()); } - public buildableUnits(tile: TileRef | null): BuildableUnit[] { + public buildableUnits( + tile: TileRef | null, + transportShipFilter: TransportShipFilter = TransportShipFilter.Default, + ): BuildableUnit[] { + const inSpawnPhase = this.mg.inSpawnPhase(); + + if (transportShipFilter === TransportShipFilter.Only) { + const u = UnitType.TransportShip; + return [ + { + type: u, + canBuild: + inSpawnPhase || tile === null + ? false + : this.canBuild(u, tile, null), + canUpgrade: !this.mg.config().unitInfo(u).upgradable ? false : 0, + cost: this.mg.config().unitInfo(u).cost(this), + } as BuildableUnit, + ]; + } + const validTiles = tile !== null ? this.validStructureSpawnTiles(tile) : []; - return Object.values(UnitType).map((u) => { + const result: BuildableUnit[] = []; + + for (const u of Object.values(UnitType)) { + if ( + u === UnitType.TransportShip && + transportShipFilter === TransportShipFilter.Exclude + ) { + continue; + } + let canUpgrade: number | false = false; - if (!this.mg.inSpawnPhase()) { - const existingUnit = tile !== null && this.findUnitToUpgrade(u, tile); + if (!inSpawnPhase && tile !== null) { + const existingUnit = this.findUnitToUpgrade(u, tile); if (existingUnit !== false) { canUpgrade = existingUnit.id(); } } - return { + + result.push({ type: u, canBuild: - this.mg.inSpawnPhase() || tile === null + inSpawnPhase || tile === null ? false : this.canBuild(u, tile, validTiles), - canUpgrade: canUpgrade, + canUpgrade, cost: this.mg.config().unitInfo(u).cost(this), - } as BuildableUnit; - }); + } as BuildableUnit); + } + + return result; } canBuild( diff --git a/src/core/game/TransportShipUtils.ts b/src/core/game/TransportShipUtils.ts index b457ad94a..456853372 100644 --- a/src/core/game/TransportShipUtils.ts +++ b/src/core/game/TransportShipUtils.ts @@ -14,11 +14,6 @@ export function canBuildTransportShip( return false; } - const dst = targetTransportTile(game, tile); - if (dst === null) { - return false; - } - const other = game.owner(tile); if (other === player) { return false; @@ -27,6 +22,11 @@ export function canBuildTransportShip( return false; } + const dst = targetTransportTile(game, tile); + if (dst === null) { + return false; + } + if (game.isOceanShore(dst)) { let myPlayerBordersOcean = false; for (const bt of player.borderTiles()) { @@ -36,28 +36,13 @@ export function canBuildTransportShip( } } - let otherPlayerBordersOcean = false; - if (!game.hasOwner(tile)) { - otherPlayerBordersOcean = true; - } else { - for (const bt of (other as Player).borderTiles()) { - if (game.isOceanShore(bt)) { - otherPlayerBordersOcean = true; - break; - } - } - } - - if (myPlayerBordersOcean && otherPlayerBordersOcean) { + if (myPlayerBordersOcean) { return transportShipSpawn(game, player, dst); } else { return false; } } - // Now we are boating in a lake, so do a bfs from target until we find - // a border tile owned by the player - const tiles = game.bfs( dst, andFN( @@ -93,22 +78,6 @@ function transportShipSpawn( return spawn; } -export function sourceDstOceanShore( - gm: Game, - src: Player, - tile: TileRef, -): [TileRef | null, TileRef | null] { - const dst = gm.owner(tile); - const srcTile = closestShoreFromPlayer(gm, src, tile); - let dstTile: TileRef | null = null; - if (dst.isPlayer()) { - dstTile = closestShoreFromPlayer(gm, dst as Player, tile); - } else { - dstTile = closestShoreTN(gm, tile, 50); - } - return [srcTile, dstTile]; -} - export function targetTransportTile(gm: Game, tile: TileRef): TileRef | null { const dst = gm.playerBySmallID(gm.ownerID(tile)); let dstTile: TileRef | null = null; diff --git a/src/core/worker/Worker.worker.ts b/src/core/worker/Worker.worker.ts index 1014968fb..1cafc3231 100644 --- a/src/core/worker/Worker.worker.ts +++ b/src/core/worker/Worker.worker.ts @@ -83,6 +83,7 @@ ctx.addEventListener("message", async (e: MessageEvent) => { message.playerID, message.x, message.y, + message.TransportShipFilter, ); sendMessage({ type: "player_actions_result", diff --git a/src/core/worker/WorkerClient.ts b/src/core/worker/WorkerClient.ts index bde436f39..3e384956d 100644 --- a/src/core/worker/WorkerClient.ts +++ b/src/core/worker/WorkerClient.ts @@ -4,6 +4,7 @@ import { PlayerBorderTiles, PlayerID, PlayerProfile, + TransportShipFilter, } from "../game/Game"; import { TileRef } from "../game/GameMap"; import { ErrorUpdate, GameUpdateViewData } from "../game/GameUpdates"; @@ -162,6 +163,7 @@ export class WorkerClient { playerID: PlayerID, x?: number, y?: number, + TransportShipFilter?: TransportShipFilter, ): Promise { return new Promise((resolve, reject) => { if (!this.isInitialized) { @@ -186,6 +188,7 @@ export class WorkerClient { playerID: playerID, x: x, y: y, + TransportShipFilter: TransportShipFilter, }); }); } diff --git a/src/core/worker/WorkerMessages.ts b/src/core/worker/WorkerMessages.ts index a8d30e9b1..dc2be8ee9 100644 --- a/src/core/worker/WorkerMessages.ts +++ b/src/core/worker/WorkerMessages.ts @@ -3,6 +3,7 @@ import { PlayerBorderTiles, PlayerID, PlayerProfile, + TransportShipFilter, } from "../game/Game"; import { TileRef } from "../game/GameMap"; import { GameUpdateViewData } from "../game/GameUpdates"; @@ -62,6 +63,7 @@ export interface PlayerActionsMessage extends BaseWorkerMessage { playerID: PlayerID; x?: number; y?: number; + TransportShipFilter?: TransportShipFilter; } export interface PlayerActionsResultMessage extends BaseWorkerMessage {