// Player preferences: purely client-side taste, saved on this device. // Nothing here touches the rules — a preference may change what you SEE // or what routine commands the client sends on your behalf, never what // is legal. export interface Prefs { /** Token art: the photographed cardboard, or the hand-drawn vectors. */ art: "photo" | "drawn"; /** Standing on lone grabbable gold at end of turn: pick it up first. */ autoGrab: boolean; /** Spell flourishes (the animated effects layer). */ flourishes: boolean; /** Pre-filled wizard name for creating and joining games. */ wizardName: string; /** Preferred wizard color (0-5), claimed in lobbies when free. */ color: number | null; /** "Are you sure?" prompts before easy-to-regret plays. */ cautions: boolean; /** Default skill for automatons seated from the lobby. */ botTier: "apprentice" | "adept" | "archmage"; /** Instant replay: notable chronicle lines offer a first-person reel. */ instantReplay: boolean; /** The live first-person pane above the board during play. */ liveFp: boolean; /** The hand of cards stands in a column at the board's left instead of * lying along the bottom (wide screens only). */ handLeft: boolean; /** The hand's side was picked by hand; until then a wide, short screen * stands the cards beside the board on its own. */ handLeftChosen: boolean; /** The first-turn coaching slip has been read and dismissed. */ coached: boolean; /** The automaton tier was picked by hand (so a finished game leaves it alone). */ tierChosen: boolean; /** Games this browser has seen through to a winner. */ finishedGames: number; /** Keep the step light on every turn: your square and the squares a * stride reaches glow, the rest of the maze in shadow. A browser's * first game lights its first turn on its own. */ stepLightAlways: boolean; } const KEY = "wizwar-prefs"; function load(): Prefs { const fallback: Prefs = { art: "photo", autoGrab: false, flourishes: true, wizardName: "", color: null, cautions: true, botTier: "apprentice", instantReplay: true, liveFp: false, handLeft: false, handLeftChosen: false, coached: false, tierChosen: false, finishedGames: 0, stepLightAlways: false, }; try { const raw = localStorage.getItem(KEY); if (!raw) return fallback; const p = JSON.parse(raw) as Partial; return { art: p.art === "drawn" ? "drawn" : "photo", autoGrab: p.autoGrab === true, flourishes: p.flourishes !== false, wizardName: typeof p.wizardName === "string" ? p.wizardName.slice(0, 20) : "", color: typeof p.color === "number" && p.color >= 0 && p.color <= 5 ? p.color : null, cautions: p.cautions !== false, botTier: p.botTier === "adept" || p.botTier === "archmage" ? p.botTier : "apprentice", instantReplay: p.instantReplay !== false, liveFp: p.liveFp === true, handLeft: p.handLeft === true, handLeftChosen: p.handLeftChosen === true || p.handLeft === true, coached: p.coached === true, tierChosen: p.tierChosen === true, finishedGames: typeof p.finishedGames === "number" ? p.finishedGames : 0, stepLightAlways: p.stepLightAlways === true, }; } catch { return fallback; } } export const prefs = $state(load()); export function savePrefs(): void { try { localStorage.setItem(KEY, JSON.stringify(prefs)); } catch { // A full or blocked localStorage loses persistence, not the session. } }