Duplicated logic and styling that had drifted apart is unified: the gesture-sharing check, the hostile spell list, the enchanted test, the plan limits, and the shared button, link, field and disabled styles. Dead plumbing is gone: the always-true implemented flag, the unread cast length, an unreachable guard, an impossible time-stop condition, the unused sequence formatter and utility class, the template placeholder. The counter-spell tests now put a counter-spell in front of a spell, the test helpers live in one file, and the resolver's sections are numbered in order. The deploy script's cache headers now do what its comment says, and the README describes the screen rather than listing features in the order they arrived. 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 { FOE, YOU, 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 [YOU, FOE] as const as who (who)}
|
|
{@const w = summary.wizards[who]}
|
|
<div>
|
|
<dt>{who === YOU ? 'You' : duel.foe.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>
|