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.connect();
|
||||||
net.startGamePolling();
|
net.startGamePolling();
|
||||||
|
|
||||||
let name = $state("");
|
let name = $state(prefs.wizardName);
|
||||||
let joinCode = $state("");
|
let joinCode = $state("");
|
||||||
let claimPhrase = $state("");
|
let claimPhrase = $state("");
|
||||||
let showHelp = $state(false);
|
let showHelp = $state(false);
|
||||||
@@ -953,6 +953,15 @@
|
|||||||
|
|
||||||
/** The preferences slip (client-side taste, saved on this device). */
|
/** The preferences slip (client-side taste, saved on this device). */
|
||||||
let prefsOpen = $state(false);
|
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]) {
|
function setPref<K extends keyof typeof prefs>(k: K, v: (typeof prefs)[K]) {
|
||||||
prefs[k] = v;
|
prefs[k] = v;
|
||||||
savePrefs();
|
savePrefs();
|
||||||
@@ -1220,6 +1229,21 @@
|
|||||||
onchange={(e) => setPref("flourishes", e.currentTarget.checked)} />
|
onchange={(e) => setPref("flourishes", e.currentTarget.checked)} />
|
||||||
<span>Spell flourishes (the animated effects)</span>
|
<span>Spell flourishes (the animated effects)</span>
|
||||||
</label>
|
</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="pref-note">Saved on this device. Rules are never affected.</div>
|
||||||
<div class="attack-actions">
|
<div class="attack-actions">
|
||||||
<button class="hint-cancel" onclick={() => (prefsOpen = false)}>done</button>
|
<button class="hint-cancel" onclick={() => (prefsOpen = false)}>done</button>
|
||||||
@@ -1450,12 +1474,12 @@
|
|||||||
<input bind:value={name} maxlength="20" placeholder="e.g. Mordecai" />
|
<input bind:value={name} maxlength="20" placeholder="e.g. Mordecai" />
|
||||||
</label>
|
</label>
|
||||||
<div class="boxlid-actions">
|
<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
|
Create a game
|
||||||
</button>
|
</button>
|
||||||
<span class="or">or join one</span>
|
<span class="or">or join one</span>
|
||||||
<input class="code" bind:value={joinCode} maxlength="4" placeholder="CODE" />
|
<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
|
Join
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -2452,6 +2476,16 @@
|
|||||||
color: #43331f;
|
color: #43331f;
|
||||||
}
|
}
|
||||||
.pref-note { font-size: 0.72rem; color: #8a7a5e; margin-top: 0.6rem; }
|
.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; }
|
.slip.council { background: #ece4f1; border-color: #7a6f9a; }
|
||||||
.council-head {
|
.council-head {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
|||||||
@@ -10,12 +10,16 @@ export interface Prefs {
|
|||||||
autoGrab: boolean;
|
autoGrab: boolean;
|
||||||
/** Spell flourishes (the animated effects layer). */
|
/** Spell flourishes (the animated effects layer). */
|
||||||
flourishes: boolean;
|
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";
|
const KEY = "wizwar-prefs";
|
||||||
|
|
||||||
function load(): 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 {
|
try {
|
||||||
const raw = localStorage.getItem(KEY);
|
const raw = localStorage.getItem(KEY);
|
||||||
if (!raw) return fallback;
|
if (!raw) return fallback;
|
||||||
@@ -24,6 +28,8 @@ function load(): Prefs {
|
|||||||
art: p.art === "drawn" ? "drawn" : "photo",
|
art: p.art === "drawn" ? "drawn" : "photo",
|
||||||
autoGrab: p.autoGrab === true,
|
autoGrab: p.autoGrab === true,
|
||||||
flourishes: p.flourishes !== false,
|
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 {
|
} catch {
|
||||||
return fallback;
|
return fallback;
|
||||||
|
|||||||
Reference in New Issue
Block a user