Add Classic Icons toggle to Graphics Settings

Adds a "Classic icons" toggle in the structure-icons section of the
Graphics Settings modal. Off (default) keeps today's renderer look;
on switches to a classic style — lighter player-colored shape behind
a dark icon glyph, with 0.75 alpha for a subtle translucent feel.

Exposes the underlying tuning as new render-settings knobs
(`structure.fillDarken`, `borderDarken`, `iconAlpha`, `iconR/G/B`) and
threads them through the structure shader as uniforms, replacing the
previously hardcoded `darken(_, 0.65)` / `darken(_, 0.35)` calls and
the hardcoded white `vec3(1.0)` icon color. The `classicIcons` boolean
in the override schema is the single user-facing knob; the generator
derives the five underlying field values from it. Extends the
ClientGameRunner live-apply path to copy the `structure` slice too,
and adds tests covering the schema and preset derivation.
This commit is contained in:
evanpelle
2026-05-28 14:47:40 -07:00
parent e938e5936b
commit fc3d80ec73
9 changed files with 164 additions and 8 deletions
+60
View File
@@ -23,6 +23,18 @@ describe("GraphicsOverridesSchema", () => {
}
});
test("accepts partial structure overrides", () => {
const cases = [
{ structure: {} },
{ structure: { classicIcons: true } },
{ structure: { classicIcons: false } },
{ name: { darkNames: true }, structure: { classicIcons: true } },
];
for (const c of cases) {
expect(GraphicsOverridesSchema.safeParse(c).success).toBe(true);
}
});
test("rejects wrong field types", () => {
expect(
GraphicsOverridesSchema.safeParse({ name: { nameScaleFactor: "big" } })
@@ -31,6 +43,11 @@ describe("GraphicsOverridesSchema", () => {
expect(
GraphicsOverridesSchema.safeParse({ name: { darkNames: "yes" } }).success,
).toBe(false);
expect(
GraphicsOverridesSchema.safeParse({
structure: { classicIcons: "yes" },
}).success,
).toBe(false);
});
});
@@ -117,4 +134,47 @@ describe("generateRenderSettings", () => {
expect(s.dayNight).toEqual(defaults.dayNight);
expect(s.structure).toEqual(defaults.structure);
});
test("classicIcons=true → light shape + dark icon + 0.75 alpha", () => {
const s = generateRenderSettings({
structure: { classicIcons: true },
}).structure;
// Shape (circle behind) is mostly player color, lightly darkened.
expect(s.fillDarken).toBe(1.0);
expect(s.borderDarken).toBe(0.7);
// Icon glyph itself is black.
expect(s.iconR).toBe(0);
expect(s.iconG).toBe(0);
expect(s.iconB).toBe(0);
// Slightly translucent in classic mode.
expect(s.iconAlpha).toBe(0.75);
});
test("classicIcons=false or absent → keeps render-settings.json defaults (fully opaque)", () => {
const defaults = createRenderSettings().structure;
const off = generateRenderSettings({
structure: { classicIcons: false },
}).structure;
expect(off.borderDarken).toBe(defaults.borderDarken);
expect(off.fillDarken).toBe(defaults.fillDarken);
expect(off.iconR).toBe(defaults.iconR);
expect(off.iconAlpha).toBe(1);
const absent = generateRenderSettings({ structure: {} }).structure;
expect(absent.borderDarken).toBe(defaults.borderDarken);
expect(absent.fillDarken).toBe(defaults.fillDarken);
expect(absent.iconR).toBe(defaults.iconR);
expect(absent.iconAlpha).toBe(1);
});
test("classicIcons + name overrides compose independently", () => {
const s = generateRenderSettings({
name: { darkNames: true, nameScaleFactor: 0.9 },
structure: { classicIcons: true },
});
expect(s.name.fillUsePlayerColor).toBe(false);
expect(s.name.nameScaleFactor).toBe(0.9);
expect(s.structure.borderDarken).toBe(0.7);
expect(s.structure.fillDarken).toBe(1.0);
expect(s.structure.iconAlpha).toBe(0.75);
});
});