diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index ad114234c..149706c8f 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -191,7 +191,7 @@ export function createRenderer( const layers: Layer[] = [ new TerrainLayer(game, transformHandler), - new TerritoryLayer(game, eventBus), + new TerritoryLayer(game, eventBus, transformHandler), structureLayer, new UnitLayer(game, eventBus, transformHandler), new FxLayer(game), diff --git a/src/client/graphics/layers/TerritoryLayer.ts b/src/client/graphics/layers/TerritoryLayer.ts index deb2cfd0a..550bab478 100644 --- a/src/client/graphics/layers/TerritoryLayer.ts +++ b/src/client/graphics/layers/TerritoryLayer.ts @@ -8,6 +8,7 @@ import { GameUpdateType } from "../../../core/game/GameUpdates"; import { GameView, PlayerView } from "../../../core/game/GameView"; import { PseudoRandom } from "../../../core/PseudoRandom"; import { AlternateViewEvent, DragEvent } from "../../InputHandler"; +import { TransformHandler } from "../TransformHandler"; import { Layer } from "./Layer"; export class TerritoryLayer implements Layer { @@ -32,7 +33,7 @@ export class TerritoryLayer implements Layer { private lastDragTime = 0; private nodrawDragDuration = 200; - private refreshRate = 10; + private refreshRate = 10; //refresh every 10ms private lastRefresh = 0; private lastFocusedPlayer: PlayerView | null = null; @@ -40,6 +41,7 @@ export class TerritoryLayer implements Layer { constructor( private game: GameView, private eventBus: EventBus, + private transformHandler: TransformHandler, ) { this.theme = game.config().theme(); } @@ -48,11 +50,10 @@ export class TerritoryLayer implements Layer { return true; } - paintPlayerBorder(player: PlayerView) { - player.borderTiles().then((playerBorderTiles) => { - playerBorderTiles.borderTiles.forEach((tile: TileRef) => { - this.paintTerritory(tile, true); // Immediately paint the tile instead of enqueueing - }); + async paintPlayerBorder(player: PlayerView) { + const tiles = await player.borderTiles(); + tiles.borderTiles.forEach((tile: TileRef) => { + this.paintTerritory(tile, true); // Immediately paint the tile instead of enqueueing }); } @@ -128,11 +129,7 @@ export class TerritoryLayer implements Layer { euclDistFN(centerTile, 9, true), )) { if (!this.game.hasOwner(tile)) { - this.paintHighlightCell( - new Cell(this.game.x(tile), this.game.y(tile)), - color, - 255, - ); + this.paintHighlightTile(tile, color, 255); } } } @@ -155,16 +152,16 @@ export class TerritoryLayer implements Layer { const context = this.canvas.getContext("2d"); if (context === null) throw new Error("2d context not supported"); this.context = context; + this.canvas.width = this.game.width(); + this.canvas.height = this.game.height(); this.imageData = this.context.getImageData( 0, 0, - this.game.width(), - this.game.height(), + this.canvas.width, + this.canvas.height, ); this.initImageData(); - this.canvas.width = this.game.width(); - this.canvas.height = this.game.height(); this.context.putImageData(this.imageData, 0, 0); // Add a second canvas for highlights @@ -199,7 +196,19 @@ export class TerritoryLayer implements Layer { ) { this.lastRefresh = now; this.renderTerritory(); - this.context.putImageData(this.imageData, 0, 0); + + const [topLeft, bottomRight] = this.transformHandler.screenBoundingRect(); + const vx0 = Math.max(0, topLeft.x); + const vy0 = Math.max(0, topLeft.y); + const vx1 = Math.min(this.game.width() - 1, bottomRight.x); + const vy1 = Math.min(this.game.height() - 1, bottomRight.y); + + const w = vx1 - vx0 + 1; + const h = vy1 - vy0 + 1; + + if (w > 0 && h > 0) { + this.context.putImageData(this.imageData, 0, 0, vx0, vy0, w, h); + } } if (this.alternativeView) { return; @@ -245,15 +254,10 @@ export class TerritoryLayer implements Layer { } if (!this.game.hasOwner(tile)) { if (this.game.hasFallout(tile)) { - this.paintCell( - this.game.x(tile), - this.game.y(tile), - this.theme.falloutColor(), - 150, - ); + this.paintTile(tile, this.theme.falloutColor(), 150); return; } - this.clearCell(new Cell(this.game.x(tile), this.game.y(tile))); + this.clearTile(tile); return; } const owner = this.game.owner(tile) as PlayerView; @@ -273,40 +277,28 @@ export class TerritoryLayer implements Layer { const lightTile = (x % 2 === 0 && y % 2 === 0) || (y % 2 === 1 && x % 2 === 1); const borderColor = lightTile ? borderColors.light : borderColors.dark; - this.paintCell(x, y, borderColor, 255); + this.paintTile(tile, borderColor, 255); } else { const useBorderColor = playerIsFocused ? this.theme.focusedBorderColor() : this.theme.borderColor(owner); - this.paintCell( - this.game.x(tile), - this.game.y(tile), - useBorderColor, - 255, - ); + this.paintTile(tile, useBorderColor, 255); } } else { - this.paintCell( - this.game.x(tile), - this.game.y(tile), - this.theme.territoryColor(owner), - 150, - ); + this.paintTile(tile, this.theme.territoryColor(owner), 150); } } - paintCell(x: number, y: number, color: Colord, alpha: number) { - const index = y * this.game.width() + x; - const offset = index * 4; + paintTile(tile: TileRef, color: Colord, alpha: number) { + const offset = tile * 4; this.imageData.data[offset] = color.rgba.r; this.imageData.data[offset + 1] = color.rgba.g; this.imageData.data[offset + 2] = color.rgba.b; this.imageData.data[offset + 3] = alpha; } - clearCell(cell: Cell) { - const index = cell.y * this.game.width() + cell.x; - const offset = index * 4; + clearTile(tile: TileRef) { + const offset = tile * 4; this.imageData.data[offset + 3] = 0; // Set alpha to 0 (fully transparent) } @@ -324,13 +316,17 @@ export class TerritoryLayer implements Layer { }); } - paintHighlightCell(cell: Cell, color: Colord, alpha: number) { - this.clearCell(cell); + paintHighlightTile(tile: TileRef, color: Colord, alpha: number) { + this.clearTile(tile); + const x = this.game.x(tile); + const y = this.game.y(tile); this.highlightContext.fillStyle = color.alpha(alpha / 255).toRgbString(); - this.highlightContext.fillRect(cell.x, cell.y, 1, 1); + this.highlightContext.fillRect(x, y, 1, 1); } - clearHighlightCell(cell: Cell) { - this.highlightContext.clearRect(cell.x, cell.y, 1, 1); + clearHighlightTile(tile: TileRef) { + const x = this.game.x(tile); + const y = this.game.y(tile); + this.highlightContext.clearRect(x, y, 1, 1); } }