From 52bcae5106f813c9a462b9863951b6f0fb6893d5 Mon Sep 17 00:00:00 2001 From: Evan Date: Sun, 14 Jun 2026 12:42:19 -0700 Subject: [PATCH] Replace dark mode with player-adjustable lighting (#4280) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What Removes the binary **dark mode** feature and replaces it with a player-adjustable **Lighting** section in graphics settings. ### In-game settings - Removed the Dark Mode toggle from both `SettingsModal` and `UserSettingModal`, and `darkMode()`/`toggleDarkMode()`/`DARK_MODE_KEY` from `UserSettings`. ### New Lighting section (Graphics Settings) - **Ambient light** slider (1–3): mapped to the renderer's ambient as `ambient = 1 / level`. **1.0 = no effect (unchanged look), 3.0 = darkest with the strongest structure glow.** - **Light falloff** slider (1–3): writes straight to `lighting.falloffPower`. - Lighting auto-enables only when ambient < 1, so the default (slider at 1) has zero GPU cost — off by default. ### Removed dark-mode overrides - Deleted `applyDarkModeOverride()` + `DARK_AMBIENT` and their wiring in `ClientGameRunner`, `gl/index.ts`, and the `DARK_MODE_KEY` listener. - Removed the `.dark` HUD-class toggle in `Main.ts` and the `userSettings.darkMode()` read in `PlayerIcons`. ### Train glow - `UT_TRAIN` light reduced (intensity `2.0 → 0.5`, radius `8 → 6`) so structures dominate the glow. ## Notes - Removing the dark-mode setting also retires the HUD's Tailwind dark theme (same setting). The dormant `dark:` CSS variants and unused white-icon assets are left in place (out of scope). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 --- resources/lang/en.json | 7 +- src/client/ClientGameRunner.ts | 8 -- src/client/Main.ts | 29 +--- src/client/UserSettingModal.ts | 15 -- src/client/hud/PlayerIcons.ts | 2 +- .../hud/layers/GraphicsSettingsModal.ts | 134 ++++++++++++++++++ src/client/hud/layers/SettingsModal.ts | 33 ----- src/client/render/gl/GraphicsOverrides.ts | 9 ++ src/client/render/gl/RenderOverrides.ts | 22 ++- src/client/render/gl/index.ts | 5 +- src/client/render/gl/passes/PointLightPass.ts | 5 +- src/client/render/gl/render-settings.json | 4 +- src/core/game/UserSettings.ts | 9 -- tests/GraphicsOverrides.test.ts | 72 ++++++++++ 14 files changed, 239 insertions(+), 115 deletions(-) diff --git a/resources/lang/en.json b/resources/lang/en.json index e387fc5e5..00f699951 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -537,6 +537,10 @@ "hover_glow_width_label": "Hover glow size", "icon_size_desc": "How large structure icons are drawn on the map", "icon_size_label": "Structure icon size", + "lighting_ambient_desc": "Darkens the map and adds a glow around structures (0 = off)", + "lighting_ambient_label": "Ambient light", + "lighting_unit_glow_desc": "How far the glow spreads around units and structures", + "lighting_unit_glow_label": "Unit glow", "name_cull_desc": "Hide names smaller than this size", "name_cull_label": "Minimum name size", "name_scale_label": "Name Scale", @@ -550,6 +554,7 @@ "reset_label": "Reset to defaults", "section_accessibility": "Accessibility", "section_effects": "Effects", + "section_lighting": "Lighting", "section_map": "Map", "section_name_labels": "Name Labels", "section_structure_icons": "Structure Icons", @@ -1345,8 +1350,6 @@ "coordinate_grid_label": "Coordinate Grid", "cursor_cost_label_desc": "Show a cost pill under the build cursor icon", "cursor_cost_label_label": "Cursor Build Cost", - "dark_mode_desc": "Toggle the site’s appearance between light and dark themes", - "dark_mode_label": "Dark Mode", "development_only": "Development Only", "easter_bug_count_desc": "How many bugs you're okay with (0–1000, emotionally)", "easter_bug_count_label": "Bug Count", diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index 0e27bcc01..8c78d8b70 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -29,7 +29,6 @@ import { } from "../core/game/GameUpdates"; import { loadTerrainMap, TerrainMapData } from "../core/game/TerrainMapLoader"; import { - DARK_MODE_KEY, GRAPHICS_KEY, USER_SETTINGS_CHANGED_EVENT, UserSettings, @@ -68,7 +67,6 @@ import { createCanvas } from "./Utils"; import { WebGLFrameBuilder } from "./WebGLFrameBuilder"; import { createRenderer, GameRenderer } from "./hud/GameRenderer"; import { - applyDarkModeOverride, applyGraphicsOverrides, createRenderSettings, deepAssign, @@ -495,7 +493,6 @@ async function createClientGame( const resolveRenderSettings = (): RenderSettings => { const settings = createRenderSettings(); applyGraphicsOverrides(settings, userSettings.graphicsOverrides()); - applyDarkModeOverride(settings, userSettings.darkMode()); return settings; }; @@ -538,11 +535,6 @@ async function createClientGame( onGraphicsChanged, { signal: graphicsListenerAbort.signal }, ); - globalThis.addEventListener( - `${USER_SETTINGS_CHANGED_EVENT}:${DARK_MODE_KEY}`, - regenerateRenderSettings, - { signal: graphicsListenerAbort.signal }, - ); // Loaded on demand so lil-gui and the debug GUI stay out of the main bundle. let debugGui: { open(): void; destroy(): void } | null = null; diff --git a/src/client/Main.ts b/src/client/Main.ts index 08c22f192..ebfef8a41 100644 --- a/src/client/Main.ts +++ b/src/client/Main.ts @@ -12,11 +12,7 @@ import { } from "../core/Schemas"; import { GameEnv } from "../core/configuration/Config"; import { GameType } from "../core/game/Game"; -import { - DARK_MODE_KEY, - USER_SETTINGS_CHANGED_EVENT, - UserSettings, -} from "../core/game/UserSettings"; +import { UserSettings } from "../core/game/UserSettings"; import "./AccountModal"; import { getUserMe, invalidateUserMe } from "./Api"; import { userAuth } from "./Auth"; @@ -226,11 +222,6 @@ declare global { "leave-lobby": CustomEvent; "update-game-config": CustomEvent; } - - // Fixes the globalThis.addEventListener errors - interface WindowEventMap { - "event:user-settings-changed:settings.darkMode": CustomEvent; - } } export interface JoinLobbyEvent { @@ -547,24 +538,6 @@ class Client { this.joinModal.eventBus = this.eventBus; } - const applyDarkMode = (isDark: boolean) => { - if (isDark) { - document.documentElement.classList.add("dark"); - } else { - document.documentElement.classList.remove("dark"); - } - }; - - applyDarkMode(this.userSettings.darkMode()); - - globalThis.addEventListener( - `${USER_SETTINGS_CHANGED_EVENT}:${DARK_MODE_KEY}`, - (e: CustomEvent) => { - const isDark = e.detail === "true"; - applyDarkMode(isDark); - }, - ); - // Attempt to join lobby if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", () => this.handleUrl()); diff --git a/src/client/UserSettingModal.ts b/src/client/UserSettingModal.ts index 13f863bf1..5fb3130f8 100644 --- a/src/client/UserSettingModal.ts +++ b/src/client/UserSettingModal.ts @@ -200,12 +200,6 @@ export class UserSettingModal extends BaseModal { }, 5000); } - toggleDarkMode() { - this.userSettings.toggleDarkMode(); - - console.log("🌙 Dark Mode:", this.userSettings.darkMode() ? "ON" : "OFF"); - } - /** Whether colorblind mode is currently enabled in the graphics overrides. */ private colorblindMode(): boolean { return ( @@ -752,15 +746,6 @@ export class UserSettingModal extends BaseModal { private renderBasicSettings() { return html` - - - ) { + const current = this.userSettings.graphicsOverrides(); + this.userSettings.setGraphicsOverrides({ + ...current, + lighting: { ...current.lighting, ...patch }, + }); + this.requestUpdate(); + } + + private currentAmbientLevel(): number { + const ambient = + this.userSettings.graphicsOverrides().lighting?.ambient ?? + renderDefaults.lighting.ambient; + return ambientValueToSlider(ambient); + } + + private onAmbientLevelChange(event: Event) { + const level = parseFloat((event.target as HTMLInputElement).value); + this.patchLighting({ ambient: ambientSliderToValue(level) }); + } + + private currentUnitGlow(): number { + const falloff = + this.userSettings.graphicsOverrides().lighting?.falloffPower ?? + renderDefaults.lighting.falloffPower; + return falloffToUnitGlowSlider(falloff); + } + + private onUnitGlowChange(event: Event) { + const level = parseFloat((event.target as HTMLInputElement).value); + this.patchLighting({ falloffPower: unitGlowSliderToFalloff(level) }); + } + private currentClassicIcons(): boolean { return ( this.userSettings.graphicsOverrides().structure?.classicIcons ?? true @@ -485,6 +561,8 @@ export class GraphicsSettingsModal extends LitElement implements Controller { const railDrawDistance = RAIL_ZOOM_MAX - this.currentRailMinZoom(); const railThickness = this.currentRailThickness(); const oceanColor = this.currentOceanColor(); + const ambientLevel = this.currentAmbientLevel(); + const unitGlow = this.currentUnitGlow(); const colorblind = this.currentColorblind(); return html` @@ -521,6 +599,62 @@ export class GraphicsSettingsModal extends LitElement implements Controller {
+ ${translateText("graphics_setting.section_lighting")} +
+ +
+
+
+ ${translateText("graphics_setting.lighting_ambient_label")} +
+
+ ${translateText("graphics_setting.lighting_ambient_desc")} +
+ +
+
+ ${ambientLevel} +
+
+ +
+
+
+ ${translateText("graphics_setting.lighting_unit_glow_label")} +
+
+ ${translateText("graphics_setting.lighting_unit_glow_desc")} +
+ +
+
+ ${unitGlow} +
+
+ +
${translateText("graphics_setting.section_name_labels")}
diff --git a/src/client/hud/layers/SettingsModal.ts b/src/client/hud/layers/SettingsModal.ts index 4186a2f55..f1095cabe 100644 --- a/src/client/hud/layers/SettingsModal.ts +++ b/src/client/hud/layers/SettingsModal.ts @@ -8,7 +8,6 @@ import { UserSettings } from "../../../core/game/UserSettings"; import { Controller } from "../../Controller"; import { AlternateViewEvent, - RefreshGraphicsEvent, ToggleRenderDebugGuiEvent, } from "../../InputHandler"; import { translateText } from "../../Utils"; @@ -18,7 +17,6 @@ import { } from "../../sound/Sounds"; import { ShowGraphicsSettingsModalEvent } from "./GraphicsSettingsModal"; const cursorPriceIcon = assetUrl("images/CursorPriceIconWhite.svg"); -const darkModeIcon = assetUrl("images/DarkModeIconWhite.svg"); const emojiIcon = assetUrl("images/EmojiIconWhite.svg"); const exitIcon = assetUrl("images/ExitIconWhite.svg"); const mouseIcon = assetUrl("images/MouseIconWhite.svg"); @@ -141,12 +139,6 @@ export class SettingsModal extends LitElement implements Controller { this.requestUpdate(); } - private onToggleDarkModeButtonClick() { - this.userSettings.toggleDarkMode(); - this.eventBus.emit(new RefreshGraphicsEvent()); - this.requestUpdate(); - } - private onToggleRandomNameModeButtonClick() { this.userSettings.toggleRandomName(); this.requestUpdate(); @@ -353,31 +345,6 @@ export class SettingsModal extends LitElement implements Controller {
- -