mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-08-07 19:21:30 +00:00
Replace dark mode with player-adjustable lighting (#4280)
## 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 <noreply@anthropic.com>
This commit is contained in:
@@ -74,6 +74,19 @@ describe("GraphicsOverridesSchema", () => {
|
||||
}
|
||||
});
|
||||
|
||||
test("accepts partial lighting overrides", () => {
|
||||
const cases = [
|
||||
{ lighting: {} },
|
||||
{ lighting: { ambient: 0.5 } },
|
||||
{ lighting: { ambient: 1 } },
|
||||
{ lighting: { falloffPower: 2 } },
|
||||
{ lighting: { ambient: 0.3, falloffPower: 1.5 } },
|
||||
];
|
||||
for (const c of cases) {
|
||||
expect(GraphicsOverridesSchema.safeParse(c).success).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
test("rejects wrong field types", () => {
|
||||
expect(
|
||||
GraphicsOverridesSchema.safeParse({ name: { nameScaleFactor: "big" } })
|
||||
@@ -115,6 +128,16 @@ describe("GraphicsOverridesSchema", () => {
|
||||
railroad: { railThickness: "wide" },
|
||||
}).success,
|
||||
).toBe(false);
|
||||
expect(
|
||||
GraphicsOverridesSchema.safeParse({
|
||||
lighting: { ambient: "dark" },
|
||||
}).success,
|
||||
).toBe(false);
|
||||
expect(
|
||||
GraphicsOverridesSchema.safeParse({
|
||||
lighting: { falloffPower: "soft" },
|
||||
}).success,
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -351,6 +374,55 @@ describe("applyGraphicsOverrides", () => {
|
||||
expect(z.railThickness).toBe(defaults.railThickness);
|
||||
});
|
||||
|
||||
test("ambient < 1 sets ambient and enables the lighting pass", () => {
|
||||
const l = gen({ lighting: { ambient: 0.5 } }).lighting;
|
||||
expect(l.ambient).toBe(0.5);
|
||||
expect(l.enabled).toBe(true);
|
||||
});
|
||||
|
||||
test("ambient === 1 sets ambient but leaves lighting disabled (identity)", () => {
|
||||
const l = gen({ lighting: { ambient: 1 } }).lighting;
|
||||
expect(l.ambient).toBe(1);
|
||||
expect(l.enabled).toBe(false);
|
||||
});
|
||||
|
||||
test("ambient absent → lighting stays at render-settings.json defaults", () => {
|
||||
const defaults = createRenderSettings().lighting;
|
||||
expect(gen({}).lighting.ambient).toBe(defaults.ambient);
|
||||
expect(gen({}).lighting.enabled).toBe(defaults.enabled);
|
||||
expect(gen({ lighting: {} }).lighting.enabled).toBe(defaults.enabled);
|
||||
});
|
||||
|
||||
test("applies falloffPower override (including values below default)", () => {
|
||||
expect(gen({ lighting: { falloffPower: 1.4 } }).lighting.falloffPower).toBe(
|
||||
1.4,
|
||||
);
|
||||
expect(gen({ lighting: { falloffPower: 3 } }).lighting.falloffPower).toBe(
|
||||
3,
|
||||
);
|
||||
});
|
||||
|
||||
test("falloffPower override alone does not enable the lighting pass", () => {
|
||||
expect(gen({ lighting: { falloffPower: 1.4 } }).lighting.enabled).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test("lighting override leaves other lighting fields at defaults", () => {
|
||||
const defaults = createRenderSettings().lighting;
|
||||
const l = gen({ lighting: { ambient: 0.4 } }).lighting;
|
||||
expect(l.falloffPower).toBe(defaults.falloffPower);
|
||||
expect(l.blurZoomDivisor).toBe(defaults.blurZoomDivisor);
|
||||
expect(l.lightRadiusMultiplier).toBe(defaults.lightRadiusMultiplier);
|
||||
});
|
||||
|
||||
test("ambient + falloffPower compose together", () => {
|
||||
const l = gen({ lighting: { ambient: 0.3, falloffPower: 1 } }).lighting;
|
||||
expect(l.ambient).toBe(0.3);
|
||||
expect(l.falloffPower).toBe(1);
|
||||
expect(l.enabled).toBe(true);
|
||||
});
|
||||
|
||||
test("classicIcons + name overrides compose independently", () => {
|
||||
const s = gen({
|
||||
name: { darkNames: true, nameScaleFactor: 0.9 },
|
||||
|
||||
Reference in New Issue
Block a user