The store plays any seat, locally against the bot or remotely through the server. A room has a four-letter code that is also its invite link; the hall opens or joins one and lists the seats this browser holds with whose move it is. The ledger and summary show every seat, threats name their wizard, and the board is one component shared by the hall and the room page. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
141 lines
2.7 KiB
Svelte
141 lines
2.7 KiB
Svelte
<script lang="ts">
|
|
import type { Duel } from '$lib/game/duel.svelte';
|
|
|
|
let { duel }: { duel: Duel } = $props();
|
|
|
|
const summary = $derived(duel.summary);
|
|
|
|
function signed(n: number): string {
|
|
return n > 0 ? `+${n}` : String(n);
|
|
}
|
|
</script>
|
|
|
|
{#if summary}
|
|
<section class="result" id="turn-result" aria-label="What the last reveal did">
|
|
<h2>{summary.phase === 'timestop' ? 'In the stopped moment' : `Turn ${summary.turn + 1}`}</h2>
|
|
<dl>
|
|
{#each duel.state.seats as who (who)}
|
|
{@const w = summary.wizards[who]}
|
|
<div>
|
|
<dt>{who === duel.me ? 'You' : duel.state.wizards[who].name}</dt>
|
|
<dd>
|
|
{#if w.effects.length === 0}
|
|
<span class="muted">untouched</span>
|
|
{:else}
|
|
{#each w.effects as e, i (i)}
|
|
<span class="effect" class:hurt={e.delta < 0} class:healed={e.delta > 0}>{e.label} {signed(e.delta)}</span>
|
|
{/each}
|
|
{/if}
|
|
<span class="hp" class:changed={w.before !== w.after}>health {w.before}{#if w.before !== w.after} → {w.after}{/if}</span>
|
|
</dd>
|
|
</div>
|
|
{/each}
|
|
</dl>
|
|
{#if summary.events.length}
|
|
<ul class="events">
|
|
{#each summary.events as e, i (i)}
|
|
<li>{e}</li>
|
|
{/each}
|
|
</ul>
|
|
{/if}
|
|
{#if duel.showLessons && summary.lessons.length}
|
|
<ul class="lessons">
|
|
{#each summary.lessons as l, i (i)}
|
|
<li>{l}</li>
|
|
{/each}
|
|
</ul>
|
|
{/if}
|
|
</section>
|
|
{/if}
|
|
|
|
<style>
|
|
.result {
|
|
scroll-margin-top: 0.5rem;
|
|
margin: 0.5rem 0 0;
|
|
padding: 0.4rem 0.8rem;
|
|
background: var(--slate);
|
|
border-left: 2px solid var(--frost);
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
/* On a phone, leave the last two ledger rows in view above the result. Must match COMPACT_QUERY in duel.svelte.ts. */
|
|
@media (max-width: 860px) {
|
|
.result {
|
|
scroll-margin-top: 9.5rem;
|
|
}
|
|
}
|
|
|
|
h2 {
|
|
font-size: 0.8rem;
|
|
color: var(--bone-faint);
|
|
margin-bottom: 0.15rem;
|
|
}
|
|
|
|
dl {
|
|
margin: 0;
|
|
display: grid;
|
|
gap: 0.15rem;
|
|
}
|
|
|
|
dl > div {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.2rem 0.6rem;
|
|
align-items: baseline;
|
|
}
|
|
|
|
dt {
|
|
font-style: italic;
|
|
min-width: 5.5em;
|
|
color: var(--bone-dim);
|
|
}
|
|
|
|
dd {
|
|
margin: 0;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.2rem 0.75rem;
|
|
}
|
|
|
|
.effect.hurt {
|
|
color: var(--ember);
|
|
}
|
|
|
|
.effect.healed {
|
|
color: var(--frost);
|
|
}
|
|
|
|
.hp {
|
|
color: var(--bone-dim);
|
|
}
|
|
|
|
.hp.changed {
|
|
color: var(--bone);
|
|
}
|
|
|
|
.lessons {
|
|
list-style: none;
|
|
margin: 0.4rem 0 0;
|
|
padding: 0.4rem 0 0.1rem 0.7rem;
|
|
border-left: 2px solid var(--ember);
|
|
color: var(--bone);
|
|
font-size: 0.85rem;
|
|
font-style: italic;
|
|
line-height: 1.45;
|
|
}
|
|
|
|
.lessons li + li {
|
|
margin-top: 0.3rem;
|
|
}
|
|
|
|
.events {
|
|
list-style: none;
|
|
margin: 0.35rem 0 0;
|
|
padding: 0.35rem 0 0;
|
|
border-top: 1px solid var(--rule);
|
|
color: var(--bone-dim);
|
|
font-size: 0.85rem;
|
|
line-height: 1.4;
|
|
}
|
|
</style>
|