diff --git a/resources/lang/en.json b/resources/lang/en.json index 80cb188e49..1851e86bb8 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -810,6 +810,8 @@ "keybinds_hint": "Click a key to rebind it. You can assign a single key or Shift + key combination.", "dark_mode_label": "Dark Mode", "dark_mode_desc": "Toggle the site’s appearance between light and dark themes", + "colorblind_label": "Colorblind Mode", + "colorblind_desc": "Use colorblind-friendly territory and border colors", "emojis_label": "Emojis", "emojis_desc": "Toggle whether emojis are shown in game", "alert_frame_label": "Alert Frame", @@ -944,6 +946,7 @@ "colored": "Colored", "black": "Black", "section_structure_icons": "Structure Icons", + "section_accessibility": "Accessibility", "classic_icons_label": "Classic icons", "classic_icons_desc": "Lighter outline with near-black interior", "section_map": "Map", diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index eb806dccff..b25a05396c 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -497,10 +497,20 @@ async function createClientGame( applyGraphicsOverrides(live, userSettings.graphicsOverrides()); applyDarkModeOverride(live, userSettings.darkMode()); }; + // Re-apply render settings, then re-theme and recolor players, on a + // graphics-override change (covers a theme switch such as colorblind mode). + const onGraphicsChanged = (): void => { + regenerateRenderSettings(); + // A graphics override can switch the active theme (e.g. colorblind mode), + // so re-theme existing players and re-upload the palette to recolor their + // territory fills/borders live. + gameView.refreshPlayerColors(); + webglBuilder.refreshPalette(gameView); + }; regenerateRenderSettings(); globalThis.addEventListener( `${USER_SETTINGS_CHANGED_EVENT}:${GRAPHICS_KEY}`, - regenerateRenderSettings, + onGraphicsChanged, { signal: graphicsListenerAbort.signal }, ); globalThis.addEventListener( diff --git a/src/client/UserSettingModal.ts b/src/client/UserSettingModal.ts index fe52b0a020..13f863bf12 100644 --- a/src/client/UserSettingModal.ts +++ b/src/client/UserSettingModal.ts @@ -206,6 +206,25 @@ export class UserSettingModal extends BaseModal { console.log("🌙 Dark Mode:", this.userSettings.darkMode() ? "ON" : "OFF"); } + /** Whether colorblind mode is currently enabled in the graphics overrides. */ + private colorblindMode(): boolean { + return ( + this.userSettings.graphicsOverrides().accessibility?.colorblind ?? false + ); + } + + /** Flip the colorblind-mode graphics override and persist it. */ + private toggleColorblindMode() { + const overrides = this.userSettings.graphicsOverrides(); + this.userSettings.setGraphicsOverrides({ + ...overrides, + accessibility: { + ...overrides.accessibility, + colorblind: !this.colorblindMode(), + }, + }); + } + private toggleEmojis() { this.userSettings.toggleEmojis(); @@ -742,6 +761,15 @@ export class UserSettingModal extends BaseModal { @change=${this.toggleDarkMode} > + + + , + ) { + const current = this.userSettings.graphicsOverrides(); + this.userSettings.setGraphicsOverrides({ + ...current, + accessibility: { ...current.accessibility, ...patch }, + }); + this.requestUpdate(); + } + private currentSpecialEffects(): boolean { return ( this.userSettings.graphicsOverrides().passEnabled?.fx ?? @@ -299,6 +311,18 @@ export class GraphicsSettingsModal extends LitElement implements Controller { this.patchPassEnabled({ fx: !this.currentSpecialEffects() }); } + /** Whether colorblind mode is currently enabled. */ + private currentColorblind(): boolean { + return ( + this.userSettings.graphicsOverrides().accessibility?.colorblind ?? false + ); + } + + /** Toggle colorblind-friendly colors. */ + private onToggleColorblind() { + this.patchAccessibility({ colorblind: !this.currentColorblind() }); + } + private onNameScaleChange(event: Event) { const value = parseFloat((event.target as HTMLInputElement).value); this.patchName({ nameScaleFactor: value }); @@ -339,6 +363,7 @@ export class GraphicsSettingsModal extends LitElement implements Controller { const territoryAlpha = this.currentTerritoryAlpha(); const railDrawDistance = RAIL_ZOOM_MAX - this.currentRailMinZoom(); const railThickness = this.currentRailThickness(); + const colorblind = this.currentColorblind(); return html`
+
+ ${translateText("graphics_setting.section_accessibility")} +
+ + +