Lobby standee picker for online rooms
Waiting rooms now show the six wizard standees: tap to claim yours, claimed ones gray out with the claimant named for screen readers, the roster shows each player's chosen standee (or "choosing..."), and conflicts are refused first-come. Choices sync live to everyone in the room, resolve to first-free defaults for the undecided when the host flips the boards, persist in the start line of the room log (so restored games keep their colors), and flow through the same engine config field the hotseat picker uses. Also fixed: the static-serving realpath guard crashed the dev server when no client build exists — it now 404s static requests instead. Verified end to end: claim, conflict refusal, second pick, start, and in-game colorIndex 5/2. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
77ae65b263
commit
4cbf5a013e
@@ -647,9 +647,33 @@
|
||||
<div class="boxlid-tag">Share the code. Two to six wizards enter the maze.</div>
|
||||
<ul class="roster">
|
||||
{#each net.players as p (p)}
|
||||
<li><span class="dot" style:background={playerColor(p)}></span>{p}{p === net.hostId ? " — host" : ""}</li>
|
||||
{@const chosen = net.roomColors[p]}
|
||||
<li>
|
||||
{#if chosen !== undefined}
|
||||
<img class="roster-standee" src={`/tokens/wizard-${chosen}.png`} alt="" />
|
||||
{:else}
|
||||
<span class="dot" style:background="#b3a687"></span>
|
||||
{/if}
|
||||
{p}{p === net.hostId ? " — host" : ""}{chosen === undefined ? " — choosing…" : ""}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<div class="standee-row" role="group" aria-label="choose your wizard">
|
||||
{#each [0, 1, 2, 3, 4, 5] as c (c)}
|
||||
{@const takenBy = Object.entries(net.roomColors).find(([, v]) => v === c)?.[0]}
|
||||
<button
|
||||
class="standee"
|
||||
class:current={net.you != null && net.roomColors[net.you] === c}
|
||||
class:taken={takenBy !== undefined && takenBy !== net.you}
|
||||
disabled={takenBy !== undefined && takenBy !== net.you}
|
||||
style:--ring={PLAYER_COLORS[c]}
|
||||
onclick={() => net.pickColor(c)}
|
||||
aria-label={`wizard color ${c + 1}${takenBy && takenBy !== net.you ? ` (taken by ${takenBy})` : ""}`}
|
||||
>
|
||||
<img src={`/tokens/wizard-${c}.png`} alt="" />
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{#if net.you === net.hostId}
|
||||
<label class="check">
|
||||
<input type="checkbox" bind:checked={withExpansion} />
|
||||
@@ -1062,6 +1086,7 @@
|
||||
.code { width: 6.5rem; padding: 0.55rem 0.4rem; text-transform: uppercase; text-align: center; letter-spacing: 0.15em; }
|
||||
.roster { list-style: none; padding: 0; margin: 0 0 1rem; text-align: left; }
|
||||
.roster li { display: flex; align-items: center; gap: 0.5rem; padding: 0.25rem 0; font-size: 1.05rem; }
|
||||
.roster-standee { width: 1.9rem; height: 1.9rem; border-radius: 4px; object-fit: cover; }
|
||||
.check { display: flex; gap: 0.5rem; align-items: center; justify-content: center; font-size: 0.92rem; margin-bottom: 1rem; }
|
||||
.waiting { color: #6b5a41; font-style: italic; }
|
||||
|
||||
|
||||
@@ -166,6 +166,8 @@ class Net {
|
||||
players = $state<string[]>([]);
|
||||
hostId = $state<string | null>(null);
|
||||
started = $state(false);
|
||||
/** Lobby standee choices, by player name. */
|
||||
roomColors = $state<Record<string, number>>({});
|
||||
you = $state<string | null>(null);
|
||||
view = $state<GameView | null>(null);
|
||||
log = $state<string[]>([]);
|
||||
@@ -225,6 +227,7 @@ class Net {
|
||||
}
|
||||
case "room":
|
||||
this.roomId = msg.roomId;
|
||||
this.roomColors = msg.colors ?? {};
|
||||
if (this.you && this.token) {
|
||||
const seat: Seat = { name: this.you, roomId: msg.roomId, token: this.token };
|
||||
localStorage.setItem(SEAT_KEY, JSON.stringify(seat));
|
||||
@@ -369,6 +372,10 @@ class Net {
|
||||
this.send({ type: "start", expansion });
|
||||
}
|
||||
|
||||
pickColor(color: number): void {
|
||||
this.send({ type: "pickColor", color });
|
||||
}
|
||||
|
||||
command(command: Command): void {
|
||||
this.send({ type: "command", command });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user