mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:20:47 +00:00
Feature/Move theme system from core to client-side ThemeProvider (#4108)
**Add approved & assigned issue number here:** Resolves #2549 ## Description: Themes are purely for the client's rendering, and the server doesn't need context on them. This PR moves `Theme.ts` from `src/core/configuration` to `src/client/theme` and moves affiliation colors to `render-settings.json`. This is to support the ability to add additional themes more quickly, such as colorblind-friendly themes. No visible changes occur from this refactor. ## 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 --------- Co-authored-by: Josh Harris <josh@wickedsick.com>
This commit is contained in:
@@ -75,6 +75,7 @@ import {
|
|||||||
} from "./render/gl";
|
} from "./render/gl";
|
||||||
import { ALL_UNIT_TYPES, UnitState } from "./render/types";
|
import { ALL_UNIT_TYPES, UnitState } from "./render/types";
|
||||||
import { SoundManager } from "./sound/SoundManager";
|
import { SoundManager } from "./sound/SoundManager";
|
||||||
|
import { themeProvider } from "./theme/ThemeProvider";
|
||||||
|
|
||||||
export interface LobbyConfig {
|
export interface LobbyConfig {
|
||||||
cosmetics: PlayerCosmeticRefs;
|
cosmetics: PlayerCosmeticRefs;
|
||||||
@@ -110,6 +111,7 @@ export function joinLobby(
|
|||||||
console.log(`joining lobby: gameID: ${lobbyConfig.gameID}`);
|
console.log(`joining lobby: gameID: ${lobbyConfig.gameID}`);
|
||||||
|
|
||||||
const userSettings: UserSettings = new UserSettings();
|
const userSettings: UserSettings = new UserSettings();
|
||||||
|
themeProvider.reset(); // fresh colour allocators for this game
|
||||||
startGame(lobbyConfig.gameID, lobbyConfig.gameStartInfo?.config ?? {});
|
startGame(lobbyConfig.gameID, lobbyConfig.gameStartInfo?.config ?? {});
|
||||||
|
|
||||||
const transport = new Transport(lobbyConfig, eventBus);
|
const transport = new Transport(lobbyConfig, eventBus);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { LitElement, html } from "lit";
|
import { LitElement, html } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators.js";
|
import { customElement, property, state } from "lit/decorators.js";
|
||||||
import { repeat } from "lit/directives/repeat.js";
|
import { repeat } from "lit/directives/repeat.js";
|
||||||
import { PastelTheme } from "../../core/configuration/PastelTheme";
|
|
||||||
import {
|
import {
|
||||||
ColoredTeams,
|
ColoredTeams,
|
||||||
Duos,
|
Duos,
|
||||||
@@ -17,6 +16,8 @@ import { assignTeamsLobbyPreview } from "../../core/game/TeamAssignment";
|
|||||||
import { UserSettings } from "../../core/game/UserSettings";
|
import { UserSettings } from "../../core/game/UserSettings";
|
||||||
import { ClientInfo, TeamCountConfig } from "../../core/Schemas";
|
import { ClientInfo, TeamCountConfig } from "../../core/Schemas";
|
||||||
import { createRandomName, formatPlayerDisplayName } from "../../core/Util";
|
import { createRandomName, formatPlayerDisplayName } from "../../core/Util";
|
||||||
|
import { Theme } from "../theme/Theme";
|
||||||
|
import { themeProvider } from "../theme/ThemeProvider";
|
||||||
import { getTranslatedPlayerTeamLabel, translateText } from "../Utils";
|
import { getTranslatedPlayerTeamLabel, translateText } from "../Utils";
|
||||||
|
|
||||||
export interface TeamPreviewData {
|
export interface TeamPreviewData {
|
||||||
@@ -37,7 +38,9 @@ export class LobbyTeamView extends LitElement {
|
|||||||
@property({ type: Number }) nationCount: number = 0;
|
@property({ type: Number }) nationCount: number = 0;
|
||||||
@property({ type: Boolean }) isPublicGame: boolean = false;
|
@property({ type: Boolean }) isPublicGame: boolean = false;
|
||||||
|
|
||||||
private theme: PastelTheme = new PastelTheme();
|
private get theme(): Theme {
|
||||||
|
return themeProvider.current();
|
||||||
|
}
|
||||||
@state() private showTeamColors: boolean = false;
|
@state() private showTeamColors: boolean = false;
|
||||||
private userSettings: UserSettings = new UserSettings();
|
private userSettings: UserSettings = new UserSettings();
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Colord } from "colord";
|
import { Colord } from "colord";
|
||||||
import { Theme } from "src/core/configuration/Theme";
|
|
||||||
import { assetUrl } from "../../core/AssetUrls";
|
import { assetUrl } from "../../core/AssetUrls";
|
||||||
import { TrainType, UnitType } from "../../core/game/Game";
|
import { TrainType, UnitType } from "../../core/game/Game";
|
||||||
import { UnitView } from "../../core/game/GameView";
|
import { UnitView } from "../../core/game/GameView";
|
||||||
|
import { Theme } from "../theme/Theme";
|
||||||
const atomBombSprite = assetUrl("sprites/atombomb.png");
|
const atomBombSprite = assetUrl("sprites/atombomb.png");
|
||||||
const hydrogenBombSprite = assetUrl("sprites/hydrogenbomb.png");
|
const hydrogenBombSprite = assetUrl("sprites/hydrogenbomb.png");
|
||||||
const mirvSprite = assetUrl("sprites/mirv2.png");
|
const mirvSprite = assetUrl("sprites/mirv2.png");
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
} from "../../../core/game/GameUpdates";
|
} from "../../../core/game/GameUpdates";
|
||||||
import { GameView, PlayerView, UnitView } from "../../../core/game/GameView";
|
import { GameView, PlayerView, UnitView } from "../../../core/game/GameView";
|
||||||
import { Controller } from "../../Controller";
|
import { Controller } from "../../Controller";
|
||||||
|
import { themeProvider } from "../../theme/ThemeProvider";
|
||||||
import {
|
import {
|
||||||
GoToPlayerEvent,
|
GoToPlayerEvent,
|
||||||
GoToPositionEvent,
|
GoToPositionEvent,
|
||||||
@@ -166,7 +167,7 @@ export class AttacksDisplay extends LitElement implements Controller {
|
|||||||
const cached = this.spriteDataURLCache.get(key);
|
const cached = this.spriteDataURLCache.get(key);
|
||||||
if (cached) return cached;
|
if (cached) return cached;
|
||||||
try {
|
try {
|
||||||
const canvas = getColoredSprite(unit, this.game.config().theme());
|
const canvas = getColoredSprite(unit, themeProvider.current());
|
||||||
const dataURL = canvas.toDataURL();
|
const dataURL = canvas.toDataURL();
|
||||||
this.spriteDataURLCache.set(key, dataURL);
|
this.spriteDataURLCache.set(key, dataURL);
|
||||||
return dataURL;
|
return dataURL;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { GameMode, Team } from "../../../core/game/Game";
|
|||||||
import { GameView } from "../../../core/game/GameView";
|
import { GameView } from "../../../core/game/GameView";
|
||||||
import { Controller } from "../../Controller";
|
import { Controller } from "../../Controller";
|
||||||
import { Platform } from "../../Platform";
|
import { Platform } from "../../Platform";
|
||||||
|
import { themeProvider } from "../../theme/ThemeProvider";
|
||||||
import { getTranslatedPlayerTeamLabel, translateText } from "../../Utils";
|
import { getTranslatedPlayerTeamLabel, translateText } from "../../Utils";
|
||||||
import { ImmunityBarVisibleEvent } from "./ImmunityTimer";
|
import { ImmunityBarVisibleEvent } from "./ImmunityTimer";
|
||||||
import { SpawnBarVisibleEvent } from "./SpawnTimer";
|
import { SpawnBarVisibleEvent } from "./SpawnTimer";
|
||||||
@@ -66,10 +67,7 @@ export class GameLeftSidebar extends LitElement implements Controller {
|
|||||||
if (!this.playerTeam && this.game.myPlayer()?.team()) {
|
if (!this.playerTeam && this.game.myPlayer()?.team()) {
|
||||||
this.playerTeam = this.game.myPlayer()!.team();
|
this.playerTeam = this.game.myPlayer()!.team();
|
||||||
if (this.playerTeam) {
|
if (this.playerTeam) {
|
||||||
this.playerColor = this.game
|
this.playerColor = themeProvider.current().teamColor(this.playerTeam);
|
||||||
.config()
|
|
||||||
.theme()
|
|
||||||
.teamColor(this.playerTeam);
|
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
MouseMoveEvent,
|
MouseMoveEvent,
|
||||||
TouchEvent,
|
TouchEvent,
|
||||||
} from "../../InputHandler";
|
} from "../../InputHandler";
|
||||||
|
import { themeProvider } from "../../theme/ThemeProvider";
|
||||||
import { TransformHandler } from "../../TransformHandler";
|
import { TransformHandler } from "../../TransformHandler";
|
||||||
import {
|
import {
|
||||||
getTranslatedPlayerTeamLabel,
|
getTranslatedPlayerTeamLabel,
|
||||||
@@ -360,9 +361,8 @@ export class PlayerInfoOverlay extends LitElement implements Controller {
|
|||||||
>
|
>
|
||||||
<span class="text-xs font-normal text-gray-400"
|
<span class="text-xs font-normal text-gray-400"
|
||||||
>[<span
|
>[<span
|
||||||
style="color: ${this.game
|
style="color: ${themeProvider
|
||||||
.config()
|
.current()
|
||||||
.theme()
|
|
||||||
.teamColor(player.team()!)
|
.teamColor(player.team()!)
|
||||||
.toHex()}"
|
.toHex()}"
|
||||||
>${playerTeam}</span
|
>${playerTeam}</span
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { EventBus, GameEvent } from "../../../core/EventBus";
|
|||||||
import { GameMode, GameType, Team } from "../../../core/game/Game";
|
import { GameMode, GameType, Team } from "../../../core/game/Game";
|
||||||
import { GameView } from "../../../core/game/GameView";
|
import { GameView } from "../../../core/game/GameView";
|
||||||
import { Controller } from "../../Controller";
|
import { Controller } from "../../Controller";
|
||||||
|
import { themeProvider } from "../../theme/ThemeProvider";
|
||||||
import { TransformHandler } from "../../TransformHandler";
|
import { TransformHandler } from "../../TransformHandler";
|
||||||
|
|
||||||
export class SpawnBarVisibleEvent implements GameEvent {
|
export class SpawnBarVisibleEvent implements GameEvent {
|
||||||
@@ -71,7 +72,7 @@ export class SpawnTimer extends LitElement implements Controller {
|
|||||||
teamTiles.set(team, tiles + player.numTilesOwned());
|
teamTiles.set(team, tiles + player.numTilesOwned());
|
||||||
}
|
}
|
||||||
|
|
||||||
const theme = this.game.config().theme();
|
const theme = themeProvider.current();
|
||||||
const total = sumIterator(teamTiles.values());
|
const total = sumIterator(teamTiles.values());
|
||||||
if (total > 0) {
|
if (total > 0) {
|
||||||
for (const [team, count] of teamTiles) {
|
for (const [team, count] of teamTiles) {
|
||||||
|
|||||||
@@ -79,6 +79,27 @@ export interface RenderSettings {
|
|||||||
defensePostRange: number;
|
defensePostRange: number;
|
||||||
embargoTintRatio: number;
|
embargoTintRatio: number;
|
||||||
friendlyTintRatio: number;
|
friendlyTintRatio: number;
|
||||||
|
embargoTintR: number;
|
||||||
|
embargoTintG: number;
|
||||||
|
embargoTintB: number;
|
||||||
|
friendlyTintR: number;
|
||||||
|
friendlyTintG: number;
|
||||||
|
friendlyTintB: number;
|
||||||
|
};
|
||||||
|
/** Alt-view affiliation colors (0–1 RGB). */
|
||||||
|
affiliation: {
|
||||||
|
selfR: number;
|
||||||
|
selfG: number;
|
||||||
|
selfB: number;
|
||||||
|
allyR: number;
|
||||||
|
allyG: number;
|
||||||
|
allyB: number;
|
||||||
|
neutralR: number;
|
||||||
|
neutralG: number;
|
||||||
|
neutralB: number;
|
||||||
|
enemyR: number;
|
||||||
|
enemyG: number;
|
||||||
|
enemyB: number;
|
||||||
};
|
};
|
||||||
railroad: {
|
railroad: {
|
||||||
railMinZoom: number;
|
railMinZoom: number;
|
||||||
|
|||||||
@@ -478,7 +478,7 @@ export class GPURenderer {
|
|||||||
this.sceneTarget = { fbo: sceneFbo, tex: sceneTex, w: 1, h: 1 };
|
this.sceneTarget = { fbo: sceneFbo, tex: sceneTex, w: 1, h: 1 };
|
||||||
|
|
||||||
// --- Alt-view passes ---
|
// --- Alt-view passes ---
|
||||||
this.affiliationPalette = new AffiliationPalette(gl);
|
this.affiliationPalette = new AffiliationPalette(gl, this.settings);
|
||||||
const affTex = this.affiliationPalette.getTexture();
|
const affTex = this.affiliationPalette.getTexture();
|
||||||
this.borderStampPass.setAffiliationTex(affTex);
|
this.borderStampPass.setAffiliationTex(affTex);
|
||||||
this.unitPass.setAffiliationTex(affTex);
|
this.unitPass.setAffiliationTex(affTex);
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ export class BorderStampPass {
|
|||||||
private uDefenseCheckerDarken: WebGLUniformLocation;
|
private uDefenseCheckerDarken: WebGLUniformLocation;
|
||||||
private uEmbargoTintRatio: WebGLUniformLocation;
|
private uEmbargoTintRatio: WebGLUniformLocation;
|
||||||
private uFriendlyTintRatio: WebGLUniformLocation;
|
private uFriendlyTintRatio: WebGLUniformLocation;
|
||||||
|
private uEmbargoTint: WebGLUniformLocation;
|
||||||
|
private uFriendlyTint: WebGLUniformLocation;
|
||||||
private uAltView: WebGLUniformLocation;
|
private uAltView: WebGLUniformLocation;
|
||||||
|
|
||||||
private vao: WebGLVertexArrayObject;
|
private vao: WebGLVertexArrayObject;
|
||||||
@@ -79,6 +81,8 @@ export class BorderStampPass {
|
|||||||
this.program,
|
this.program,
|
||||||
"uFriendlyTintRatio",
|
"uFriendlyTintRatio",
|
||||||
)!;
|
)!;
|
||||||
|
this.uEmbargoTint = gl.getUniformLocation(this.program, "uEmbargoTint")!;
|
||||||
|
this.uFriendlyTint = gl.getUniformLocation(this.program, "uFriendlyTint")!;
|
||||||
this.uAltView = gl.getUniformLocation(this.program, "uAltView")!;
|
this.uAltView = gl.getUniformLocation(this.program, "uAltView")!;
|
||||||
|
|
||||||
gl.useProgram(this.program);
|
gl.useProgram(this.program);
|
||||||
@@ -109,6 +113,18 @@ export class BorderStampPass {
|
|||||||
gl.uniform1f(this.uDefenseCheckerDarken, mo.defenseCheckerDarken);
|
gl.uniform1f(this.uDefenseCheckerDarken, mo.defenseCheckerDarken);
|
||||||
gl.uniform1f(this.uEmbargoTintRatio, mo.embargoTintRatio);
|
gl.uniform1f(this.uEmbargoTintRatio, mo.embargoTintRatio);
|
||||||
gl.uniform1f(this.uFriendlyTintRatio, mo.friendlyTintRatio);
|
gl.uniform1f(this.uFriendlyTintRatio, mo.friendlyTintRatio);
|
||||||
|
gl.uniform3f(
|
||||||
|
this.uEmbargoTint,
|
||||||
|
mo.embargoTintR,
|
||||||
|
mo.embargoTintG,
|
||||||
|
mo.embargoTintB,
|
||||||
|
);
|
||||||
|
gl.uniform3f(
|
||||||
|
this.uFriendlyTint,
|
||||||
|
mo.friendlyTintR,
|
||||||
|
mo.friendlyTintG,
|
||||||
|
mo.friendlyTintB,
|
||||||
|
);
|
||||||
gl.uniform1i(this.uAltView, this.altView ? 1 : 0);
|
gl.uniform1i(this.uAltView, this.altView ? 1 : 0);
|
||||||
|
|
||||||
gl.activeTexture(gl.TEXTURE0);
|
gl.activeTexture(gl.TEXTURE0);
|
||||||
|
|||||||
@@ -75,7 +75,27 @@
|
|||||||
"highlightThicken": 2,
|
"highlightThicken": 2,
|
||||||
"defensePostRange": 30,
|
"defensePostRange": 30,
|
||||||
"embargoTintRatio": 0.35,
|
"embargoTintRatio": 0.35,
|
||||||
"friendlyTintRatio": 0.35
|
"friendlyTintRatio": 0.35,
|
||||||
|
"embargoTintR": 1,
|
||||||
|
"embargoTintG": 0,
|
||||||
|
"embargoTintB": 0,
|
||||||
|
"friendlyTintR": 0,
|
||||||
|
"friendlyTintG": 1,
|
||||||
|
"friendlyTintB": 0
|
||||||
|
},
|
||||||
|
"affiliation": {
|
||||||
|
"selfR": 0,
|
||||||
|
"selfG": 1,
|
||||||
|
"selfB": 0,
|
||||||
|
"allyR": 1,
|
||||||
|
"allyG": 1,
|
||||||
|
"allyB": 0,
|
||||||
|
"neutralR": 0.502,
|
||||||
|
"neutralG": 0.502,
|
||||||
|
"neutralB": 0.502,
|
||||||
|
"enemyR": 1,
|
||||||
|
"enemyG": 0,
|
||||||
|
"enemyB": 0
|
||||||
},
|
},
|
||||||
"railroad": {
|
"railroad": {
|
||||||
"railMinZoom": 4,
|
"railMinZoom": 4,
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ uniform float uHighlightBrighten;
|
|||||||
uniform float uDefenseCheckerDarken;
|
uniform float uDefenseCheckerDarken;
|
||||||
uniform float uEmbargoTintRatio;
|
uniform float uEmbargoTintRatio;
|
||||||
uniform float uFriendlyTintRatio;
|
uniform float uFriendlyTintRatio;
|
||||||
|
uniform vec3 uEmbargoTint;
|
||||||
|
uniform vec3 uFriendlyTint;
|
||||||
|
|
||||||
in vec2 vWorldPos;
|
in vec2 vWorldPos;
|
||||||
out vec4 fragColor;
|
out vec4 fragColor;
|
||||||
@@ -46,9 +48,9 @@ void main() {
|
|||||||
}
|
}
|
||||||
// Relationship tint (applied BEFORE defense checkerboard, matching game)
|
// Relationship tint (applied BEFORE defense checkerboard, matching game)
|
||||||
if (relation > 0.75) {
|
if (relation > 0.75) {
|
||||||
bc = mix(bc, vec3(1.0, 0.0, 0.0), uEmbargoTintRatio);
|
bc = mix(bc, uEmbargoTint, uEmbargoTintRatio);
|
||||||
} else if (relation > 0.25) {
|
} else if (relation > 0.25) {
|
||||||
bc = mix(bc, vec3(0.0, 1.0, 0.0), uFriendlyTintRatio);
|
bc = mix(bc, uFriendlyTint, uFriendlyTintRatio);
|
||||||
}
|
}
|
||||||
// Defense bonus: checkerboard darken (applied AFTER tint, matching game)
|
// Defense bonus: checkerboard darken (applied AFTER tint, matching game)
|
||||||
if (defense) {
|
if (defense) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
* Rebuilt when localPlayerID or relationship data changes.
|
* Rebuilt when localPlayerID or relationship data changes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { RenderSettings } from "../RenderSettings";
|
||||||
import { getPaletteSize } from "./ColorUtils";
|
import { getPaletteSize } from "./ColorUtils";
|
||||||
import { createTexture2D } from "./GlUtils";
|
import { createTexture2D } from "./GlUtils";
|
||||||
|
|
||||||
@@ -16,20 +17,6 @@ const RELATION_NEUTRAL = 0;
|
|||||||
const RELATION_FRIENDLY = 1;
|
const RELATION_FRIENDLY = 1;
|
||||||
const RELATION_EMBARGO = 2;
|
const RELATION_EMBARGO = 2;
|
||||||
|
|
||||||
// Affiliation RGB values (upstream PastelTheme)
|
|
||||||
const SELF_R = 0,
|
|
||||||
SELF_G = 255,
|
|
||||||
SELF_B = 0;
|
|
||||||
const ALLY_R = 255,
|
|
||||||
ALLY_G = 255,
|
|
||||||
ALLY_B = 0;
|
|
||||||
const NEUTRAL_R = 128,
|
|
||||||
NEUTRAL_G = 128,
|
|
||||||
NEUTRAL_B = 128;
|
|
||||||
const ENEMY_R = 255,
|
|
||||||
ENEMY_G = 0,
|
|
||||||
ENEMY_B = 0;
|
|
||||||
|
|
||||||
const TEX_W = getPaletteSize(); // 4096 — covers full 12-bit smallID range
|
const TEX_W = getPaletteSize(); // 4096 — covers full 12-bit smallID range
|
||||||
const TEX_H = 2;
|
const TEX_H = 2;
|
||||||
|
|
||||||
@@ -44,7 +31,10 @@ export class AffiliationPalette {
|
|||||||
private relationData: Uint8Array | null = null;
|
private relationData: Uint8Array | null = null;
|
||||||
private relationSize = 0;
|
private relationSize = 0;
|
||||||
|
|
||||||
constructor(gl: WebGL2RenderingContext) {
|
constructor(
|
||||||
|
gl: WebGL2RenderingContext,
|
||||||
|
private settings: RenderSettings,
|
||||||
|
) {
|
||||||
this.gl = gl;
|
this.gl = gl;
|
||||||
this.rebuild(); // initialize to spectator-mode defaults (gray borders, red units)
|
this.rebuild(); // initialize to spectator-mode defaults (gray borders, red units)
|
||||||
this.tex = createTexture2D(gl, {
|
this.tex = createTexture2D(gl, {
|
||||||
@@ -100,6 +90,22 @@ export class AffiliationPalette {
|
|||||||
const rel = this.relationData;
|
const rel = this.relationData;
|
||||||
const rs = this.relationSize;
|
const rs = this.relationSize;
|
||||||
|
|
||||||
|
// Affiliation RGB values (0–1) from render-settings, expanded to 0–255.
|
||||||
|
const a = this.settings.affiliation;
|
||||||
|
const to255 = (v: number) => Math.round(v * 255);
|
||||||
|
const SELF_R = to255(a.selfR),
|
||||||
|
SELF_G = to255(a.selfG),
|
||||||
|
SELF_B = to255(a.selfB);
|
||||||
|
const ALLY_R = to255(a.allyR),
|
||||||
|
ALLY_G = to255(a.allyG),
|
||||||
|
ALLY_B = to255(a.allyB);
|
||||||
|
const NEUTRAL_R = to255(a.neutralR),
|
||||||
|
NEUTRAL_G = to255(a.neutralG),
|
||||||
|
NEUTRAL_B = to255(a.neutralB);
|
||||||
|
const ENEMY_R = to255(a.enemyR),
|
||||||
|
ENEMY_G = to255(a.enemyG),
|
||||||
|
ENEMY_B = to255(a.enemyB);
|
||||||
|
|
||||||
for (let owner = 0; owner < TEX_W; owner++) {
|
for (let owner = 0; owner < TEX_W; owner++) {
|
||||||
// Determine relationship
|
// Determine relationship
|
||||||
let relation = RELATION_NEUTRAL;
|
let relation = RELATION_NEUTRAL;
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import { colord, Colord, extend } from "colord";
|
|||||||
import labPlugin from "colord/plugins/lab";
|
import labPlugin from "colord/plugins/lab";
|
||||||
import lchPlugin from "colord/plugins/lch";
|
import lchPlugin from "colord/plugins/lch";
|
||||||
import Color from "colorjs.io";
|
import Color from "colorjs.io";
|
||||||
import { ColoredTeams, Team } from "../game/Game";
|
import { ColoredTeams, Team } from "../../core/game/Game";
|
||||||
import { PseudoRandom } from "../PseudoRandom";
|
import { PseudoRandom } from "../../core/PseudoRandom";
|
||||||
import { simpleHash } from "../Util";
|
import { simpleHash } from "../../core/Util";
|
||||||
import {
|
import {
|
||||||
blueTeamColors,
|
blueTeamColors,
|
||||||
botTeamColors,
|
botTeamColors,
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Colord, colord, LabaColor } from "colord";
|
import { Colord, colord, LabaColor } from "colord";
|
||||||
import { PseudoRandom } from "../PseudoRandom";
|
import { PseudoRandom } from "../../core/PseudoRandom";
|
||||||
import { PlayerType, Team, TerrainType } from "../game/Game";
|
import { PlayerType, Team, TerrainType } from "../../core/game/Game";
|
||||||
import { GameMap, TileRef } from "../game/GameMap";
|
import { GameMap, TileRef } from "../../core/game/GameMap";
|
||||||
import { PlayerView } from "../game/GameView";
|
import { PlayerView } from "../../core/game/GameView";
|
||||||
import { ColorAllocator } from "./ColorAllocator";
|
import { ColorAllocator } from "./ColorAllocator";
|
||||||
import { botColors, fallbackColors, humanColors, nationColors } from "./Colors";
|
import { botColors, fallbackColors, humanColors, nationColors } from "./Colors";
|
||||||
import { Theme } from "./Theme";
|
import { Theme } from "./Theme";
|
||||||
@@ -26,15 +26,6 @@ export class PastelTheme implements Theme {
|
|||||||
private water = colord("rgb(70,132,180)");
|
private water = colord("rgb(70,132,180)");
|
||||||
private shorelineWater = colord("rgb(100,143,255)");
|
private shorelineWater = colord("rgb(100,143,255)");
|
||||||
|
|
||||||
/** Alternate View colors for self, green */
|
|
||||||
private _selfColor = colord("rgb(0,255,0)");
|
|
||||||
/** Alternate View colors for allies, yellow */
|
|
||||||
private _allyColor = colord("rgb(255,255,0)");
|
|
||||||
/** Alternate View colors for neutral, gray */
|
|
||||||
private _neutralColor = colord("rgb(128,128,128)");
|
|
||||||
/** Alternate View colors for enemies, red */
|
|
||||||
private _enemyColor = colord("rgb(255,0,0)");
|
|
||||||
|
|
||||||
/** Default spawn highlight colors for other players in FFA, yellow */
|
/** Default spawn highlight colors for other players in FFA, yellow */
|
||||||
private _spawnHighlightColor = colord("rgb(255,213,79)");
|
private _spawnHighlightColor = colord("rgb(255,213,79)");
|
||||||
/** Added non-default spawn highlight colors for self, full white */
|
/** Added non-default spawn highlight colors for self, full white */
|
||||||
@@ -197,19 +188,6 @@ export class PastelTheme implements Theme {
|
|||||||
return "Overpass, sans-serif";
|
return "Overpass, sans-serif";
|
||||||
}
|
}
|
||||||
|
|
||||||
selfColor(): Colord {
|
|
||||||
return this._selfColor;
|
|
||||||
}
|
|
||||||
allyColor(): Colord {
|
|
||||||
return this._allyColor;
|
|
||||||
}
|
|
||||||
neutralColor(): Colord {
|
|
||||||
return this._neutralColor;
|
|
||||||
}
|
|
||||||
enemyColor(): Colord {
|
|
||||||
return this._enemyColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
spawnHighlightColor(): Colord {
|
spawnHighlightColor(): Colord {
|
||||||
return this._spawnHighlightColor;
|
return this._spawnHighlightColor;
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Colord, colord } from "colord";
|
import { Colord, colord } from "colord";
|
||||||
import { TerrainType } from "../game/Game";
|
import { TerrainType } from "../../core/game/Game";
|
||||||
import { GameMap, TileRef } from "../game/GameMap";
|
import { GameMap, TileRef } from "../../core/game/GameMap";
|
||||||
import { PastelTheme } from "./PastelTheme";
|
import { PastelTheme } from "./PastelTheme";
|
||||||
|
|
||||||
export class PastelThemeDark extends PastelTheme {
|
export class PastelThemeDark extends PastelTheme {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Colord } from "colord";
|
import { Colord } from "colord";
|
||||||
import { Team } from "../game/Game";
|
import { Team } from "../../core/game/Game";
|
||||||
import { GameMap, TileRef } from "../game/GameMap";
|
import { GameMap, TileRef } from "../../core/game/GameMap";
|
||||||
import { PlayerView } from "../game/GameView";
|
import { PlayerView } from "../../core/game/GameView";
|
||||||
|
|
||||||
export interface Theme {
|
export interface Theme {
|
||||||
teamColor(team: Team): Colord;
|
teamColor(team: Team): Colord;
|
||||||
@@ -19,11 +19,6 @@ export interface Theme {
|
|||||||
falloutColor(): Colord;
|
falloutColor(): Colord;
|
||||||
font(): string;
|
font(): string;
|
||||||
textColor(playerInfo: PlayerView): string;
|
textColor(playerInfo: PlayerView): string;
|
||||||
// unit color for alternate view
|
|
||||||
selfColor(): Colord;
|
|
||||||
allyColor(): Colord;
|
|
||||||
neutralColor(): Colord;
|
|
||||||
enemyColor(): Colord;
|
|
||||||
spawnHighlightColor(): Colord;
|
spawnHighlightColor(): Colord;
|
||||||
spawnHighlightSelfColor(): Colord;
|
spawnHighlightSelfColor(): Colord;
|
||||||
spawnHighlightTeamColor(): Colord;
|
spawnHighlightTeamColor(): Colord;
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { UserSettings } from "../../core/game/UserSettings";
|
||||||
|
import { PastelTheme } from "./PastelTheme";
|
||||||
|
import { PastelThemeDark } from "./PastelThemeDark";
|
||||||
|
import { Theme } from "./Theme";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client-side source of truth for the active theme. Themes were moved out of
|
||||||
|
* `src/core` (the simulation never reads colors); this singleton replaces the
|
||||||
|
* old `Config.theme()` accessor.
|
||||||
|
*/
|
||||||
|
class ThemeProvider {
|
||||||
|
private readonly userSettings = new UserSettings();
|
||||||
|
private light = new PastelTheme();
|
||||||
|
private dark = new PastelThemeDark();
|
||||||
|
|
||||||
|
/** The active theme, selected from the user's dark-mode preference. */
|
||||||
|
current(): Theme {
|
||||||
|
return this.userSettings.darkMode() ? this.dark : this.light;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recreate the themes so their colour allocators start empty. Call once per
|
||||||
|
* game — matches the previous per-`Config` theme lifecycle and prevents
|
||||||
|
* colour-pool depletion across games in a single session.
|
||||||
|
*/
|
||||||
|
reset(): void {
|
||||||
|
this.light = new PastelTheme();
|
||||||
|
this.dark = new PastelThemeDark();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const themeProvider = new ThemeProvider();
|
||||||
@@ -29,6 +29,7 @@ import {
|
|||||||
} from "../../core/game/GameUpdates";
|
} from "../../core/game/GameUpdates";
|
||||||
import { UserSettings } from "../../core/game/UserSettings";
|
import { UserSettings } from "../../core/game/UserSettings";
|
||||||
import { PlayerState, PlayerStatic, PlayerTypeEnum } from "../render/types";
|
import { PlayerState, PlayerStatic, PlayerTypeEnum } from "../render/types";
|
||||||
|
import { themeProvider } from "../theme/ThemeProvider";
|
||||||
import { GameView } from "./GameView";
|
import { GameView } from "./GameView";
|
||||||
import { UnitView } from "./UnitView";
|
import { UnitView } from "./UnitView";
|
||||||
|
|
||||||
@@ -133,7 +134,7 @@ export class PlayerView {
|
|||||||
this.anonymousName = createRandomName(data.name!, data.playerType!);
|
this.anonymousName = createRandomName(data.name!, data.playerType!);
|
||||||
}
|
}
|
||||||
|
|
||||||
const theme = this.game.config().theme();
|
const theme = themeProvider.current();
|
||||||
|
|
||||||
const defaultTerritoryColor = theme.territoryColor(this);
|
const defaultTerritoryColor = theme.territoryColor(this);
|
||||||
const defaultBorderColor = theme.borderColor(defaultTerritoryColor);
|
const defaultBorderColor = theme.borderColor(defaultTerritoryColor);
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ import { UserSettings } from "../game/UserSettings";
|
|||||||
import { GameConfig, TeamCountConfig } from "../Schemas";
|
import { GameConfig, TeamCountConfig } from "../Schemas";
|
||||||
import { NukeType } from "../StatsSchemas";
|
import { NukeType } from "../StatsSchemas";
|
||||||
import { assertNever, sigmoid, toInt, within } from "../Util";
|
import { assertNever, sigmoid, toInt, within } from "../Util";
|
||||||
import { PastelTheme } from "./PastelTheme";
|
|
||||||
import { PastelThemeDark } from "./PastelThemeDark";
|
|
||||||
import { Theme } from "./Theme";
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@@ -84,8 +81,6 @@ export const JwksSchema = z.object({
|
|||||||
export const SAM_CONSTRUCTION_TICKS = 30 * 10;
|
export const SAM_CONSTRUCTION_TICKS = 30 * 10;
|
||||||
|
|
||||||
export class Config {
|
export class Config {
|
||||||
private pastelTheme: PastelTheme = new PastelTheme();
|
|
||||||
private pastelThemeDark: PastelThemeDark = new PastelThemeDark();
|
|
||||||
private unitInfoCache = new Map<UnitType, UnitInfo>();
|
private unitInfoCache = new Map<UnitType, UnitInfo>();
|
||||||
constructor(
|
constructor(
|
||||||
private _gameConfig: GameConfig,
|
private _gameConfig: GameConfig,
|
||||||
@@ -562,11 +557,6 @@ export class Config {
|
|||||||
numBots(): number {
|
numBots(): number {
|
||||||
return this.bots();
|
return this.bots();
|
||||||
}
|
}
|
||||||
theme(): Theme {
|
|
||||||
return this.userSettings()?.darkMode()
|
|
||||||
? this.pastelThemeDark
|
|
||||||
: this.pastelTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
attackLogic(
|
attackLogic(
|
||||||
gm: Game,
|
gm: Game,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { colord, Colord } from "colord";
|
|||||||
import {
|
import {
|
||||||
ColorAllocator,
|
ColorAllocator,
|
||||||
selectDistinctColorIndex,
|
selectDistinctColorIndex,
|
||||||
} from "../src/core/configuration/ColorAllocator";
|
} from "../src/client/theme/ColorAllocator";
|
||||||
import {
|
import {
|
||||||
blue,
|
blue,
|
||||||
botColor,
|
botColor,
|
||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
red,
|
red,
|
||||||
teal,
|
teal,
|
||||||
yellow,
|
yellow,
|
||||||
} from "../src/core/configuration/Colors";
|
} from "../src/client/theme/Colors";
|
||||||
import { ColoredTeams } from "../src/core/game/Game";
|
import { ColoredTeams } from "../src/core/game/Game";
|
||||||
|
|
||||||
const mockColors: Colord[] = [
|
const mockColors: Colord[] = [
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { colord } from "colord";
|
import { colord } from "colord";
|
||||||
|
import { Theme } from "../../src/client/theme/Theme";
|
||||||
import { GameView } from "../../src/client/view/GameView";
|
import { GameView } from "../../src/client/view/GameView";
|
||||||
import { PlayerView } from "../../src/client/view/PlayerView";
|
import { PlayerView } from "../../src/client/view/PlayerView";
|
||||||
import { Config } from "../../src/core/configuration/Config";
|
import { Config } from "../../src/core/configuration/Config";
|
||||||
import { Theme } from "../../src/core/configuration/Theme";
|
|
||||||
import {
|
import {
|
||||||
NameViewData,
|
NameViewData,
|
||||||
PlayerType,
|
PlayerType,
|
||||||
@@ -45,10 +45,6 @@ export function stubTheme(): Theme {
|
|||||||
falloutColor: () => white,
|
falloutColor: () => white,
|
||||||
font: () => "Arial",
|
font: () => "Arial",
|
||||||
textColor: () => "#000000",
|
textColor: () => "#000000",
|
||||||
selfColor: () => white,
|
|
||||||
allyColor: () => white,
|
|
||||||
neutralColor: () => grey,
|
|
||||||
enemyColor: () => grey,
|
|
||||||
spawnHighlightColor: () => white,
|
spawnHighlightColor: () => white,
|
||||||
spawnHighlightSelfColor: () => white,
|
spawnHighlightSelfColor: () => white,
|
||||||
spawnHighlightTeamColor: () => white,
|
spawnHighlightTeamColor: () => white,
|
||||||
|
|||||||
Reference in New Issue
Block a user