mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-04 12:12:32 +00:00
Feature/colorblind mode (#4150)
**Add approved & assigned issue number here:** Resolves #2549 ## Description: Adds colorblind mode. Similar to dark mode, it exists as a toggle in settings. When enabled, it swaps the game's theme (which is refactored to extend from a theme base class) to use more colorblind-friendly colors and brightness variations. Borders are darkened, and terrarin is separated by lightness. Friendly/Foe colors and switched to blue/orange instead of red/green. The theme refactor supports adding new themes without having to reimplement the color distribution system. New themes can extend the BaseTheme and supply the data, such as palettes, team-color variations, and terrain. New setting: <img width="880" height="273" alt="Screenshot 2026-06-04 at 11 30 27 AM" src="https://github.com/user-attachments/assets/d5d573d5-cc64-4ac1-95c2-00627faf17cc" /> New color palette: <img width="1119" height="757" alt="Screenshot 2026-06-04 at 11 30 59 AM" src="https://github.com/user-attachments/assets/2bb15bc9-992b-41ae-ab0e-b01fe0c3c6bb" /> ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory ## Please put your Discord username so you can be contacted if a bug or regression is found: jetaviz
This commit is contained in:
@@ -872,6 +872,17 @@ export class GameView implements GameMap {
|
||||
return Array.from(this._players.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Recompute every player's theme-derived colors. Call when the active theme
|
||||
* changes mid-game (e.g. toggling colorblind mode) so existing territories
|
||||
* re-color; the renderer palette must be refreshed afterwards.
|
||||
*/
|
||||
refreshPlayerColors(): void {
|
||||
for (const p of this._players.values()) {
|
||||
p.refreshColors();
|
||||
}
|
||||
}
|
||||
|
||||
playerBySmallID(id: number): PlayerView | TerraNullius {
|
||||
if (id === 0) {
|
||||
return new TerraNulliusImpl();
|
||||
|
||||
@@ -105,19 +105,20 @@ export class PlayerView {
|
||||
/** Static header data — set once at construction, never mutated. */
|
||||
public static: PlayerStatic;
|
||||
|
||||
private _territoryColor: Colord;
|
||||
private _borderColor: Colord;
|
||||
private _railColor: Colord;
|
||||
// Assigned via computeColors() in the constructor; re-assignable on theme change.
|
||||
private _territoryColor!: Colord;
|
||||
private _borderColor!: Colord;
|
||||
private _railColor!: Colord;
|
||||
// Update here to include structure light and dark colors
|
||||
private _structureColors: { light: Colord; dark: Colord };
|
||||
private _structureColors!: { light: Colord; dark: Colord };
|
||||
|
||||
// Pre-computed border color variants
|
||||
private _borderColorNeutral: Colord;
|
||||
private _borderColorFriendly: Colord;
|
||||
private _borderColorEmbargo: Colord;
|
||||
private _borderColorDefendedNeutral: { light: Colord; dark: Colord };
|
||||
private _borderColorDefendedFriendly: { light: Colord; dark: Colord };
|
||||
private _borderColorDefendedEmbargo: { light: Colord; dark: Colord };
|
||||
private _borderColorNeutral!: Colord;
|
||||
private _borderColorFriendly!: Colord;
|
||||
private _borderColorEmbargo!: Colord;
|
||||
private _borderColorDefendedNeutral!: { light: Colord; dark: Colord };
|
||||
private _borderColorDefendedFriendly!: { light: Colord; dark: Colord };
|
||||
private _borderColorDefendedEmbargo!: { light: Colord; dark: Colord };
|
||||
|
||||
constructor(
|
||||
private game: GameView,
|
||||
@@ -135,6 +136,23 @@ export class PlayerView {
|
||||
this.anonymousName = createRandomName(data.name!, data.playerType!);
|
||||
}
|
||||
|
||||
this.computeColors();
|
||||
|
||||
const pattern = userSettings.territoryPatterns()
|
||||
? this.cosmetics.pattern
|
||||
: undefined;
|
||||
this.decoder =
|
||||
pattern === undefined
|
||||
? undefined
|
||||
: new PatternDecoder(pattern, base64url.decode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute every theme-derived color (fill, border, structure, and the
|
||||
* neutral/friendly/embargo border variants) from the active theme. Re-callable
|
||||
* so a mid-game theme change — e.g. toggling colorblind mode — can refresh them.
|
||||
*/
|
||||
private computeColors(): void {
|
||||
const theme = themeProvider.current();
|
||||
|
||||
const defaultTerritoryColor = theme.territoryColor(this);
|
||||
@@ -164,7 +182,7 @@ export class PlayerView {
|
||||
this._structureColors = theme.structureColors(this._territoryColor);
|
||||
|
||||
const maybeFocusedBorderColor =
|
||||
this.game.myClientID() === data.clientID
|
||||
this.game.myClientID() === this.static.clientID
|
||||
? theme.focusedBorderColor()
|
||||
: defaultBorderColor;
|
||||
|
||||
@@ -230,11 +248,11 @@ export class PlayerView {
|
||||
this._borderColorDefendedEmbargo = theme.defendedBorderColors(
|
||||
this._borderColorEmbargo,
|
||||
);
|
||||
}
|
||||
|
||||
this.decoder =
|
||||
pattern === undefined
|
||||
? undefined
|
||||
: new PatternDecoder(pattern, base64url.decode);
|
||||
/** Recompute colors after the active theme changes (e.g. colorblind toggle). */
|
||||
refreshColors(): void {
|
||||
this.computeColors();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user