Name and color join the preferences
Your wizard's name pre-fills the lobby (and is remembered whenever you create or join), and a preferred color — six swatches in the slip, tap again for none — is claimed quietly in any lobby where it is free and you have not chosen. First-come still wins a contested color; the preference never bumps anyone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138A8CjeQRpvzKxuMfz1Bqc
This commit is contained in:
co-authored by
Claude Fable 5
parent
62c4680d1f
commit
b7f93fe454
@@ -18,7 +18,7 @@
|
||||
net.connect();
|
||||
net.startGamePolling();
|
||||
|
||||
let name = $state("");
|
||||
let name = $state(prefs.wizardName);
|
||||
let joinCode = $state("");
|
||||
let claimPhrase = $state("");
|
||||
let showHelp = $state(false);
|
||||
@@ -953,6 +953,15 @@
|
||||
|
||||
/** The preferences slip (client-side taste, saved on this device). */
|
||||
let prefsOpen = $state(false);
|
||||
|
||||
// In a lobby before the deal: claim the preferred color when it is free
|
||||
// and this seat has not chosen. One quiet attempt per availability change.
|
||||
$effect(() => {
|
||||
if (view || !net.roomId || !net.you || prefs.color == null) return;
|
||||
if (net.roomColors[net.you] !== undefined) return;
|
||||
const takenBy = Object.entries(net.roomColors).find(([, v]) => v === prefs.color)?.[0];
|
||||
if (takenBy === undefined) net.pickColor(prefs.color);
|
||||
});
|
||||
function setPref<K extends keyof typeof prefs>(k: K, v: (typeof prefs)[K]) {
|
||||
prefs[k] = v;
|
||||
savePrefs();
|
||||
@@ -1220,6 +1229,21 @@
|
||||
onchange={(e) => setPref("flourishes", e.currentTarget.checked)} />
|
||||
<span>Spell flourishes (the animated effects)</span>
|
||||
</label>
|
||||
<label class="pref-row">
|
||||
<span>Wizard name</span>
|
||||
<input class="setup-input pref-name" maxlength="20" value={prefs.wizardName}
|
||||
placeholder="e.g. Mordecai"
|
||||
onchange={(e) => { setPref("wizardName", e.currentTarget.value.trim()); name = prefs.wizardName; }} />
|
||||
</label>
|
||||
<div class="pref-row">
|
||||
<span>Preferred color</span>
|
||||
{#each [0, 1, 2, 3, 4, 5] as c (c)}
|
||||
<button class="pref-swatch" class:current={prefs.color === c}
|
||||
style:background={PLAYER_COLORS[c]}
|
||||
aria-label={`prefer wizard color ${c + 1}`}
|
||||
onclick={() => setPref("color", prefs.color === c ? null : c)}></button>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="pref-note">Saved on this device. Rules are never affected.</div>
|
||||
<div class="attack-actions">
|
||||
<button class="hint-cancel" onclick={() => (prefsOpen = false)}>done</button>
|
||||
@@ -1450,12 +1474,12 @@
|
||||
<input bind:value={name} maxlength="20" placeholder="e.g. Mordecai" />
|
||||
</label>
|
||||
<div class="boxlid-actions">
|
||||
<button class="stamp" disabled={!name.trim()} onclick={() => net.create(name)}>
|
||||
<button class="stamp" disabled={!name.trim()} onclick={() => { setPref("wizardName", name.trim()); net.create(name); }}>
|
||||
Create a game
|
||||
</button>
|
||||
<span class="or">or join one</span>
|
||||
<input class="code" bind:value={joinCode} maxlength="4" placeholder="CODE" />
|
||||
<button class="stamp" disabled={!name.trim() || !joinCode.trim()} onclick={() => net.join(joinCode, name)}>
|
||||
<button class="stamp" disabled={!name.trim() || !joinCode.trim()} onclick={() => { setPref("wizardName", name.trim()); net.join(joinCode, name); }}>
|
||||
Join
|
||||
</button>
|
||||
</div>
|
||||
@@ -2452,6 +2476,16 @@
|
||||
color: #43331f;
|
||||
}
|
||||
.pref-note { font-size: 0.72rem; color: #8a7a5e; margin-top: 0.6rem; }
|
||||
.pref-name { max-width: 11rem; }
|
||||
.pref-swatch {
|
||||
width: 1.3rem;
|
||||
height: 1.3rem;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #43331f;
|
||||
cursor: pointer;
|
||||
opacity: 0.55;
|
||||
}
|
||||
.pref-swatch.current { opacity: 1; outline: 2px solid #d8b23a; outline-offset: 1px; }
|
||||
.slip.council { background: #ece4f1; border-color: #7a6f9a; }
|
||||
.council-head {
|
||||
font-weight: 700;
|
||||
|
||||
@@ -10,12 +10,16 @@ export interface Prefs {
|
||||
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;
|
||||
}
|
||||
|
||||
const KEY = "wizwar-prefs";
|
||||
|
||||
function load(): Prefs {
|
||||
const fallback: Prefs = { art: "photo", autoGrab: false, flourishes: true };
|
||||
const fallback: Prefs = { art: "photo", autoGrab: false, flourishes: true, wizardName: "", color: null };
|
||||
try {
|
||||
const raw = localStorage.getItem(KEY);
|
||||
if (!raw) return fallback;
|
||||
@@ -24,6 +28,8 @@ function load(): Prefs {
|
||||
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,
|
||||
};
|
||||
} catch {
|
||||
return fallback;
|
||||
|
||||
Reference in New Issue
Block a user