From bbf768025e370fc5114aa79e6a15da9ef3bea402 Mon Sep 17 00:00:00 2001 From: scamiv <6170744+scamiv@users.noreply.github.com> Date: Thu, 4 Dec 2025 00:46:38 +0100 Subject: [PATCH] Refactor territory defense management in GameImpl and CanvasTerritoryRenderer - Consolidated defended state logic for tiles into dedicated methods in GameImpl to improve clarity and maintainability. - Updated CanvasTerritoryRenderer to utilize the new isDefended method for determining tile defense status. - Removed redundant checks and streamlined the painting logic for territory tiles. --- .../graphics/layers/TerritoryRenderers.ts | 36 +-------- src/core/game/GameImpl.ts | 73 +++++++++++++++++++ 2 files changed, 75 insertions(+), 34 deletions(-) diff --git a/src/client/graphics/layers/TerritoryRenderers.ts b/src/client/graphics/layers/TerritoryRenderers.ts index c3ab6aafa..3d453d539 100644 --- a/src/client/graphics/layers/TerritoryRenderers.ts +++ b/src/client/graphics/layers/TerritoryRenderers.ts @@ -1,6 +1,5 @@ import { Colord } from "colord"; import { Theme } from "../../../core/configuration/Config"; -import { UnitType } from "../../../core/game/Game"; import { TileRef } from "../../../core/game/GameMap"; import { GameView, PlayerView } from "../../../core/game/GameView"; import { FrameProfiler } from "../FrameProfiler"; @@ -83,15 +82,8 @@ export class CanvasTerritoryRenderer implements TerritoryRendererStrategy { : null; const isBorderTile = this.game.isBorder(tile); const hasFallout = this.game.hasFallout(tile); - let isDefended = false; - if (owner && isBorderTile) { - isDefended = this.game.hasUnitNearby( - tile, - this.game.config().defensePostRange(), - UnitType.DefensePost, - owner.id(), - ); - } + const isDefended = + owner && isBorderTile ? this.game.isDefended(tile) : false; if (!owner) { if (hasFallout) { @@ -264,30 +256,6 @@ export class WebglTerritoryRenderer implements TerritoryRendererStrategy { } paintTile(tile: TileRef): void { - const hasOwner = this.game.hasOwner(tile); - const rawOwner = hasOwner ? this.game.owner(tile) : null; - const owner = - rawOwner && - typeof (rawOwner as any).isPlayer === "function" && - (rawOwner as any).isPlayer() - ? (rawOwner as PlayerView) - : null; - const isBorderTile = this.game.isBorder(tile); - - // Update defended state in the shared buffer (used for checkerboard pattern). - if (owner && isBorderTile) { - const isDefended = this.game.hasUnitNearby( - tile, - this.game.config().defensePostRange(), - UnitType.DefensePost, - owner.id(), - ); - this.game.setDefended(tile, isDefended); - } else { - // Clear defended state for non-border tiles - this.game.setDefended(tile, false); - } - this.renderer.markTile(tile); } diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index 26127f558..64361cfd6 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -532,6 +532,10 @@ export class GameImpl implements Game { owner._lastTileChange = this._ticks; this.updateBorders(tile); this._map.setFallout(tile, false); + + // Update defended state for the conquered tile and nearby border tiles + this.updateDefendedStateForTileChange(tile, owner); + this.addUpdate({ type: GameUpdateType.Tile, update: this.toTileUpdate(tile), @@ -553,6 +557,9 @@ export class GameImpl implements Game { this._map.setOwnerID(tile, 0); this.updateBorders(tile); + if (this._map.isDefended(tile)) { + this._map.setDefended(tile, false); + } this.addUpdate({ type: GameUpdateType.Tile, update: this.toTileUpdate(tile), @@ -752,14 +759,23 @@ export class GameImpl implements Game { addUnit(u: Unit) { this.unitGrid.addUnit(u); + if (u.type() === UnitType.DefensePost) { + this.updateDefendedStateForDefensePost(u.tile(), u.owner() as PlayerImpl); + } } removeUnit(u: Unit) { + if (u.type() === UnitType.DefensePost) { + this.updateDefendedStateForDefensePost(u.tile(), u.owner() as PlayerImpl); + } this.unitGrid.removeUnit(u); if (u.hasTrainStation()) { this._railNetwork.removeStation(u); } } updateUnitTile(u: Unit) { + if (u.type() === UnitType.DefensePost) { + this.updateDefendedStateForDefensePost(u.tile(), u.owner() as PlayerImpl); + } this.unitGrid.updateUnitCell(u); } @@ -955,6 +971,63 @@ export class GameImpl implements Game { // Record stats this.stats().goldWar(conqueror, conquered, gold); } + + /** + * Update defended state for border tiles within range of a defense post. + */ + private updateDefendedStateForDefensePost( + center: TileRef, + owner: PlayerImpl, + ) { + const range = this.config().defensePostRange(); + const rangeSq = range * range; + + for (const tile of owner._borderTiles) { + if (this._map.euclideanDistSquared(center, tile) <= rangeSq) { + const wasDefended = this._map.isDefended(tile); + const isDefended = this.unitGrid.hasUnitNearby( + tile, + range, + UnitType.DefensePost, + owner.id(), + ); + if (wasDefended !== isDefended) { + this._map.setDefended(tile, isDefended); + this.addUpdate({ + type: GameUpdateType.Tile, + update: this.toTileUpdate(tile), + }); + } + } + } + } + + /** + * Update defended state when a tile changes ownership. + */ + private updateDefendedStateForTileChange(tile: TileRef, owner: PlayerImpl) { + const wasDefended = this._map.isDefended(tile); + const isDefended = this.unitGrid.hasUnitNearby( + tile, + this.config().defensePostRange(), + UnitType.DefensePost, + owner.id(), + ); + if (wasDefended !== isDefended) { + this._map.setDefended(tile, isDefended); + this.addUpdate({ + type: GameUpdateType.Tile, + update: this.toTileUpdate(tile), + }); + } + + // If the conquered tile has a defense post, update nearby border tiles + if ( + this.unitGrid.hasUnitNearby(tile, 0, UnitType.DefensePost, owner.id()) + ) { + this.updateDefendedStateForDefensePost(tile, owner); + } + } } // Or a more dynamic approach that will catch new enum values: