diff --git a/src/client/graphics/TransformHandler.ts b/src/client/graphics/TransformHandler.ts index 6cf43cd77..6b6159694 100644 --- a/src/client/graphics/TransformHandler.ts +++ b/src/client/graphics/TransformHandler.ts @@ -70,7 +70,7 @@ export class TransformHandler { ); } - worldToScreenCoordinates(cell: Cell): { x: number; y: number } { + worldToCanvasCoordinates(cell: Cell): { x: number; y: number } { // Step 1: Convert from Cell coordinates to game coordinates // (reverse of Math.floor operation - we'll use the exact values) const gameX = cell.x; @@ -90,23 +90,23 @@ export class TransformHandler { const canvasY = (centerY - this.offsetY) * this.scale + this.game.height() / 2; - // Step 4: Convert canvas coordinates back to screen coordinates - const canvasRect = this.boundingRect(); - const screenX = canvasX + canvasRect.left; - const screenY = canvasY + canvasRect.top; - return { x: screenX, y: screenY }; + return { x: canvasX, y: canvasY }; + } + + worldToScreenCoordinates(cell: Cell): { x: number; y: number } { + // Step 1-3: Convert world coordinates to canvas coordinates in worldToCanvasCoordinates + // Step 4 only where needed: Convert canvas coordinates back to screen coordinates + const canvasCoords = this.worldToCanvasCoordinates(cell); + return this.canvasToScreenCoordinates(canvasCoords.x, canvasCoords.y); } screenToWorldCoordinates(screenX: number, screenY: number): Cell { - const canvasRect = this.boundingRect(); - const canvasX = screenX - canvasRect.left; - const canvasY = screenY - canvasRect.top; + const canvasCoords = this.screenToCanvasCoordinates(screenX, screenY); - // Calculate the world point we want to zoom towards const centerX = - (canvasX - this.game.width() / 2) / this.scale + this.offsetX; + (canvasCoords.x - this.game.width() / 2) / this.scale + this.offsetX; const centerY = - (canvasY - this.game.height() / 2) / this.scale + this.offsetY; + (canvasCoords.y - this.game.height() / 2) / this.scale + this.offsetY; const gameX = centerX + this.game.width() / 2; const gameY = centerY + this.game.height() / 2; @@ -114,6 +114,25 @@ export class TransformHandler { return new Cell(Math.floor(gameX), Math.floor(gameY)); } + canvasToScreenCoordinates( + canvasX: number, + canvasY: number, + ): { x: number; y: number } { + const canvasRect = this.boundingRect(); + return { + x: canvasX + canvasRect.left, + y: canvasY + canvasRect.top, + }; + } + + screenToCanvasCoordinates( + screenX: number, + screenY: number, + ): { x: number; y: number } { + const canvasRect = this.boundingRect(); + return { x: screenX - canvasRect.left, y: screenY - canvasRect.top }; + } + screenBoundingRect(): [Cell, Cell] { const canvasRect = this.boundingRect(); const canvasWidth = canvasRect.width; @@ -235,19 +254,19 @@ export class TransformHandler { // Clamp the scale to prevent extreme zooming this.scale = Math.max(0.2, Math.min(20, this.scale)); - const canvasRect = this.boundingRect(); - const canvasX = event.x - canvasRect.left; - const canvasY = event.y - canvasRect.top; + const canvasCoords = this.screenToCanvasCoordinates(event.x, event.y); // Calculate the world point we want to zoom towards const zoomPointX = - (canvasX - this.game.width() / 2) / oldScale + this.offsetX; + (canvasCoords.x - this.game.width() / 2) / oldScale + this.offsetX; const zoomPointY = - (canvasY - this.game.height() / 2) / oldScale + this.offsetY; + (canvasCoords.y - this.game.height() / 2) / oldScale + this.offsetY; // Adjust the offset - this.offsetX = zoomPointX - (canvasX - this.game.width() / 2) / this.scale; - this.offsetY = zoomPointY - (canvasY - this.game.height() / 2) / this.scale; + this.offsetX = + zoomPointX - (canvasCoords.x - this.game.width() / 2) / this.scale; + this.offsetY = + zoomPointY - (canvasCoords.y - this.game.height() / 2) / this.scale; this.clampOffsets(); this.changed = true; } diff --git a/src/client/graphics/layers/BuildMenu.ts b/src/client/graphics/layers/BuildMenu.ts index 797b4e568..ad0c9efee 100644 --- a/src/client/graphics/layers/BuildMenu.ts +++ b/src/client/graphics/layers/BuildMenu.ts @@ -147,9 +147,6 @@ export class BuildMenu extends LitElement implements Layer { e.x, e.y, ); - if (clickedCell === null) { - return; - } if (!this.game.isValidCoord(clickedCell.x, clickedCell.y)) { return; } diff --git a/src/client/graphics/layers/CoordinateGridLayer.ts b/src/client/graphics/layers/CoordinateGridLayer.ts index afa15fcdb..885f76f82 100644 --- a/src/client/graphics/layers/CoordinateGridLayer.ts +++ b/src/client/graphics/layers/CoordinateGridLayer.ts @@ -147,10 +147,10 @@ export class CoordinateGridLayer implements Layer { canvasWidth: number, canvasHeight: number, ): string { - const topLeft = this.transformHandler.worldToScreenCoordinates( + const topLeft = this.transformHandler.worldToCanvasCoordinates( new Cell(0, 0), ); - const bottomRight = this.transformHandler.worldToScreenCoordinates( + const bottomRight = this.transformHandler.worldToCanvasCoordinates( new Cell(width, height), ); const darkMode = this.game.config().userSettings()?.darkMode() ?? false; @@ -191,16 +191,16 @@ export class CoordinateGridLayer implements Layer { const canvasWidth = context.canvas.width; const canvasHeight = context.canvas.height; - const mapTopScreenRaw = this.transformHandler.worldToScreenCoordinates( + const mapTopScreenRaw = this.transformHandler.worldToCanvasCoordinates( new Cell(0, 0), ).y; - const mapBottomScreenRaw = this.transformHandler.worldToScreenCoordinates( + const mapBottomScreenRaw = this.transformHandler.worldToCanvasCoordinates( new Cell(0, height), ).y; - const mapLeftScreenRaw = this.transformHandler.worldToScreenCoordinates( + const mapLeftScreenRaw = this.transformHandler.worldToCanvasCoordinates( new Cell(0, 0), ).x; - const mapRightScreenRaw = this.transformHandler.worldToScreenCoordinates( + const mapRightScreenRaw = this.transformHandler.worldToCanvasCoordinates( new Cell(width, 0), ).x; @@ -216,11 +216,11 @@ export class CoordinateGridLayer implements Layer { for (let col = 0; col <= fullCols; col++) { const worldX = col * cellWidth + mapLeftWorld; - const screenX = this.transformHandler.worldToScreenCoordinates( + const screenX = this.transformHandler.worldToCanvasCoordinates( new Cell(worldX, mapTopWorld), ).x; if (screenX < -1 || screenX > canvasWidth + 1) continue; - const screenBottom = this.transformHandler.worldToScreenCoordinates( + const screenBottom = this.transformHandler.worldToCanvasCoordinates( new Cell(worldX, gridHeight), ).y; context.moveTo(screenX, mapTopScreen); @@ -228,13 +228,13 @@ export class CoordinateGridLayer implements Layer { } // Final vertical line at map right edge only if grid fits perfectly if (!hasExtraCol) { - const mapRightLine = this.transformHandler.worldToScreenCoordinates( + const mapRightLine = this.transformHandler.worldToCanvasCoordinates( new Cell(gridWidth, mapTopWorld), ).x; context.moveTo(mapRightLine, mapTopScreen); context.lineTo( mapRightLine, - this.transformHandler.worldToScreenCoordinates( + this.transformHandler.worldToCanvasCoordinates( new Cell(gridWidth, gridHeight), ).y, ); @@ -242,11 +242,11 @@ export class CoordinateGridLayer implements Layer { for (let row = 0; row <= fullRows; row++) { const worldY = row * cellHeight + mapTopWorld; - const screenY = this.transformHandler.worldToScreenCoordinates( + const screenY = this.transformHandler.worldToCanvasCoordinates( new Cell(mapLeftWorld, worldY), ).y; if (screenY < -1 || screenY > canvasHeight + 1) continue; - const screenRight = this.transformHandler.worldToScreenCoordinates( + const screenRight = this.transformHandler.worldToCanvasCoordinates( new Cell(gridWidth, worldY), ).x; context.moveTo(mapLeftScreen, screenY); @@ -254,12 +254,12 @@ export class CoordinateGridLayer implements Layer { } // Final horizontal line at map bottom edge only if grid fits perfectly if (!hasExtraRow) { - const mapBottomLine = this.transformHandler.worldToScreenCoordinates( + const mapBottomLine = this.transformHandler.worldToCanvasCoordinates( new Cell(mapLeftWorld, gridHeight), ).y; context.moveTo(mapLeftScreen, mapBottomLine); context.lineTo( - this.transformHandler.worldToScreenCoordinates( + this.transformHandler.worldToCanvasCoordinates( new Cell(gridWidth, gridHeight), ).x, mapBottomLine, @@ -291,7 +291,7 @@ export class CoordinateGridLayer implements Layer { const startY = row * cellHeight; const rowHeight = row < fullRows ? cellHeight : lastRowHeight; const centerY = startY + rowHeight / 2; - const screenY = this.transformHandler.worldToScreenCoordinates( + const screenY = this.transformHandler.worldToCanvasCoordinates( new Cell(0, centerY), ).y; if (screenY < -LABEL_PADDING || screenY > canvasHeight + LABEL_PADDING) @@ -301,14 +301,14 @@ export class CoordinateGridLayer implements Layer { const startX = col * cellWidth; const colWidth = col < fullCols ? cellWidth : lastColWidth; const centerX = startX + colWidth / 2; - const screenX = this.transformHandler.worldToScreenCoordinates( + const screenX = this.transformHandler.worldToCanvasCoordinates( new Cell(centerX, centerY), ).x; if (screenX < -LABEL_PADDING || screenX > canvasWidth + LABEL_PADDING) continue; // Position at cell top-left in screen space - const cellTopLeft = this.transformHandler.worldToScreenCoordinates( + const cellTopLeft = this.transformHandler.worldToCanvasCoordinates( new Cell(startX, startY), ); drawLabel( diff --git a/src/client/graphics/layers/NukeTrajectoryPreviewLayer.ts b/src/client/graphics/layers/NukeTrajectoryPreviewLayer.ts index 02932ccfb..fed84489e 100644 --- a/src/client/graphics/layers/NukeTrajectoryPreviewLayer.ts +++ b/src/client/graphics/layers/NukeTrajectoryPreviewLayer.ts @@ -106,18 +106,9 @@ export class NukeTrajectoryPreviewLayer implements Layer { } // Convert mouse position to world coordinates - const rect = this.transformHandler.boundingRect(); - if (!rect) { - this.trajectoryPoints = []; - this.cachedSpawnTile = null; - return; - } - - const localX = this.mousePos.x - rect.left; - const localY = this.mousePos.y - rect.top; const worldCoords = this.transformHandler.screenToWorldCoordinates( - localX, - localY, + this.mousePos.x, + this.mousePos.y, ); if (!this.game.isValidCoord(worldCoords.x, worldCoords.y)) { @@ -192,17 +183,9 @@ export class NukeTrajectoryPreviewLayer implements Layer { } // Convert mouse position to world coordinates - const rect = this.transformHandler.boundingRect(); - if (!rect) { - this.trajectoryPoints = []; - return; - } - - const localX = this.mousePos.x - rect.left; - const localY = this.mousePos.y - rect.top; const worldCoords = this.transformHandler.screenToWorldCoordinates( - localX, - localY, + this.mousePos.x, + this.mousePos.y, ); if (!this.game.isValidCoord(worldCoords.x, worldCoords.y)) { diff --git a/src/client/graphics/layers/StructureDrawingUtils.ts b/src/client/graphics/layers/StructureDrawingUtils.ts index 52a1b4951..9c3eb6ce5 100644 --- a/src/client/graphics/layers/StructureDrawingUtils.ts +++ b/src/client/graphics/layers/StructureDrawingUtils.ts @@ -184,7 +184,7 @@ export class SpriteFactory { const parentContainer = new PIXI.Container(); const tile = unit.tile(); const worldPos = new Cell(this.game.x(tile), this.game.y(tile)); - const screenPos = this.transformHandler.worldToScreenCoordinates(worldPos); + const screenPos = this.transformHandler.worldToCanvasCoordinates(worldPos); const isMarkedForDeletion = unit.markedForDeletion() !== false; const isConstruction = unit.isUnderConstruction(); diff --git a/src/client/graphics/layers/StructureIconsLayer.ts b/src/client/graphics/layers/StructureIconsLayer.ts index 3430fae28..20281eab8 100644 --- a/src/client/graphics/layers/StructureIconsLayer.ts +++ b/src/client/graphics/layers/StructureIconsLayer.ts @@ -265,14 +265,12 @@ export class StructureIconsLayer implements Layer { if (now - this.lastGhostQueryAt < 50) { return; } - const rect = this.transformHandler.boundingRect(); - if (!rect) return; - - const localX = this.mousePos.x - rect.left; - const localY = this.mousePos.y - rect.top; this.lastGhostQueryAt = now; let tileRef: TileRef | undefined; - const tile = this.transformHandler.screenToWorldCoordinates(localX, localY); + const tile = this.transformHandler.screenToWorldCoordinates( + this.mousePos.x, + this.mousePos.y, + ); if (this.game.isValidCoord(tile.x, tile.y)) { tileRef = this.game.ref(tile.x, tile.y); } @@ -457,11 +455,7 @@ export class StructureIconsLayer implements Layer { this.removeGhostStructure(); return; } - const rect = this.transformHandler.boundingRect(); - if (!rect) return; - const x = e.x - rect.left; - const y = e.y - rect.top; - const tile = this.transformHandler.screenToWorldCoordinates(x, y); + const tile = this.transformHandler.screenToWorldCoordinates(e.x, e.y); if (this.ghostUnit.buildableUnit.canUpgrade !== false) { this.eventBus.emit( new SendUpgradeStructureIntentEvent( @@ -496,13 +490,9 @@ export class StructureIconsLayer implements Layer { this.mousePos.y = e.y; if (!this.ghostUnit) return; - const rect = this.transformHandler.boundingRect(); - if (!rect) return; - - const localX = e.x - rect.left; - const localY = e.y - rect.top; - this.ghostUnit.container.position.set(localX, localY); - this.ghostUnit.range?.position.set(localX, localY); + const local = this.transformHandler.screenToCanvasCoordinates(e.x, e.y); + this.ghostUnit.container.position.set(local.x, local.y); + this.ghostUnit.range?.position.set(local.x, local.y); } private createGhostStructure(type: PlayerBuildableUnitType | null) { @@ -511,13 +501,14 @@ export class StructureIconsLayer implements Layer { if (type === null) { return; } - const rect = this.transformHandler.boundingRect(); - const localX = this.mousePos.x - rect.left; - const localY = this.mousePos.y - rect.top; + const local = this.transformHandler.screenToCanvasCoordinates( + this.mousePos.x, + this.mousePos.y, + ); const ghost = this.factory.createGhostContainer( player, this.ghostStage, - { x: localX, y: localY }, + { x: local.x, y: local.y }, type, ); this.ghostUnit = { @@ -749,7 +740,7 @@ export class StructureIconsLayer implements Layer { private computeNewLocation(render: StructureRenderInfo) { const tile = render.unit.tile(); const worldPos = new Cell(this.game.x(tile), this.game.y(tile)); - const screenPos = this.transformHandler.worldToScreenCoordinates(worldPos); + const screenPos = this.transformHandler.worldToCanvasCoordinates(worldPos); screenPos.x = Math.round(screenPos.x); const scale = this.transformHandler.scale; diff --git a/src/client/graphics/ui/MoveIndicatorUI.ts b/src/client/graphics/ui/MoveIndicatorUI.ts index 21f012546..8da36b7d4 100644 --- a/src/client/graphics/ui/MoveIndicatorUI.ts +++ b/src/client/graphics/ui/MoveIndicatorUI.ts @@ -35,7 +35,7 @@ export class MoveIndicatorUI implements UIElement { const chevronSize = this.chevronSize * scale; // Get screen coordinates - const screenPos = this.transformHandler.worldToScreenCoordinates(this.cell); + const screenPos = this.transformHandler.worldToCanvasCoordinates(this.cell); const centerX = screenPos.x; const centerY = screenPos.y; diff --git a/src/client/graphics/ui/NavalTarget.ts b/src/client/graphics/ui/NavalTarget.ts index 4def4c20e..50a2d39a9 100644 --- a/src/client/graphics/ui/NavalTarget.ts +++ b/src/client/graphics/ui/NavalTarget.ts @@ -48,7 +48,7 @@ export class Target implements UIElement { } const alpha = Math.max(0, Math.min(1, BASE_ALPHA * t)); - const screenPos = this.transformHandler.worldToScreenCoordinates(this.cell); + const screenPos = this.transformHandler.worldToCanvasCoordinates(this.cell); screenPos.x = Math.round(screenPos.x); screenPos.y = Math.round(screenPos.y); const transformScale = this.transformHandler.scale; diff --git a/src/client/graphics/ui/NukeTelegraph.ts b/src/client/graphics/ui/NukeTelegraph.ts index 562f35907..1d54a8567 100644 --- a/src/client/graphics/ui/NukeTelegraph.ts +++ b/src/client/graphics/ui/NukeTelegraph.ts @@ -46,7 +46,7 @@ export class CircleArea implements UIElement { const innerDiameter = (this.innerDiameter / 2) * (1 - t) + this.innerDiameter * t; - const screenPos = this.transformHandler.worldToScreenCoordinates(this.cell); + const screenPos = this.transformHandler.worldToCanvasCoordinates(this.cell); screenPos.x = Math.round(screenPos.x); screenPos.y = Math.round(screenPos.y); diff --git a/src/client/graphics/ui/TextIndicator.ts b/src/client/graphics/ui/TextIndicator.ts index beee16a25..1c49006a1 100644 --- a/src/client/graphics/ui/TextIndicator.ts +++ b/src/client/graphics/ui/TextIndicator.ts @@ -37,7 +37,7 @@ export class TextIndicator implements UIElement { return true; } - const screenPos = this.transformHandler.worldToScreenCoordinates(this.cell); + const screenPos = this.transformHandler.worldToCanvasCoordinates(this.cell); screenPos.x = Math.round(screenPos.x); screenPos.y = Math.round(screenPos.y);