mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-28 16:54:15 +00:00
Move theme data into the render-settings JSON pipeline (#4223)
**Add approved & assigned issue number here:** N/A — maintainer refactor. ## Description: Replaces the theme class hierarchy (`BaseTheme`/`PastelTheme`/`ColorblindTheme`) with theme JSON files — `default-theme.json` and `colorblind-theme.json` — combined with `render-settings.json` at runtime into a single graphics-configuration pipeline (`settings.theme`). One `SettingsTheme` class keeps the algorithms (color allocation, team-variation generation, LAB-contrast structure colors) and reads all data from `ThemeSettings`; adding a theme is now just adding a JSON file. Colorblind mode (#4150) is fully preserved: - Same palettes — the 32-color CVD-safe pool and Okabe-Ito team colors are baked into `colorblind-theme.json` - The relative border rule (`l × 0.6`) is expressed as a `borderLightnessScale` knob alongside the default theme's absolute `borderDarken` - The mid-game re-theme wiring (`refreshPlayerColors`/`refreshPalette`) and the affiliation/friend-foe tint overrides are unchanged; `applyGraphicsOverrides` now also swaps the `settings.theme` slice - `deepAssign` replaces arrays wholesale so differing palette lengths survive theme switches Verified against the previous implementation with an equivalence test (since removed): default-theme colors are byte-identical including allocation order; colorblind team/derived colors are byte-identical, and FFA assignment may permute within the same palette (hex baking rounds upstream's fractional-RGB colord objects, which can flip the allocator's greedy delta-E ordering — rendered colors round identically either way). Also removes dead theme surface (`terrainColor`, `backgroundColor`, `falloutColor`, `font`, `textColor`, spawn-highlight variants, `PastelThemeDark`) — GL terrain colors and dark mode were already handled in the renderer. Note this means the colorblind terrain bands from #4150 were dead code (nothing calls `terrainColor`; GL terrain comes from `ColorUtils.encodeTerrainTile`); wiring CVD-safe terrain into the terrain texture would be a follow-up. ## Please complete the following: - [x] I have added screenshots for all UI updates — N/A, no UI changes (verified color-identical) - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file — N/A, no user-visible text - [x] I have added relevant tests to the test directory — `tests/Colors.test.ts` updated for the new pipeline (team colors from theme JSON, colorblind palette/border tests) ## Please put your Discord username so you can be contacted if a bug or regression is found: evanpelle 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -49,6 +49,7 @@ each frame (and animate from local time, e.g. the spawn-overlay breath).
|
||||
| `gl/Camera.ts` | World↔screen math; mutated externally each frame via `setCameraState` |
|
||||
| `gl/RenderSettings.ts` | Typed view of `render-settings.json` (tuning knobs) |
|
||||
| `gl/render-settings.json` | All per-pass tuning constants (alpha, radii, colors, etc.) |
|
||||
| `gl/*-theme.json` | Theme data (player/team palettes, color-derivation knobs) — the active one is combined into `settings.theme` at runtime |
|
||||
| `gl/passes/` | One file per pass — see "Pass conventions" below |
|
||||
| `gl/utils/` | Cross-pass helpers: `GlUtils` (program/shader compile), `TileCodec` (`OWNER_MASK` etc.), `NukeTrajectory` (Bezier math), `Affiliation`, `HeatManager`, `GpuResources` |
|
||||
| `gl/shaders/` | `.glsl` source files (`?raw` imported by passes) |
|
||||
@@ -144,6 +145,14 @@ constants. Passes read their slice (`settings.spawnOverlay`, `settings.bar`,
|
||||
etc.) at construct time and use it in `draw`. The debug GUI in `gl/debug/`
|
||||
gives a live-editable view of the same object during development.
|
||||
|
||||
Theme data (player/team palettes, color-derivation knobs) lives in sibling
|
||||
theme JSONs (`gl/default-theme.json`, `gl/colorblind-theme.json`);
|
||||
`createRenderSettings()` combines the active one with `render-settings.json`
|
||||
into the `settings.theme` slice (the colorblind graphics override swaps the
|
||||
slice in `applyGraphicsOverrides`). The theme module in `src/client/theme/`
|
||||
builds its allocators and color derivations from the same theme JSONs — see
|
||||
`ThemeProvider.ts`.
|
||||
|
||||
## Adding a new pass
|
||||
|
||||
1. Define any new types in `types/` if the pass needs new input shapes.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GraphicsOverrides } from "./GraphicsOverrides";
|
||||
import type { RenderSettings } from "./RenderSettings";
|
||||
import { createThemeSettings, type RenderSettings } from "./RenderSettings";
|
||||
|
||||
const DARK_AMBIENT = 0.35;
|
||||
|
||||
@@ -73,6 +73,9 @@ export function applyGraphicsOverrides(
|
||||
settings.name.outlineB = channel;
|
||||
}
|
||||
if (overrides.accessibility?.colorblind === true) {
|
||||
// Swap the active theme slice for the colorblind palette (replaced
|
||||
// wholesale — palette arrays differ in length between themes).
|
||||
settings.theme = createThemeSettings("colorblind");
|
||||
// Swap the red/green friend-foe encoding (the most common confusion axis)
|
||||
// for a colorblind-safe blue/orange pairing (Okabe-Ito).
|
||||
// Alt-view affiliation borders: self/ally in the blue family, enemy orange.
|
||||
|
||||
@@ -1,6 +1,46 @@
|
||||
import colorblindTheme from "./colorblind-theme.json";
|
||||
import defaultTheme from "./default-theme.json";
|
||||
import defaults from "./render-settings.json";
|
||||
|
||||
/**
|
||||
* Theme data — player/team palettes and color-derivation knobs. Loaded from a
|
||||
* theme JSON file (default-theme.json or colorblind-theme.json) and combined
|
||||
* with render-settings.json at runtime so all graphics configuration flows
|
||||
* through one pipeline. Colors are hex strings; palettes are consumed by the
|
||||
* theme module (src/client/theme/), which generates team variations and
|
||||
* allocates player colors at runtime.
|
||||
*/
|
||||
export interface ThemeSettings {
|
||||
/**
|
||||
* Base color per colored team (keys match ColoredTeams). Per-player
|
||||
* variations are generated at runtime; Bot stays a single flat color.
|
||||
*/
|
||||
teamColors: Record<string, string>;
|
||||
humanColors: string[];
|
||||
nationColors: string[];
|
||||
botColors: string[];
|
||||
/** Used when the primary palettes are exhausted. */
|
||||
fallbackColors: string[];
|
||||
/** Border = territory color darkened by this absolute amount. */
|
||||
borderDarken: number;
|
||||
/**
|
||||
* Border HSL lightness multiplier, applied before borderDarken. 1 = no-op.
|
||||
* Scaling keeps every border the same proportion darker than its fill
|
||||
* (used by the colorblind theme so dark fills don't collapse to black).
|
||||
*/
|
||||
borderLightnessScale: number;
|
||||
defendedBorderDarkenLight: number;
|
||||
defendedBorderDarkenDark: number;
|
||||
/** Minimum LAB delta between structure fill and border colors. */
|
||||
structureContrastTarget: number;
|
||||
/** Border color of the local player's territory. */
|
||||
focusedBorderColor: string;
|
||||
/** Tint applied to unit sprites during spawn highlight. */
|
||||
spawnHighlightColor: string;
|
||||
}
|
||||
|
||||
export interface RenderSettings {
|
||||
theme: ThemeSettings;
|
||||
passEnabled: {
|
||||
terrain: boolean;
|
||||
territory: boolean;
|
||||
@@ -311,9 +351,30 @@ export interface RenderSettings {
|
||||
lightConfigs: Record<string, { radius: number; intensity: number }>;
|
||||
}
|
||||
|
||||
/** Create a fresh settings object with defaults from render-settings.json. */
|
||||
export type ThemeName = "default" | "colorblind";
|
||||
|
||||
// Typed so tsc validates each theme JSON against the ThemeSettings shape.
|
||||
const THEMES: Record<ThemeName, ThemeSettings> = {
|
||||
default: defaultTheme,
|
||||
colorblind: colorblindTheme,
|
||||
};
|
||||
|
||||
/** Create fresh theme settings with defaults from the named theme JSON. */
|
||||
export function createThemeSettings(
|
||||
name: ThemeName = "default",
|
||||
): ThemeSettings {
|
||||
return JSON.parse(JSON.stringify(THEMES[name])) as ThemeSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fresh settings object: render-settings.json combined with the
|
||||
* active theme JSON.
|
||||
*/
|
||||
export function createRenderSettings(): RenderSettings {
|
||||
return JSON.parse(JSON.stringify(defaults)) as RenderSettings;
|
||||
return {
|
||||
...(JSON.parse(JSON.stringify(defaults)) as Omit<RenderSettings, "theme">),
|
||||
theme: createThemeSettings(),
|
||||
};
|
||||
}
|
||||
|
||||
/** Dump current settings to a downloadable JSON file. */
|
||||
|
||||
@@ -673,6 +673,8 @@ export class GPURenderer {
|
||||
);
|
||||
// SAM radius pass stores its own copy
|
||||
this.samRadiusPass.setPaletteData(this.paletteData);
|
||||
// Name pass caches per-player colors and bakes them into slot rows
|
||||
this.namePass.refreshPlayerColors(this.paletteData);
|
||||
}
|
||||
|
||||
/** Register late-arriving players (updates palette + NamePass lookup maps). */
|
||||
|
||||
@@ -4,10 +4,18 @@
|
||||
|
||||
type Obj = Record<string, any>;
|
||||
|
||||
/** Recursively assign source values onto target, preserving target's structure. */
|
||||
/**
|
||||
* Recursively assign source values onto target, preserving target's structure.
|
||||
* Arrays are replaced wholesale (theme palettes differ in length between
|
||||
* themes, so per-index merging would leave stale entries behind).
|
||||
*/
|
||||
export function deepAssign(target: Obj, source: Obj): void {
|
||||
for (const key of Object.keys(source)) {
|
||||
if (
|
||||
if (Array.isArray(source[key])) {
|
||||
if (key in target) {
|
||||
target[key] = structuredClone(source[key]);
|
||||
}
|
||||
} else if (
|
||||
typeof source[key] === "object" &&
|
||||
source[key] !== null &&
|
||||
typeof target[key] === "object" &&
|
||||
|
||||
@@ -0,0 +1,430 @@
|
||||
{
|
||||
"teamColors": {
|
||||
"Red": "#d55e00",
|
||||
"Blue": "#0072b2",
|
||||
"Teal": "#009e73",
|
||||
"Purple": "#cc79a7",
|
||||
"Yellow": "#f0e442",
|
||||
"Orange": "#e69f00",
|
||||
"Green": "#56b4e9",
|
||||
"Bot": "#d1cdc7",
|
||||
"Humans": "#0072b2",
|
||||
"Nations": "#d55e00"
|
||||
},
|
||||
"humanColors": [
|
||||
"#b60056",
|
||||
"#007700",
|
||||
"#0076fb",
|
||||
"#db5f11",
|
||||
"#00b8ae",
|
||||
"#ff75f9",
|
||||
"#b6c706",
|
||||
"#00e8ff",
|
||||
"#c7003b",
|
||||
"#008b3a",
|
||||
"#7e72ff",
|
||||
"#cf8400",
|
||||
"#00c8ee",
|
||||
"#ff75dd",
|
||||
"#90e448",
|
||||
"#0069e0",
|
||||
"#cd331d",
|
||||
"#009e79",
|
||||
"#cb6af6",
|
||||
"#b9a600",
|
||||
"#00d4ff",
|
||||
"#ff83bd",
|
||||
"#007100",
|
||||
"#0069ef",
|
||||
"#c76000",
|
||||
"#00afb8",
|
||||
"#ff64e0",
|
||||
"#9ac407",
|
||||
"#00dcff",
|
||||
"#ba0025",
|
||||
"#008445",
|
||||
"#8f62ef"
|
||||
],
|
||||
"nationColors": [
|
||||
"#b60056",
|
||||
"#007700",
|
||||
"#0076fb",
|
||||
"#db5f11",
|
||||
"#00b8ae",
|
||||
"#ff75f9",
|
||||
"#b6c706",
|
||||
"#00e8ff",
|
||||
"#c7003b",
|
||||
"#008b3a",
|
||||
"#7e72ff",
|
||||
"#cf8400",
|
||||
"#00c8ee",
|
||||
"#ff75dd",
|
||||
"#90e448",
|
||||
"#0069e0",
|
||||
"#cd331d",
|
||||
"#009e79",
|
||||
"#cb6af6",
|
||||
"#b9a600",
|
||||
"#00d4ff",
|
||||
"#ff83bd",
|
||||
"#007100",
|
||||
"#0069ef",
|
||||
"#c76000",
|
||||
"#00afb8",
|
||||
"#ff64e0",
|
||||
"#9ac407",
|
||||
"#00dcff",
|
||||
"#ba0025",
|
||||
"#008445",
|
||||
"#8f62ef"
|
||||
],
|
||||
"botColors": [
|
||||
"#b60056",
|
||||
"#007700",
|
||||
"#0076fb",
|
||||
"#db5f11",
|
||||
"#00b8ae",
|
||||
"#ff75f9",
|
||||
"#b6c706",
|
||||
"#00e8ff",
|
||||
"#c7003b",
|
||||
"#008b3a",
|
||||
"#7e72ff",
|
||||
"#cf8400",
|
||||
"#00c8ee",
|
||||
"#ff75dd",
|
||||
"#90e448",
|
||||
"#0069e0",
|
||||
"#cd331d",
|
||||
"#009e79",
|
||||
"#cb6af6",
|
||||
"#b9a600",
|
||||
"#00d4ff",
|
||||
"#ff83bd",
|
||||
"#007100",
|
||||
"#0069ef",
|
||||
"#c76000",
|
||||
"#00afb8",
|
||||
"#ff64e0",
|
||||
"#9ac407",
|
||||
"#00dcff",
|
||||
"#ba0025",
|
||||
"#008445",
|
||||
"#8f62ef"
|
||||
],
|
||||
"fallbackColors": [
|
||||
"#230000",
|
||||
"#2d0000",
|
||||
"#370000",
|
||||
"#410000",
|
||||
"#4b0000",
|
||||
"#550000",
|
||||
"#5f0000",
|
||||
"#690000",
|
||||
"#730000",
|
||||
"#7d0000",
|
||||
"#870000",
|
||||
"#910000",
|
||||
"#9b0000",
|
||||
"#a50000",
|
||||
"#af0000",
|
||||
"#b90000",
|
||||
"#c30005",
|
||||
"#cd000a",
|
||||
"#d7000f",
|
||||
"#e10014",
|
||||
"#eb0019",
|
||||
"#f5001e",
|
||||
"#ff0023",
|
||||
"#ff0a2d",
|
||||
"#ff1437",
|
||||
"#ff1e41",
|
||||
"#ff284b",
|
||||
"#ff3255",
|
||||
"#ff3c5f",
|
||||
"#ff4669",
|
||||
"#ff5073",
|
||||
"#ff5a7d",
|
||||
"#ff6487",
|
||||
"#ff6e91",
|
||||
"#ff789b",
|
||||
"#ff82a5",
|
||||
"#ff8caf",
|
||||
"#ff96b9",
|
||||
"#ffa0c3",
|
||||
"#ffaacd",
|
||||
"#ffb4d7",
|
||||
"#ffbee1",
|
||||
"#ffc8eb",
|
||||
"#002d00",
|
||||
"#003700",
|
||||
"#004100",
|
||||
"#004b00",
|
||||
"#005500",
|
||||
"#005f00",
|
||||
"#006900",
|
||||
"#007300",
|
||||
"#007d00",
|
||||
"#008700",
|
||||
"#009100",
|
||||
"#009b00",
|
||||
"#00a500",
|
||||
"#00af00",
|
||||
"#00b900",
|
||||
"#00c305",
|
||||
"#00cd0a",
|
||||
"#00d70f",
|
||||
"#00e114",
|
||||
"#00eb19",
|
||||
"#00f51e",
|
||||
"#00ff23",
|
||||
"#0aff2d",
|
||||
"#14ff37",
|
||||
"#1eff41",
|
||||
"#28ff4b",
|
||||
"#32ff55",
|
||||
"#3cff5f",
|
||||
"#46ff69",
|
||||
"#50ff73",
|
||||
"#5aff7d",
|
||||
"#64ff87",
|
||||
"#6eff91",
|
||||
"#78ff9b",
|
||||
"#82ffa5",
|
||||
"#8cffaf",
|
||||
"#96ffb9",
|
||||
"#a0ffc3",
|
||||
"#aaffcd",
|
||||
"#b4ffd7",
|
||||
"#beffe1",
|
||||
"#c8ffeb",
|
||||
"#000023",
|
||||
"#00002d",
|
||||
"#000037",
|
||||
"#000041",
|
||||
"#00004b",
|
||||
"#000055",
|
||||
"#00005f",
|
||||
"#000069",
|
||||
"#000073",
|
||||
"#00007d",
|
||||
"#000087",
|
||||
"#000091",
|
||||
"#00009b",
|
||||
"#0000a5",
|
||||
"#0000af",
|
||||
"#0000b9",
|
||||
"#0500c3",
|
||||
"#0a00cd",
|
||||
"#0f00d7",
|
||||
"#1400e1",
|
||||
"#1900eb",
|
||||
"#1e00f5",
|
||||
"#2300ff",
|
||||
"#2d0aff",
|
||||
"#3714ff",
|
||||
"#411eff",
|
||||
"#4b28ff",
|
||||
"#5532ff",
|
||||
"#5f3cff",
|
||||
"#6946ff",
|
||||
"#7350ff",
|
||||
"#7d5aff",
|
||||
"#8764ff",
|
||||
"#916eff",
|
||||
"#9b78ff",
|
||||
"#a582ff",
|
||||
"#af8cff",
|
||||
"#b996ff",
|
||||
"#c3a0ff",
|
||||
"#cdaaff",
|
||||
"#d7b4ff",
|
||||
"#e1beff",
|
||||
"#ebc8ff",
|
||||
"#230023",
|
||||
"#2d002d",
|
||||
"#370037",
|
||||
"#410041",
|
||||
"#4b004b",
|
||||
"#550055",
|
||||
"#5f005f",
|
||||
"#690069",
|
||||
"#730073",
|
||||
"#7d007d",
|
||||
"#870087",
|
||||
"#910091",
|
||||
"#9b009b",
|
||||
"#a500a5",
|
||||
"#af00af",
|
||||
"#b900b9",
|
||||
"#c305c3",
|
||||
"#cd0acd",
|
||||
"#d70fd7",
|
||||
"#e114e1",
|
||||
"#eb19eb",
|
||||
"#f51ef5",
|
||||
"#ff23ff",
|
||||
"#ff2dff",
|
||||
"#ff37ff",
|
||||
"#ff41ff",
|
||||
"#ff4bff",
|
||||
"#ff55ff",
|
||||
"#ff5fff",
|
||||
"#ff69ff",
|
||||
"#ff73ff",
|
||||
"#ff7dff",
|
||||
"#ff87ff",
|
||||
"#ff91ff",
|
||||
"#ff9bff",
|
||||
"#ffa5ff",
|
||||
"#ffafff",
|
||||
"#ffb9ff",
|
||||
"#ffc3ff",
|
||||
"#ffcdff",
|
||||
"#ffd7ff",
|
||||
"#002323",
|
||||
"#002d2d",
|
||||
"#003737",
|
||||
"#004141",
|
||||
"#004b4b",
|
||||
"#005555",
|
||||
"#005f5f",
|
||||
"#006969",
|
||||
"#007373",
|
||||
"#007d7d",
|
||||
"#008787",
|
||||
"#009191",
|
||||
"#009b9b",
|
||||
"#00a5a5",
|
||||
"#00afaf",
|
||||
"#00b9b9",
|
||||
"#05c3c3",
|
||||
"#0acdcd",
|
||||
"#0fd7d7",
|
||||
"#14e1e1",
|
||||
"#19ebeb",
|
||||
"#1ef5f5",
|
||||
"#23ffff",
|
||||
"#2dffff",
|
||||
"#37ffff",
|
||||
"#41ffff",
|
||||
"#4bffff",
|
||||
"#55ffff",
|
||||
"#5fffff",
|
||||
"#69ffff",
|
||||
"#73ffff",
|
||||
"#7dffff",
|
||||
"#87ffff",
|
||||
"#91ffff",
|
||||
"#9bffff",
|
||||
"#a5ffff",
|
||||
"#afffff",
|
||||
"#b9ffff",
|
||||
"#c3ffff",
|
||||
"#cdffff",
|
||||
"#d7ffff",
|
||||
"#232300",
|
||||
"#2d2d00",
|
||||
"#373700",
|
||||
"#414100",
|
||||
"#4b4b00",
|
||||
"#555500",
|
||||
"#5f5f00",
|
||||
"#696900",
|
||||
"#737300",
|
||||
"#7d7d00",
|
||||
"#878700",
|
||||
"#919100",
|
||||
"#9b9b00",
|
||||
"#a5a500",
|
||||
"#afaf00",
|
||||
"#b9b900",
|
||||
"#c3c305",
|
||||
"#cdcd0a",
|
||||
"#d7d70f",
|
||||
"#e1e114",
|
||||
"#ebeb19",
|
||||
"#f5f51e",
|
||||
"#ffff23",
|
||||
"#ffff2d",
|
||||
"#ffff37",
|
||||
"#ffff41",
|
||||
"#ffff4b",
|
||||
"#ffff55",
|
||||
"#ffff5f",
|
||||
"#ffff69",
|
||||
"#ffff73",
|
||||
"#ffff7d",
|
||||
"#ffff87",
|
||||
"#ffff91",
|
||||
"#ffff9b",
|
||||
"#ffffa5",
|
||||
"#ffffaf",
|
||||
"#ffffb9",
|
||||
"#ffffc3",
|
||||
"#ffffcd",
|
||||
"#ffffd7",
|
||||
"#d7ffc8",
|
||||
"#e1ffaf",
|
||||
"#f0faa0",
|
||||
"#f5f5af",
|
||||
"#96c8ff",
|
||||
"#a0d7ff",
|
||||
"#aae1ff",
|
||||
"#b4ebfa",
|
||||
"#bef5f0",
|
||||
"#d2fff5",
|
||||
"#dcffff",
|
||||
"#e6faff",
|
||||
"#f0f0ff",
|
||||
"#fae6ff",
|
||||
"#aabeff",
|
||||
"#b4b4ff",
|
||||
"#c8aaff",
|
||||
"#be8cc3",
|
||||
"#c391c8",
|
||||
"#c896cd",
|
||||
"#cd9bd2",
|
||||
"#d2a0d7",
|
||||
"#d7a5dc",
|
||||
"#dcaae1",
|
||||
"#e1afe6",
|
||||
"#e6b4eb",
|
||||
"#ebb9f0",
|
||||
"#f0bef5",
|
||||
"#f5c3fa",
|
||||
"#fac8ff",
|
||||
"#ffcdff",
|
||||
"#ffd2ff",
|
||||
"#ffd2fa",
|
||||
"#ffcdf5",
|
||||
"#ffd7f5",
|
||||
"#dca0ff",
|
||||
"#eb96ff",
|
||||
"#f5a0f0",
|
||||
"#ffaae1",
|
||||
"#ffb9d7",
|
||||
"#ffc3eb",
|
||||
"#ffc8dc",
|
||||
"#ffd2e6",
|
||||
"#ffdceb",
|
||||
"#ffdcfa",
|
||||
"#ffe1ff",
|
||||
"#ffe6f5",
|
||||
"#ffebeb",
|
||||
"#ffd7c3",
|
||||
"#ffe1b4",
|
||||
"#ffe6be",
|
||||
"#ffebc8",
|
||||
"#fff5d2",
|
||||
"#fff0dc"
|
||||
],
|
||||
"borderDarken": 0,
|
||||
"borderLightnessScale": 0.6,
|
||||
"defendedBorderDarkenLight": 0.2,
|
||||
"defendedBorderDarkenDark": 0.4,
|
||||
"structureContrastTarget": 0.5,
|
||||
"focusedBorderColor": "#e6e6e6",
|
||||
"spawnHighlightColor": "#ffd54f"
|
||||
}
|
||||
@@ -0,0 +1,495 @@
|
||||
{
|
||||
"teamColors": {
|
||||
"Red": "#eb3333",
|
||||
"Blue": "#2962ff",
|
||||
"Teal": "#2bd4bd",
|
||||
"Purple": "#9234ea",
|
||||
"Yellow": "#e7b008",
|
||||
"Orange": "#f97415",
|
||||
"Green": "#41be52",
|
||||
"Bot": "#d1cdc7",
|
||||
"Humans": "#2962ff",
|
||||
"Nations": "#eb3333"
|
||||
},
|
||||
"humanColors": [
|
||||
"#a3e635",
|
||||
"#84cc16",
|
||||
"#10b981",
|
||||
"#34d399",
|
||||
"#2dd4bf",
|
||||
"#4ade80",
|
||||
"#6ee7b7",
|
||||
"#86efac",
|
||||
"#97ffbb",
|
||||
"#baffc9",
|
||||
"#e6fad2",
|
||||
"#22c55e",
|
||||
"#43be54",
|
||||
"#52b788",
|
||||
"#30b2b4",
|
||||
"#e6fffa",
|
||||
"#dcf0fa",
|
||||
"#e9d5ff",
|
||||
"#ccccff",
|
||||
"#dcdcff",
|
||||
"#cae1ff",
|
||||
"#93c5fd",
|
||||
"#7dd3fc",
|
||||
"#63cafd",
|
||||
"#38bdf8",
|
||||
"#60a5fa",
|
||||
"#3b82f6",
|
||||
"#4f46e5",
|
||||
"#7c3aed",
|
||||
"#9333ea",
|
||||
"#b388ff",
|
||||
"#a78bfa",
|
||||
"#d946ef",
|
||||
"#a855f7",
|
||||
"#be5cfb",
|
||||
"#c084fc",
|
||||
"#f0abfc",
|
||||
"#f472b6",
|
||||
"#ec4899",
|
||||
"#dc2626",
|
||||
"#ef4444",
|
||||
"#eb4b4b",
|
||||
"#f56565",
|
||||
"#f87171",
|
||||
"#fb7185",
|
||||
"#fda4af",
|
||||
"#fca5a5",
|
||||
"#ffcce5",
|
||||
"#fad7e1",
|
||||
"#fbebf5",
|
||||
"#f0f0c8",
|
||||
"#fafad2",
|
||||
"#fff0c8",
|
||||
"#ffdfba",
|
||||
"#fcd34d",
|
||||
"#fbbf24",
|
||||
"#eab308",
|
||||
"#ca8a04",
|
||||
"#f59e0b",
|
||||
"#fb923c",
|
||||
"#f97316",
|
||||
"#ea580c",
|
||||
"#854d0e"
|
||||
],
|
||||
"nationColors": [
|
||||
"#d2d264",
|
||||
"#b4d278",
|
||||
"#aabe64",
|
||||
"#50c878",
|
||||
"#82c882",
|
||||
"#8cb48c",
|
||||
"#a0bea0",
|
||||
"#a0b48c",
|
||||
"#64a050",
|
||||
"#648c6e",
|
||||
"#64b4a0",
|
||||
"#82b4aa",
|
||||
"#aabeb4",
|
||||
"#648296",
|
||||
"#78a0c8",
|
||||
"#8c96b4",
|
||||
"#64d2d2",
|
||||
"#8cb4dc",
|
||||
"#82aabe",
|
||||
"#64b4e6",
|
||||
"#5082be",
|
||||
"#7878be",
|
||||
"#966ebe",
|
||||
"#a078a0",
|
||||
"#aa8cbe",
|
||||
"#b482b4",
|
||||
"#be8c96",
|
||||
"#b464e6",
|
||||
"#b4a0b4",
|
||||
"#aa96aa",
|
||||
"#968296",
|
||||
"#e6b4b4",
|
||||
"#d2a0c8",
|
||||
"#e682b4",
|
||||
"#d264a0",
|
||||
"#be6482",
|
||||
"#dc7878",
|
||||
"#c8826e",
|
||||
"#e68c8c",
|
||||
"#e66464",
|
||||
"#e69664",
|
||||
"#d28c50",
|
||||
"#e6b450",
|
||||
"#c8a06e",
|
||||
"#be9682",
|
||||
"#beb4a0",
|
||||
"#b4aa8c",
|
||||
"#c8c88c",
|
||||
"#beaa64"
|
||||
],
|
||||
"botColors": [
|
||||
"#96a08c",
|
||||
"#a0a096",
|
||||
"#aaaa8c",
|
||||
"#aaaa78",
|
||||
"#96a078",
|
||||
"#96aa82",
|
||||
"#96aa96",
|
||||
"#82aa82",
|
||||
"#8ca08c",
|
||||
"#789664",
|
||||
"#788c78",
|
||||
"#64aa82",
|
||||
"#78a096",
|
||||
"#82a096",
|
||||
"#78aaaa",
|
||||
"#78a0be",
|
||||
"#8296aa",
|
||||
"#8296a0",
|
||||
"#8c96a0",
|
||||
"#8ca0aa",
|
||||
"#96a0a0",
|
||||
"#6478a0",
|
||||
"#78828c",
|
||||
"#8282a0",
|
||||
"#8c828c",
|
||||
"#8c78a0",
|
||||
"#968296",
|
||||
"#968ca0",
|
||||
"#a082a0",
|
||||
"#aa96aa",
|
||||
"#a078be",
|
||||
"#a07882",
|
||||
"#aa788c",
|
||||
"#aa8278",
|
||||
"#aa8282",
|
||||
"#b48c8c",
|
||||
"#be82a0",
|
||||
"#be7878",
|
||||
"#be8c78",
|
||||
"#bea064",
|
||||
"#aa8c64",
|
||||
"#a08c82",
|
||||
"#aa9682",
|
||||
"#a09678",
|
||||
"#a0968c",
|
||||
"#a08c96",
|
||||
"#a096a0",
|
||||
"#968c96",
|
||||
"#b4a0a0"
|
||||
],
|
||||
"fallbackColors": [
|
||||
"#230000",
|
||||
"#2d0000",
|
||||
"#370000",
|
||||
"#410000",
|
||||
"#4b0000",
|
||||
"#550000",
|
||||
"#5f0000",
|
||||
"#690000",
|
||||
"#730000",
|
||||
"#7d0000",
|
||||
"#870000",
|
||||
"#910000",
|
||||
"#9b0000",
|
||||
"#a50000",
|
||||
"#af0000",
|
||||
"#b90000",
|
||||
"#c30005",
|
||||
"#cd000a",
|
||||
"#d7000f",
|
||||
"#e10014",
|
||||
"#eb0019",
|
||||
"#f5001e",
|
||||
"#ff0023",
|
||||
"#ff0a2d",
|
||||
"#ff1437",
|
||||
"#ff1e41",
|
||||
"#ff284b",
|
||||
"#ff3255",
|
||||
"#ff3c5f",
|
||||
"#ff4669",
|
||||
"#ff5073",
|
||||
"#ff5a7d",
|
||||
"#ff6487",
|
||||
"#ff6e91",
|
||||
"#ff789b",
|
||||
"#ff82a5",
|
||||
"#ff8caf",
|
||||
"#ff96b9",
|
||||
"#ffa0c3",
|
||||
"#ffaacd",
|
||||
"#ffb4d7",
|
||||
"#ffbee1",
|
||||
"#ffc8eb",
|
||||
"#002d00",
|
||||
"#003700",
|
||||
"#004100",
|
||||
"#004b00",
|
||||
"#005500",
|
||||
"#005f00",
|
||||
"#006900",
|
||||
"#007300",
|
||||
"#007d00",
|
||||
"#008700",
|
||||
"#009100",
|
||||
"#009b00",
|
||||
"#00a500",
|
||||
"#00af00",
|
||||
"#00b900",
|
||||
"#00c305",
|
||||
"#00cd0a",
|
||||
"#00d70f",
|
||||
"#00e114",
|
||||
"#00eb19",
|
||||
"#00f51e",
|
||||
"#00ff23",
|
||||
"#0aff2d",
|
||||
"#14ff37",
|
||||
"#1eff41",
|
||||
"#28ff4b",
|
||||
"#32ff55",
|
||||
"#3cff5f",
|
||||
"#46ff69",
|
||||
"#50ff73",
|
||||
"#5aff7d",
|
||||
"#64ff87",
|
||||
"#6eff91",
|
||||
"#78ff9b",
|
||||
"#82ffa5",
|
||||
"#8cffaf",
|
||||
"#96ffb9",
|
||||
"#a0ffc3",
|
||||
"#aaffcd",
|
||||
"#b4ffd7",
|
||||
"#beffe1",
|
||||
"#c8ffeb",
|
||||
"#000023",
|
||||
"#00002d",
|
||||
"#000037",
|
||||
"#000041",
|
||||
"#00004b",
|
||||
"#000055",
|
||||
"#00005f",
|
||||
"#000069",
|
||||
"#000073",
|
||||
"#00007d",
|
||||
"#000087",
|
||||
"#000091",
|
||||
"#00009b",
|
||||
"#0000a5",
|
||||
"#0000af",
|
||||
"#0000b9",
|
||||
"#0500c3",
|
||||
"#0a00cd",
|
||||
"#0f00d7",
|
||||
"#1400e1",
|
||||
"#1900eb",
|
||||
"#1e00f5",
|
||||
"#2300ff",
|
||||
"#2d0aff",
|
||||
"#3714ff",
|
||||
"#411eff",
|
||||
"#4b28ff",
|
||||
"#5532ff",
|
||||
"#5f3cff",
|
||||
"#6946ff",
|
||||
"#7350ff",
|
||||
"#7d5aff",
|
||||
"#8764ff",
|
||||
"#916eff",
|
||||
"#9b78ff",
|
||||
"#a582ff",
|
||||
"#af8cff",
|
||||
"#b996ff",
|
||||
"#c3a0ff",
|
||||
"#cdaaff",
|
||||
"#d7b4ff",
|
||||
"#e1beff",
|
||||
"#ebc8ff",
|
||||
"#230023",
|
||||
"#2d002d",
|
||||
"#370037",
|
||||
"#410041",
|
||||
"#4b004b",
|
||||
"#550055",
|
||||
"#5f005f",
|
||||
"#690069",
|
||||
"#730073",
|
||||
"#7d007d",
|
||||
"#870087",
|
||||
"#910091",
|
||||
"#9b009b",
|
||||
"#a500a5",
|
||||
"#af00af",
|
||||
"#b900b9",
|
||||
"#c305c3",
|
||||
"#cd0acd",
|
||||
"#d70fd7",
|
||||
"#e114e1",
|
||||
"#eb19eb",
|
||||
"#f51ef5",
|
||||
"#ff23ff",
|
||||
"#ff2dff",
|
||||
"#ff37ff",
|
||||
"#ff41ff",
|
||||
"#ff4bff",
|
||||
"#ff55ff",
|
||||
"#ff5fff",
|
||||
"#ff69ff",
|
||||
"#ff73ff",
|
||||
"#ff7dff",
|
||||
"#ff87ff",
|
||||
"#ff91ff",
|
||||
"#ff9bff",
|
||||
"#ffa5ff",
|
||||
"#ffafff",
|
||||
"#ffb9ff",
|
||||
"#ffc3ff",
|
||||
"#ffcdff",
|
||||
"#ffd7ff",
|
||||
"#002323",
|
||||
"#002d2d",
|
||||
"#003737",
|
||||
"#004141",
|
||||
"#004b4b",
|
||||
"#005555",
|
||||
"#005f5f",
|
||||
"#006969",
|
||||
"#007373",
|
||||
"#007d7d",
|
||||
"#008787",
|
||||
"#009191",
|
||||
"#009b9b",
|
||||
"#00a5a5",
|
||||
"#00afaf",
|
||||
"#00b9b9",
|
||||
"#05c3c3",
|
||||
"#0acdcd",
|
||||
"#0fd7d7",
|
||||
"#14e1e1",
|
||||
"#19ebeb",
|
||||
"#1ef5f5",
|
||||
"#23ffff",
|
||||
"#2dffff",
|
||||
"#37ffff",
|
||||
"#41ffff",
|
||||
"#4bffff",
|
||||
"#55ffff",
|
||||
"#5fffff",
|
||||
"#69ffff",
|
||||
"#73ffff",
|
||||
"#7dffff",
|
||||
"#87ffff",
|
||||
"#91ffff",
|
||||
"#9bffff",
|
||||
"#a5ffff",
|
||||
"#afffff",
|
||||
"#b9ffff",
|
||||
"#c3ffff",
|
||||
"#cdffff",
|
||||
"#d7ffff",
|
||||
"#232300",
|
||||
"#2d2d00",
|
||||
"#373700",
|
||||
"#414100",
|
||||
"#4b4b00",
|
||||
"#555500",
|
||||
"#5f5f00",
|
||||
"#696900",
|
||||
"#737300",
|
||||
"#7d7d00",
|
||||
"#878700",
|
||||
"#919100",
|
||||
"#9b9b00",
|
||||
"#a5a500",
|
||||
"#afaf00",
|
||||
"#b9b900",
|
||||
"#c3c305",
|
||||
"#cdcd0a",
|
||||
"#d7d70f",
|
||||
"#e1e114",
|
||||
"#ebeb19",
|
||||
"#f5f51e",
|
||||
"#ffff23",
|
||||
"#ffff2d",
|
||||
"#ffff37",
|
||||
"#ffff41",
|
||||
"#ffff4b",
|
||||
"#ffff55",
|
||||
"#ffff5f",
|
||||
"#ffff69",
|
||||
"#ffff73",
|
||||
"#ffff7d",
|
||||
"#ffff87",
|
||||
"#ffff91",
|
||||
"#ffff9b",
|
||||
"#ffffa5",
|
||||
"#ffffaf",
|
||||
"#ffffb9",
|
||||
"#ffffc3",
|
||||
"#ffffcd",
|
||||
"#ffffd7",
|
||||
"#d7ffc8",
|
||||
"#e1ffaf",
|
||||
"#f0faa0",
|
||||
"#f5f5af",
|
||||
"#96c8ff",
|
||||
"#a0d7ff",
|
||||
"#aae1ff",
|
||||
"#b4ebfa",
|
||||
"#bef5f0",
|
||||
"#d2fff5",
|
||||
"#dcffff",
|
||||
"#e6faff",
|
||||
"#f0f0ff",
|
||||
"#fae6ff",
|
||||
"#aabeff",
|
||||
"#b4b4ff",
|
||||
"#c8aaff",
|
||||
"#be8cc3",
|
||||
"#c391c8",
|
||||
"#c896cd",
|
||||
"#cd9bd2",
|
||||
"#d2a0d7",
|
||||
"#d7a5dc",
|
||||
"#dcaae1",
|
||||
"#e1afe6",
|
||||
"#e6b4eb",
|
||||
"#ebb9f0",
|
||||
"#f0bef5",
|
||||
"#f5c3fa",
|
||||
"#fac8ff",
|
||||
"#ffcdff",
|
||||
"#ffd2ff",
|
||||
"#ffd2fa",
|
||||
"#ffcdf5",
|
||||
"#ffd7f5",
|
||||
"#dca0ff",
|
||||
"#eb96ff",
|
||||
"#f5a0f0",
|
||||
"#ffaae1",
|
||||
"#ffb9d7",
|
||||
"#ffc3eb",
|
||||
"#ffc8dc",
|
||||
"#ffd2e6",
|
||||
"#ffdceb",
|
||||
"#ffdcfa",
|
||||
"#ffe1ff",
|
||||
"#ffe6f5",
|
||||
"#ffebeb",
|
||||
"#ffd7c3",
|
||||
"#ffe1b4",
|
||||
"#ffe6be",
|
||||
"#ffebc8",
|
||||
"#fff5d2",
|
||||
"#fff0dc"
|
||||
],
|
||||
"borderDarken": 0.125,
|
||||
"borderLightnessScale": 1,
|
||||
"defendedBorderDarkenLight": 0.2,
|
||||
"defendedBorderDarkenDark": 0.4,
|
||||
"structureContrastTarget": 0.5,
|
||||
"focusedBorderColor": "#e6e6e6",
|
||||
"spawnHighlightColor": "#ffd54f"
|
||||
}
|
||||
@@ -222,6 +222,25 @@ export class NamePass {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-read every known player's territory color from the palette and rewrite
|
||||
* the live slot rows. Called after a mid-game palette refresh (e.g. toggling
|
||||
* colorblind mode) so name fills/outlines pick up the re-themed colors.
|
||||
*/
|
||||
refreshPlayerColors(paletteData: Float32Array): void {
|
||||
for (const [id, p] of this.playerByID) {
|
||||
const off = p.smallID * 4;
|
||||
this.playerColors.set(id, [
|
||||
paletteData[off],
|
||||
paletteData[off + 1],
|
||||
paletteData[off + 2],
|
||||
]);
|
||||
}
|
||||
for (const slot of this.slots.values()) {
|
||||
this.writePlayerDataRow(slot);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the texture layer for a slot's flag (called once at slot creation).
|
||||
* If the image is already loaded the layer index is set immediately; otherwise
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* GPU-ready color utilities.
|
||||
*
|
||||
* Terrain RGBA: Uint8Array(w × h × 4) — one RGBA pixel per tile, computed
|
||||
* from PastelTheme rules applied to the raw terrain byte layout.
|
||||
* from the terrain color rules applied to the raw terrain byte layout.
|
||||
*
|
||||
* Player palette is NOT built here — consumers provide a pre-built
|
||||
* Float32Array(PALETTE_SIZE × 2 × 4) to the GPURenderer constructor.
|
||||
@@ -19,7 +19,7 @@ export function getPaletteSize(): number {
|
||||
|
||||
/**
|
||||
* Compute a static RGBA8 texture from raw terrain bytes.
|
||||
* Replicates PastelTheme.terrainColor() on the CPU.
|
||||
* The single source of truth for terrain colors.
|
||||
*
|
||||
* Terrain byte layout per tile:
|
||||
* bit 7: isLand
|
||||
|
||||
Reference in New Issue
Block a user