From 6ca06a6f6ff58dbf37a8927193575be89bb67dc0 Mon Sep 17 00:00:00 2001
From: TKTK123456 <103334266+TKTK123456@users.noreply.github.com>
Date: Mon, 1 Jun 2026 20:39:52 -0400
Subject: [PATCH] Sam/factory radius ghost upgrade fix (#4104)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
> **Before opening a PR:** discuss new features on
[Discord](https://discord.gg/K9zernJB5z) first, and file bugs or small
improvements as
[issues](https://github.com/openfrontio/OpenFrontIO/issues/new/choose).
You must be assigned to an `approved` issue — unsolicited PRs will be
auto-closed.
**Add approved & assigned issue number here:**
Resolves #4059
## Description:
Makes ghost radius centred on building being upgrading (if upgrading
building)
## Please complete the following:
- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
## Please put your Discord username so you can be contacted if a bug or
regression is found:
tktk123456
---
src/client/controllers/BuildPreviewController.ts | 12 ++++++++++++
src/client/render/gl/passes/RangeCirclePass.ts | 4 ++--
src/client/render/types/Renderer.ts | 2 ++
3 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/src/client/controllers/BuildPreviewController.ts b/src/client/controllers/BuildPreviewController.ts
index 3a9de6cdb..26a55b1b3 100644
--- a/src/client/controllers/BuildPreviewController.ts
+++ b/src/client/controllers/BuildPreviewController.ts
@@ -335,12 +335,24 @@ export class BuildPreviewController implements Controller {
rangeRadius = this.game.config().defensePostRange();
break;
}
+ let radiusTileX = this.game.x(tileRef);
+ let radiusTileY = this.game.y(tileRef);
+ if (
+ rangeRadius > 0 &&
+ u.canUpgrade !== false &&
+ upgradeTargetTile !== null
+ ) {
+ radiusTileX = this.game.x(upgradeTargetTile);
+ radiusTileY = this.game.y(upgradeTargetTile);
+ }
const cost = u.cost;
return {
ghostType: u.type,
tileX: this.game.x(tileRef),
tileY: this.game.y(tileRef),
+ radiusTileX,
+ radiusTileY,
canBuild: u.canBuild !== false,
canUpgrade: u.canUpgrade !== false,
cost: Number(cost),
diff --git a/src/client/render/gl/passes/RangeCirclePass.ts b/src/client/render/gl/passes/RangeCirclePass.ts
index 8fbe41273..05351f95f 100644
--- a/src/client/render/gl/passes/RangeCirclePass.ts
+++ b/src/client/render/gl/passes/RangeCirclePass.ts
@@ -54,8 +54,8 @@ export class RangeCirclePass {
updateGhostPreview(data: GhostPreviewData | null): void {
if (data && data.rangeRadius > 0) {
- this.centerX = data.tileX;
- this.centerY = data.tileY;
+ this.centerX = data.radiusTileX;
+ this.centerY = data.radiusTileY;
this.radius = data.rangeRadius;
this.warning = data.rangeWarning;
} else {
diff --git a/src/client/render/types/Renderer.ts b/src/client/render/types/Renderer.ts
index 652a5e338..355013ced 100644
--- a/src/client/render/types/Renderer.ts
+++ b/src/client/render/types/Renderer.ts
@@ -152,6 +152,8 @@ export interface GhostPreviewData {
ghostType: string; // UnitType string ("City", "Port", etc.)
tileX: number; // Hover tile X
tileY: number; // Hover tile Y
+ radiusTileX: number;
+ radiusTileY: number;
canBuild: boolean; // Valid placement?
canUpgrade: boolean; // Upgrading existing structure?
cost: number; // Gold cost