Files
wizwar6e/packages/web/src/prefs.svelte.ts
T
Eric WagonerandClaude Fable 5 d334b267a2 The hand may stand at the board's left (preference)
A player asked for cards down the side instead of along the bottom.
With the new pref on (wide screens only), the hand claims a sticky,
scrolling column at the left while the hint strips and action buttons
stay beneath the board — the table-edge dissolves into the grid so its
children place themselves. The strip moves inside the .game grid to
make that possible; the default bottom layout spans the full row as
before, and phones keep their one-column flow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015RCWSTnb1KYTPyL4GmhGnF
2026-08-29 23:28:00 -04:00

67 lines
2.4 KiB
TypeScript

// 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;
}
const KEY = "wizwar-prefs";
function load(): Prefs {
const fallback: Prefs = {
art: "photo", autoGrab: false, flourishes: true, wizardName: "", color: null,
cautions: true, botTier: "adept", instantReplay: true, liveFp: false, handLeft: false,
};
try {
const raw = localStorage.getItem(KEY);
if (!raw) return fallback;
const p = JSON.parse(raw) as Partial<Prefs>;
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 === "apprentice" || p.botTier === "archmage" ? p.botTier : "adept",
instantReplay: p.instantReplay !== false,
liveFp: p.liveFp === true,
handLeft: p.handLeft === true,
};
} catch {
return fallback;
}
}
export const prefs = $state<Prefs>(load());
export function savePrefs(): void {
try {
localStorage.setItem(KEY, JSON.stringify(prefs));
} catch {
// A full or blocked localStorage loses persistence, not the session.
}
}