mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-30 11:32:31 +00:00
269 lines
7.0 KiB
TypeScript
269 lines
7.0 KiB
TypeScript
import { Cosmetics } from "../CosmeticSchemas";
|
|
import { PlayerPattern } from "../Schemas";
|
|
|
|
const PATTERN_KEY = "territoryPattern";
|
|
|
|
export class UserSettings {
|
|
private emitChange(key: string, value: any): void {
|
|
try {
|
|
const maybeDispatch = (globalThis as any)?.dispatchEvent;
|
|
if (typeof maybeDispatch !== "function") return;
|
|
(globalThis as any).dispatchEvent(
|
|
new CustomEvent(`event:user-settings-changed:${key}`, {
|
|
detail: value,
|
|
}),
|
|
);
|
|
} catch {
|
|
// Ignore - settings should still be applied even if event dispatch fails.
|
|
}
|
|
}
|
|
|
|
get(key: string, defaultValue: boolean): boolean {
|
|
const value = localStorage.getItem(key);
|
|
if (!value) return defaultValue;
|
|
|
|
if (value === "true") return true;
|
|
|
|
if (value === "false") return false;
|
|
|
|
return defaultValue;
|
|
}
|
|
|
|
set(key: string, value: boolean) {
|
|
localStorage.setItem(key, value ? "true" : "false");
|
|
this.emitChange(key, value);
|
|
}
|
|
|
|
getFloat(key: string, defaultValue: number): number {
|
|
const value = localStorage.getItem(key);
|
|
if (!value) return defaultValue;
|
|
|
|
const floatValue = parseFloat(value);
|
|
if (isNaN(floatValue)) return defaultValue;
|
|
|
|
return floatValue;
|
|
}
|
|
|
|
setFloat(key: string, value: number) {
|
|
localStorage.setItem(key, value.toString());
|
|
this.emitChange(key, value);
|
|
}
|
|
|
|
emojis() {
|
|
return this.get("settings.emojis", true);
|
|
}
|
|
|
|
performanceOverlay() {
|
|
return this.get("settings.performanceOverlay", false);
|
|
}
|
|
|
|
alertFrame() {
|
|
return this.get("settings.alertFrame", true);
|
|
}
|
|
|
|
anonymousNames() {
|
|
return this.get("settings.anonymousNames", false);
|
|
}
|
|
|
|
lobbyIdVisibility() {
|
|
return this.get("settings.lobbyIdVisibility", true);
|
|
}
|
|
|
|
fxLayer() {
|
|
return this.get("settings.specialEffects", true);
|
|
}
|
|
|
|
structureSprites() {
|
|
return this.get("settings.structureSprites", true);
|
|
}
|
|
|
|
darkMode() {
|
|
return this.get("settings.darkMode", false);
|
|
}
|
|
|
|
leftClickOpensMenu() {
|
|
return this.get("settings.leftClickOpensMenu", false);
|
|
}
|
|
|
|
territoryPatterns() {
|
|
return this.get("settings.territoryPatterns", true);
|
|
}
|
|
|
|
attackingTroopsOverlay() {
|
|
return this.get("settings.attackingTroopsOverlay", true);
|
|
}
|
|
|
|
toggleAttackingTroopsOverlay() {
|
|
this.set("settings.attackingTroopsOverlay", !this.attackingTroopsOverlay());
|
|
}
|
|
|
|
cursorCostLabel() {
|
|
const legacy = this.get("settings.ghostPricePill", true);
|
|
return this.get("settings.cursorCostLabel", legacy);
|
|
}
|
|
|
|
focusLocked() {
|
|
return false;
|
|
// TODO: re-enable when performance issues are fixed.
|
|
this.get("settings.focusLocked", true);
|
|
}
|
|
|
|
toggleLeftClickOpenMenu() {
|
|
this.set("settings.leftClickOpensMenu", !this.leftClickOpensMenu());
|
|
}
|
|
|
|
toggleFocusLocked() {
|
|
this.set("settings.focusLocked", !this.focusLocked());
|
|
}
|
|
|
|
toggleEmojis() {
|
|
this.set("settings.emojis", !this.emojis());
|
|
}
|
|
|
|
togglePerformanceOverlay() {
|
|
this.set("settings.performanceOverlay", !this.performanceOverlay());
|
|
}
|
|
|
|
toggleAlertFrame() {
|
|
this.set("settings.alertFrame", !this.alertFrame());
|
|
}
|
|
|
|
toggleRandomName() {
|
|
this.set("settings.anonymousNames", !this.anonymousNames());
|
|
}
|
|
|
|
toggleLobbyIdVisibility() {
|
|
this.set("settings.lobbyIdVisibility", !this.lobbyIdVisibility());
|
|
}
|
|
|
|
toggleFxLayer() {
|
|
this.set("settings.specialEffects", !this.fxLayer());
|
|
}
|
|
|
|
toggleStructureSprites() {
|
|
this.set("settings.structureSprites", !this.structureSprites());
|
|
}
|
|
|
|
toggleCursorCostLabel() {
|
|
this.set("settings.cursorCostLabel", !this.cursorCostLabel());
|
|
}
|
|
|
|
toggleTerritoryPatterns() {
|
|
this.set("settings.territoryPatterns", !this.territoryPatterns());
|
|
}
|
|
|
|
toggleDarkMode() {
|
|
this.set("settings.darkMode", !this.darkMode());
|
|
if (this.darkMode()) {
|
|
document.documentElement.classList.add("dark");
|
|
} else {
|
|
document.documentElement.classList.remove("dark");
|
|
}
|
|
}
|
|
|
|
// For development only. Used for testing patterns, set in the console manually.
|
|
getDevOnlyPattern(): PlayerPattern | undefined {
|
|
const data = localStorage.getItem("dev-pattern") ?? undefined;
|
|
if (data === undefined) return undefined;
|
|
return {
|
|
name: "dev-pattern",
|
|
patternData: data,
|
|
colorPalette: {
|
|
name: "dev-color-palette",
|
|
primaryColor: localStorage.getItem("dev-primary") ?? "#ffffff",
|
|
secondaryColor: localStorage.getItem("dev-secondary") ?? "#000000",
|
|
},
|
|
} satisfies PlayerPattern;
|
|
}
|
|
|
|
getSelectedPatternName(cosmetics: Cosmetics | null): PlayerPattern | null {
|
|
if (cosmetics === null) return null;
|
|
let data = localStorage.getItem(PATTERN_KEY) ?? null;
|
|
if (data === null) return null;
|
|
const patternPrefix = "pattern:";
|
|
if (data.startsWith(patternPrefix)) {
|
|
data = data.slice(patternPrefix.length);
|
|
}
|
|
const [patternName, colorPalette] = data.split(":");
|
|
const pattern = cosmetics.patterns[patternName];
|
|
if (pattern === undefined) return null;
|
|
return {
|
|
name: patternName,
|
|
patternData: pattern.pattern,
|
|
colorPalette: cosmetics.colorPalettes?.[colorPalette],
|
|
} satisfies PlayerPattern;
|
|
}
|
|
|
|
setSelectedPatternName(patternName: string | undefined): void {
|
|
if (patternName === undefined) {
|
|
localStorage.removeItem(PATTERN_KEY);
|
|
} else {
|
|
localStorage.setItem(PATTERN_KEY, patternName);
|
|
}
|
|
this.emitChange("pattern", patternName);
|
|
}
|
|
|
|
getSelectedColor(): string | undefined {
|
|
const data = localStorage.getItem("settings.territoryColor") ?? undefined;
|
|
if (data === undefined) return undefined;
|
|
return data;
|
|
}
|
|
|
|
setSelectedColor(color: string | undefined): void {
|
|
if (color === undefined) {
|
|
localStorage.removeItem("settings.territoryColor");
|
|
} else {
|
|
localStorage.setItem("settings.territoryColor", color);
|
|
}
|
|
}
|
|
|
|
getFlag(): string | null {
|
|
let flag = localStorage.getItem("flag");
|
|
if (!flag) return null;
|
|
// Migrate bare country codes to country: prefix
|
|
if (!flag.startsWith("flag:") && !flag.startsWith("country:")) {
|
|
flag = `country:${flag}`;
|
|
localStorage.setItem("flag", flag);
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
setFlag(flag: string): void {
|
|
if (flag === "country:xx") {
|
|
this.clearFlag();
|
|
} else {
|
|
localStorage.setItem("flag", flag);
|
|
}
|
|
console.log("emitting change!");
|
|
this.emitChange("flag", flag);
|
|
}
|
|
|
|
clearFlag(): void {
|
|
localStorage.removeItem("flag");
|
|
}
|
|
|
|
backgroundMusicVolume(): number {
|
|
return this.getFloat("settings.backgroundMusicVolume", 0);
|
|
}
|
|
|
|
setBackgroundMusicVolume(volume: number): void {
|
|
this.setFloat("settings.backgroundMusicVolume", volume);
|
|
}
|
|
|
|
attackRatioIncrement(): number {
|
|
const increment = Math.round(
|
|
this.getFloat("settings.attackRatioIncrement", 10),
|
|
);
|
|
if (!Number.isFinite(increment) || increment <= 0) return 10;
|
|
return increment;
|
|
}
|
|
|
|
soundEffectsVolume(): number {
|
|
return this.getFloat("settings.soundEffectsVolume", 1);
|
|
}
|
|
|
|
setSoundEffectsVolume(volume: number): void {
|
|
this.setFloat("settings.soundEffectsVolume", volume);
|
|
}
|
|
}
|