Hotseat setup round: pick a count, then each wizard names themselves
The comma-separated name field is gone. Hotseat now starts with a 2-6 count picker and "Gather N wizards", which opens a naming round in the pass-the-device style: "Wizard 1 of 3 — what is your name?", type it, pass the device on; the last wizard's button reads "Flip the boards" and starts the game. Duplicate and empty names are refused in place, the roster-so-far shows beneath, and "never mind" backs out. Verified end to end: three names entered, game started, opening handoff went to the die-roll winner. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
ac82c28750
commit
98d7dee5c7
+65
-12
@@ -14,7 +14,8 @@
|
||||
let joinCode = $state("");
|
||||
let claimPhrase = $state("");
|
||||
let showHelp = $state(false);
|
||||
let hotseatNames = $state("");
|
||||
let hotseatCount = $state(2);
|
||||
let setupName = $state("");
|
||||
/** Tap-to-inspect: hide the enlarged card for the current selection. */
|
||||
let inspectorHidden = $state(false);
|
||||
$effect(() => {
|
||||
@@ -497,7 +498,31 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if local.handoffTo}
|
||||
{#if local.setup}
|
||||
<div class="scrim-handoff">
|
||||
<div class="handoff-card">
|
||||
<div class="handoff-eyebrow">wizard {local.setup.names.length + 1} of {local.setup.count}</div>
|
||||
<div class="handoff-name setup-title">What is your name?</div>
|
||||
<form class="setup-form" onsubmit={(e) => {
|
||||
e.preventDefault();
|
||||
const err = local.submitName(setupName);
|
||||
if (err) net.error = err;
|
||||
else setupName = "";
|
||||
}}>
|
||||
<!-- svelte-ignore a11y_autofocus -->
|
||||
<input class="setup-input" bind:value={setupName} maxlength="20"
|
||||
placeholder="e.g. Mordecai" autofocus />
|
||||
<button class="stamp" type="submit" disabled={!setupName.trim()}>
|
||||
{local.setup.names.length + 1 === local.setup.count ? "Flip the boards" : "Pass the device on"}
|
||||
</button>
|
||||
</form>
|
||||
{#if local.setup.names.length > 0}
|
||||
<div class="setup-roster">so far: {local.setup.names.join(", ")}</div>
|
||||
{/if}
|
||||
<button class="hint-cancel setup-cancel" onclick={() => local.cancelSetup()}>never mind</button>
|
||||
</div>
|
||||
</div>
|
||||
{:else if local.handoffTo}
|
||||
<div class="scrim-handoff" role="button" tabindex="0"
|
||||
onclick={() => local.takeSeat()} onkeydown={(e) => e.key === "Enter" && local.takeSeat()}>
|
||||
<div class="handoff-card">
|
||||
@@ -529,14 +554,15 @@
|
||||
</div>
|
||||
|
||||
<div class="hotseat-row">
|
||||
<input class="claim-input wide" bind:value={hotseatNames}
|
||||
placeholder="hotseat: names, comma-separated" aria-label="hotseat player names" />
|
||||
<button class="stamp tiny" disabled={hotseatNames.split(",").filter((n) => n.trim()).length < 2}
|
||||
onclick={() => {
|
||||
const err = local.start(hotseatNames.split(","), withExpansion);
|
||||
if (err) net.error = err;
|
||||
}}>
|
||||
Play here
|
||||
<span class="hotseat-label">or play here, passing the device:</span>
|
||||
<div class="count-picker" role="group" aria-label="number of wizards">
|
||||
{#each [2, 3, 4, 5, 6] as n (n)}
|
||||
<button class="count-btn" class:current={hotseatCount === n}
|
||||
onclick={() => (hotseatCount = n)}>{n}</button>
|
||||
{/each}
|
||||
</div>
|
||||
<button class="stamp tiny" onclick={() => { setupName = ""; local.beginSetup(hotseatCount, withExpansion); }}>
|
||||
Gather {hotseatCount} wizards
|
||||
</button>
|
||||
{#if local.hasSave()}
|
||||
<button class="stamp tiny" onclick={() => local.resume()}>Resume hotseat</button>
|
||||
@@ -865,7 +891,6 @@
|
||||
}
|
||||
.mast-leave:hover { color: #d8d2c0; }
|
||||
.mast-help-solo { margin-left: auto; }
|
||||
.claim-input.wide { width: 17rem; }
|
||||
.hotseat-row { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; justify-content: center; margin-top: 1.3rem; }
|
||||
.hotseat-exp { margin-top: 0.4rem; margin-bottom: 0; font-size: 0.85rem; }
|
||||
|
||||
@@ -899,6 +924,34 @@
|
||||
line-height: 1.15;
|
||||
}
|
||||
.handoff-hint { font-size: 0.85rem; color: #6b5a41; margin-top: 0.4rem; }
|
||||
.setup-title { font-size: 2.2rem; }
|
||||
.setup-form { display: flex; flex-direction: column; gap: 0.7rem; margin-top: 0.8rem; }
|
||||
.setup-input {
|
||||
background: #f4eede;
|
||||
border: 1px solid #b3a687;
|
||||
border-radius: 4px;
|
||||
padding: 0.6rem 0.8rem;
|
||||
font-family: "Archivo Narrow", sans-serif;
|
||||
font-size: 1.1rem;
|
||||
color: #43331f;
|
||||
text-align: center;
|
||||
}
|
||||
.setup-roster { margin-top: 0.8rem; font-size: 0.85rem; color: #6b5a41; }
|
||||
.setup-cancel { margin: 0.6rem auto 0; display: block; }
|
||||
.hotseat-label { flex-basis: 100%; font-size: 0.85rem; color: #6b5a41; }
|
||||
.count-picker { display: flex; gap: 0.3rem; }
|
||||
.count-btn {
|
||||
width: 2.1rem;
|
||||
height: 2.1rem;
|
||||
border-radius: 50%;
|
||||
border: 1.5px solid #6b5a41;
|
||||
background: none;
|
||||
color: #43331f;
|
||||
font-family: "Oswald", sans-serif;
|
||||
font-size: 0.95rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.count-btn.current { background: #43331f; color: #e9e1cb; }
|
||||
|
||||
.toast {
|
||||
background: #6d2119;
|
||||
@@ -1277,7 +1330,7 @@
|
||||
.boxlid { padding: 1rem 0; }
|
||||
.boxlid-inner { padding: 1.5rem 1.2rem 1.4rem; }
|
||||
.boxlid-title { font-size: 2.3rem; }
|
||||
.claim-input.wide, .claim-input { width: 100%; flex: 1 1 100%; }
|
||||
.claim-input { width: 100%; flex: 1 1 100%; }
|
||||
.hotseat-row .stamp, .claim-row .stamp { flex: 0 0 auto; }
|
||||
.mast-title { font-size: 1.05rem; white-space: nowrap; }
|
||||
.mast-sub { display: none; }
|
||||
|
||||
Reference in New Issue
Block a user