mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-21 22:57:51 +00:00
Add ocean color override to graphics settings (#4269)
## What Adds a **Terrain** section to the graphics settings modal with a color picker and a hex-code text field (paste a `#rrggbb` code) for the **ocean** (deep water) color. ## Details - The picked color sets the *shallow-water base*; the existing per-depth brightness gradient is preserved (deeper water still darkens). - Only deep water is affected — shoreline water and land are untouched. - Follows the same override pattern as every other graphics setting: the default lives in `render-settings.json` (`terrain.oceanColor`), the override is a field in `GraphicsOverrides`, and `applyGraphicsOverrides` copies it into the live `RenderSettings`. - Rebased on #4271 (settings resolved before renderer construction): the terrain texture **bakes the resolved ocean color at construction**, so a saved override shows on load with no special-casing. Terrain is baked into a GPU texture rather than read per-frame, so a *live* change still triggers an explicit `view.rebuildTerrain()`. - Resetting graphics overrides clears it back to the default ocean color. ## Testing Verified live in a headless singleplayer game: - A **saved** ocean override renders green deep-water on load, baked at construction with no settings-change event fired. - A mid-game color change recolors the deep ocean instantly, gradient preserved, shoreline/land untouched. `tsc` and ESLint clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -70,6 +70,8 @@ const RAIL_THICKNESS_MIN = 0.5;
|
||||
const RAIL_THICKNESS_MAX = 3;
|
||||
const RAIL_THICKNESS_STEP = 0.1;
|
||||
|
||||
const HEX_COLOR_RE = /^#?([0-9a-fA-F]{6})$/;
|
||||
|
||||
export class ShowGraphicsSettingsModalEvent {
|
||||
constructor(
|
||||
public readonly isVisible: boolean = true,
|
||||
@@ -334,6 +336,29 @@ export class GraphicsSettingsModal extends LitElement implements Controller {
|
||||
this.patchStructure({ iconSize: value });
|
||||
}
|
||||
|
||||
private patchTerrain(patch: Partial<GraphicsOverrides["terrain"]>) {
|
||||
const current = this.userSettings.graphicsOverrides();
|
||||
this.userSettings.setGraphicsOverrides({
|
||||
...current,
|
||||
terrain: { ...current.terrain, ...patch },
|
||||
});
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
private currentOceanColor(): string {
|
||||
return (
|
||||
this.userSettings.graphicsOverrides().terrain?.oceanColor ??
|
||||
renderDefaults.terrain.oceanColor
|
||||
);
|
||||
}
|
||||
|
||||
private onOceanColorChange(event: Event) {
|
||||
const value = (event.target as HTMLInputElement).value.trim();
|
||||
const match = HEX_COLOR_RE.exec(value);
|
||||
if (!match) return; // ignore partial/invalid hex while typing
|
||||
this.patchTerrain({ oceanColor: `#${match[1].toLowerCase()}` });
|
||||
}
|
||||
|
||||
private currentClassicIcons(): boolean {
|
||||
return (
|
||||
this.userSettings.graphicsOverrides().structure?.classicIcons ?? true
|
||||
@@ -459,6 +484,7 @@ export class GraphicsSettingsModal extends LitElement implements Controller {
|
||||
const coordinateGridOpacity = this.currentCoordinateGridOpacity();
|
||||
const railDrawDistance = RAIL_ZOOM_MAX - this.currentRailMinZoom();
|
||||
const railThickness = this.currentRailThickness();
|
||||
const oceanColor = this.currentOceanColor();
|
||||
const colorblind = this.currentColorblind();
|
||||
|
||||
return html`
|
||||
@@ -919,6 +945,39 @@ export class GraphicsSettingsModal extends LitElement implements Controller {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="px-3 py-1 text-xs font-semibold text-slate-400 uppercase tracking-wider mt-2"
|
||||
>
|
||||
${translateText("graphics_setting.section_terrain")}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
|
||||
>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium">
|
||||
${translateText("graphics_setting.ocean_color_label")}
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${translateText("graphics_setting.ocean_color_desc")}
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
.value=${oceanColor}
|
||||
placeholder=${renderDefaults.terrain.oceanColor}
|
||||
spellcheck="false"
|
||||
@change=${this.onOceanColorChange}
|
||||
class="w-24 px-2 py-1 bg-slate-900 border border-slate-500 rounded-sm text-sm text-white font-mono"
|
||||
/>
|
||||
<input
|
||||
type="color"
|
||||
.value=${oceanColor}
|
||||
@input=${this.onOceanColorChange}
|
||||
class="w-10 h-8 bg-transparent border border-slate-500 rounded-sm cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="px-3 py-1 text-xs font-semibold text-slate-400 uppercase tracking-wider mt-2"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user