Add territory saturation and opacity graphics settings

Expose two new user-configurable map-overlay controls in the graphics
settings modal: territory saturation (mutes fill colors toward grayscale)
and territory opacity (lets terrain show through the fill).

The territory fragment shader blends the fill toward its luminance based
on uSaturation and applies uTerritoryAlpha as the absolute fill opacity.
Both are wired through RenderSettings, the GraphicsOverrides schema,
applyGraphicsOverrides, the debug Layout sliders, and TerritoryPass
uniforms, with defaults (saturation 1, alpha 0.588) in render-settings.json.
Adds the corresponding en.json label/description strings.
This commit is contained in:
evanpelle
2026-06-09 19:15:54 -07:00
parent 855695b78e
commit 2d28d5463b
10 changed files with 184 additions and 0 deletions
+44
View File
@@ -42,6 +42,18 @@ describe("GraphicsOverridesSchema", () => {
}
});
test("accepts partial mapOverlay overrides", () => {
const cases = [
{ mapOverlay: {} },
{ mapOverlay: { territorySaturation: 0.5 } },
{ mapOverlay: { territoryAlpha: 0.8 } },
{ mapOverlay: { territorySaturation: 0, territoryAlpha: 1 } },
];
for (const c of cases) {
expect(GraphicsOverridesSchema.safeParse(c).success).toBe(true);
}
});
test("rejects wrong field types", () => {
expect(
GraphicsOverridesSchema.safeParse({ name: { nameScaleFactor: "big" } })
@@ -55,6 +67,11 @@ describe("GraphicsOverridesSchema", () => {
structure: { classicIcons: "yes" },
}).success,
).toBe(false);
expect(
GraphicsOverridesSchema.safeParse({
mapOverlay: { territorySaturation: "full" },
}).success,
).toBe(false);
});
});
@@ -170,6 +187,33 @@ describe("applyGraphicsOverrides", () => {
expect(absent.iconAlpha).toBe(1);
});
test("applies territorySaturation override (including 0)", () => {
expect(
gen({ mapOverlay: { territorySaturation: 0.4 } }).mapOverlay
.territorySaturation,
).toBe(0.4);
expect(
gen({ mapOverlay: { territorySaturation: 0 } }).mapOverlay
.territorySaturation,
).toBe(0);
});
test("applies territoryAlpha override (including 0)", () => {
expect(
gen({ mapOverlay: { territoryAlpha: 0.3 } }).mapOverlay.territoryAlpha,
).toBe(0.3);
expect(
gen({ mapOverlay: { territoryAlpha: 0 } }).mapOverlay.territoryAlpha,
).toBe(0);
});
test("mapOverlay override leaves other mapOverlay fields at defaults", () => {
const defaults = createRenderSettings().mapOverlay;
const mo = gen({ mapOverlay: { territorySaturation: 0.2 } }).mapOverlay;
expect(mo.territoryAlpha).toBe(defaults.territoryAlpha);
expect(mo.territoryDefenseDarken).toBe(defaults.territoryDefenseDarken);
});
test("classicIcons + name overrides compose independently", () => {
const s = gen({
name: { darkNames: true, nameScaleFactor: 0.9 },