From feedback on the BoardGameGeek thread. The hands are framed and labelled as yours, so they no longer read as sitting under your opponent's columns. A move can be typed: the first letter goes to the left hand, the second to the right, K stabs, a dash rests, Backspace takes one back and Enter seals. The chosen pair, what it will do, any warning and the button now sit in one row that stays in view. A clap with one hand is warned of before the turn is sealed. The button says what the player does: Seal the turn. The chronicle reads oldest first and scrolls to the newest line. Headings, tabs and notes lose the label-and-colon construction and the abstract headings; the chronicle's imagery stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141G6xqLeNRYEtviLWSB5Up
133 lines
3.1 KiB
Svelte
133 lines
3.1 KiB
Svelte
<script lang="ts">
|
|
import type { Duel } from '$lib/game/duel.svelte';
|
|
import { HANDS } from '$lib/game/spells';
|
|
import { MAX_HP } from '$lib/game/state';
|
|
|
|
let { duel }: { duel: Duel } = $props();
|
|
|
|
/** Past three wizards the bar names you, the wizard in focus, and the count; the roster is a tap away. */
|
|
const crowded = $derived(duel.others.length >= 3);
|
|
const shown = $derived(crowded ? [duel.focus ?? duel.foeId].filter((id) => id !== duel.me) : duel.others);
|
|
|
|
function toRoster() {
|
|
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
document.getElementById('roster')?.scrollIntoView({ behavior: reduce ? 'auto' : 'smooth', block: 'start' });
|
|
}
|
|
</script>
|
|
|
|
{#if !duel.state.over}
|
|
<div class="bar" class:watching={duel.spectating}>
|
|
<div class="health" class:many={duel.others.length > 1}>
|
|
{#if !duel.spectating}<span><em>{duel.you.name}</em> {duel.you.hp}/{MAX_HP}</span>{/if}
|
|
{#each shown as id (id)}
|
|
{@const w = duel.state.wizards[id]}
|
|
<span class:gone={!w.alive || w.surrendered}><em>{w.name}</em> {w.hp}/{MAX_HP}</span>
|
|
{/each}
|
|
{#if crowded}
|
|
<button type="button" class="count" onclick={toRoster}>{duel.state.seats.length} wizards</button>
|
|
{/if}
|
|
</div>
|
|
{#if duel.spectating}
|
|
<span class="gallery">the gallery</span>
|
|
{:else}
|
|
<div class="gestures" aria-label="Chosen gestures">
|
|
{#each HANDS as hand (hand)}
|
|
<span class="letter" class:empty={duel.chosenGesture('main', hand) === null}>{duel.drawn('main', hand)}</span>
|
|
{/each}
|
|
</div>
|
|
<button type="button" class="reveal" disabled={!duel.ready} onclick={() => duel.reveal()}>
|
|
{duel.out ? 'Out' : duel.moved ? 'Waiting' : duel.surrendering ? 'Surrender' : duel.timeStopped ? 'Act' : 'Seal'}
|
|
</button>
|
|
{/if}
|
|
</div>
|
|
{/if}
|
|
|
|
<style>
|
|
.bar {
|
|
display: none;
|
|
}
|
|
|
|
/* Must match COMPACT_QUERY in duel.svelte.ts. */
|
|
@media (max-width: 860px) {
|
|
.bar {
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
z-index: 5;
|
|
display: grid;
|
|
grid-template-columns: 1fr auto auto;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
padding: 0.5rem 16px calc(0.5rem + env(safe-area-inset-bottom));
|
|
background: var(--slate);
|
|
border-top: 1px solid var(--rule-strong);
|
|
}
|
|
}
|
|
|
|
.bar.watching {
|
|
grid-template-columns: 1fr auto;
|
|
}
|
|
|
|
.gallery {
|
|
font-size: 0.8rem;
|
|
font-style: italic;
|
|
color: var(--bone-faint);
|
|
}
|
|
|
|
.health {
|
|
display: flex;
|
|
flex-direction: column;
|
|
font-size: 0.8rem;
|
|
color: var(--bone-dim);
|
|
font-variant-numeric: tabular-nums;
|
|
line-height: 1.3;
|
|
}
|
|
|
|
.health.many {
|
|
flex-direction: row;
|
|
flex-wrap: wrap;
|
|
gap: 0 0.6rem;
|
|
font-size: 0.72rem;
|
|
}
|
|
|
|
.health em {
|
|
color: var(--bone);
|
|
}
|
|
|
|
.health .gone {
|
|
text-decoration: line-through;
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.count {
|
|
background: none;
|
|
border: 0;
|
|
padding: 0;
|
|
font: inherit;
|
|
color: var(--frost);
|
|
text-decoration: underline dotted;
|
|
}
|
|
|
|
.gestures {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.letter {
|
|
font-size: 1.6rem;
|
|
color: var(--ember);
|
|
min-width: 1.2em;
|
|
text-align: center;
|
|
}
|
|
|
|
.letter.empty {
|
|
color: var(--bone-faint);
|
|
}
|
|
|
|
.reveal {
|
|
padding: 0.5rem 1rem;
|
|
font-size: 1rem;
|
|
}
|
|
</style>
|