diff --git a/src/client/render/gl/RenderSettings.ts b/src/client/render/gl/RenderSettings.ts index 4d2af1bed..9c6ef0353 100644 --- a/src/client/render/gl/RenderSettings.ts +++ b/src/client/render/gl/RenderSettings.ts @@ -229,6 +229,10 @@ export interface RenderSettings { minScreenScale: number; // minimum world-scale when zoomed out (prevents vanishing) cullZoom: number; // popups hidden below this zoom level }; + ghostCost: { + screenScale: number; // screen-relative em scale; divided by zoom each frame for fixed on-screen size + screenYOffset: number; // screen-relative downward offset from icon center; divided by zoom each frame for fixed on-screen gap + }; spawnOverlay: { highlightRadius: number; // tile highlight radius (squared internally) highlightAlpha: number; // tile highlight opacity (0–1) diff --git a/src/client/render/gl/passes/WorldTextPass.ts b/src/client/render/gl/passes/WorldTextPass.ts index 5feef24b2..4ffd13d19 100644 --- a/src/client/render/gl/passes/WorldTextPass.ts +++ b/src/client/render/gl/passes/WorldTextPass.ts @@ -38,10 +38,6 @@ const CONQUEST_Y_OFFSET = 8; /** World-space font size for conquest popups. */ const CONQUEST_SCALE = 6; const CONQUEST_OUTLINE_WIDTH = 2.0; -/** Tiles below the ghost icon center for the cost label. */ -const GHOST_COST_Y_OFFSET = 3; -/** World-space font size — smaller than popups so it sits unobtrusively under the icon. */ -const GHOST_COST_SCALE = 4; /** Matches player-name outline width for a consistent UI look. */ const GHOST_COST_OUTLINE_WIDTH = 1.4; /** @@ -351,9 +347,10 @@ export class WorldTextPass { } // The vertex shader adds +0.5 to (x, y) for tile-center alignment, so we // pass raw tile coords here — same convention as the other popup entries. + // Y offset is applied in rebuildInstances (zoom-relative). this.ghostCostLabel = { x: label.tileX, - y: label.tileY + GHOST_COST_Y_OFFSET, + y: label.tileY, text: renderNumber(label.cost), colorR: r, colorG: g, @@ -474,8 +471,12 @@ export class WorldTextPass { // Ghost cost label — persistent, no fade or rise. layoutString already // centers cursors around 0, so passing the tile coord places the text // centered on the tile (vertex shader adds the +0.5 tile-center offset). + // Scale is divided by zoom so the chip keeps a constant on-screen size. const label = this.ghostCostLabel; if (label) { + const invZoom = 1 / Math.max(zoom, 0.0001); + const ghostScale = this.settings.ghostCost.screenScale * invZoom; + const ghostY = label.y + this.settings.ghostCost.screenYOffset * invZoom; layoutString( label.text, this.glyph, @@ -490,14 +491,14 @@ export class WorldTextPass { const off = count * FLOATS_PER_INSTANCE; this.instanceData[off + 0] = label.x; - this.instanceData[off + 1] = label.y; + this.instanceData[off + 1] = ghostY; this.instanceData[off + 2] = this.cursors[i]; this.instanceData[off + 3] = this.charCodes[i]; this.instanceData[off + 4] = 1; this.instanceData[off + 5] = label.colorR; this.instanceData[off + 6] = label.colorG; this.instanceData[off + 7] = label.colorB; - this.instanceData[off + 8] = GHOST_COST_SCALE; + this.instanceData[off + 8] = ghostScale; this.instanceData[off + 9] = GHOST_COST_OUTLINE_WIDTH; count++; } diff --git a/src/client/render/gl/render-settings.json b/src/client/render/gl/render-settings.json index cbf3d7273..d532d0f4e 100644 --- a/src/client/render/gl/render-settings.json +++ b/src/client/render/gl/render-settings.json @@ -244,6 +244,10 @@ "minScreenScale": 0.15, "cullZoom": 0.3 }, + "ghostCost": { + "screenScale": 30, + "screenYOffset": 50 + }, "spawnOverlay": { "highlightRadius": 9, "highlightAlpha": 1.0,