diff --git a/src/lib/components/Board.svelte b/src/lib/components/Board.svelte index ec94fce..add8844 100644 --- a/src/lib/components/Board.svelte +++ b/src/lib/components/Board.svelte @@ -11,7 +11,7 @@ import { castKey, listText, tokensText, type Duel } from '$lib/game/duel.svelte'; import { seatColor } from '$lib/game/colors'; import { GESTURE_SHORT, glyph } from '$lib/game/glyphs'; - import { MAGIC_GESTURES } from '$lib/game/spells'; + import { MAGIC_GESTURES, type Gesture } from '$lib/game/spells'; let { duel }: { duel: Duel } = $props(); @@ -20,8 +20,37 @@ const won = $derived(outcome?.winner === duel.me); /** Past three wizards the rail lists the others on a line each. */ const crowded = $derived(duel.others.length >= 3); + /** The button's word for what the player is about to do. */ + const sealLabel = $derived( + duel.moved ? 'Waiting' : duel.sending ? 'Sending' : duel.surrendering ? 'Surrender' : duel.timeStopped ? 'Act in the stopped moment' : 'Seal the turn' + ); + + /** Letters typed anywhere on the board fill the hands, left then right; Enter seals the turn. */ + const KEYS: Record = { f: 'F', p: 'P', s: 'S', w: 'W', d: 'D', c: 'C', k: '>', '>': '>', '-': '-', '.': '-', ' ': '-' }; + function onKey(e: KeyboardEvent) { + if (e.metaKey || e.ctrlKey || e.altKey) return; + const el = e.target as HTMLElement | null; + if (el && (el.isContentEditable || ['INPUT', 'TEXTAREA', 'SELECT'].includes(el.tagName))) return; + if (outcome || duel.out || duel.moved || duel.spectating) return; + if (e.key === 'Enter') { + if (duel.ready) { + e.preventDefault(); + duel.reveal(); + } + return; + } + if (e.key === 'Backspace') { + e.preventDefault(); + duel.untype(); + return; + } + const g = KEYS[e.key.toLowerCase()]; + if (g && duel.typeGesture(g)) e.preventDefault(); + } + +
@@ -54,7 +83,7 @@

Time stands still. Take your extra turn.

{duel.foe.name} cannot move, see this, or resist anything you do. Last turn's enchantments do not bind you.

{:else} -

Turn {duel.turnNumber}: write your gestures{#if duel.hasted}, twice{/if}

+

Your gestures for turn {duel.turnNumber}{#if duel.hasted}, twice over{/if}

{/if} {#if duel.threats.length && !duel.timeStopped} @@ -111,7 +140,7 @@ {#if duel.hasted} -

You are hastened: a second pair of gestures follows the first, and both take effect together.

+

You are hastened, so a second pair of gestures follows the first, and both take effect together.

@@ -191,22 +220,35 @@ {#if duel.remote?.error}

{duel.remote.error}

{/if} - {#if duel.intentText && !duel.moved} -

{duel.intentText}

- {/if} - {#if duel.moved} -

Your move is in. Waiting for {duel.awaitingText}.

- {/if} - {#if duel.surrendering} -

Both palms at once is surrender. You will lose the duel.

- {/if} - {#if duel.conflict} -

Those two spells share a gesture. A gesture can finish only one spell; let one of them pass.

- {/if} - - +
+
+ {#each duel.activeSets as set (set)} + + left {duel.drawn(set, 'left')} + right {duel.drawn(set, 'right')} + + {/each} +
+
+ {#if duel.moved} +

Your hands are still. Waiting for {duel.awaitingText}.

+ {:else if duel.intentText} +

{duel.intentText}

+ {/if} + {#if duel.surrendering} +

Both palms at once is surrender. You will lose the duel.

+ {:else if duel.loneClap && !duel.moved} +

A clap needs both hands; one hand clapping does nothing. Clap with both, or rest the other hand.

+ {/if} + {#if duel.conflict} +

Those two spells share a gesture, and a gesture can finish only one spell. Let one of them pass.

+ {/if} + {#if !duel.moved} +

Or type: F P S W D C, K to stab, a dash to rest; Backspace undoes, Enter seals.

+ {/if} +
+ +
{/if} {/if} @@ -378,8 +420,66 @@ margin: 1rem 0 0.6rem; } - .move > .reveal { - margin-top: 0.8rem; + /* Your hands, framed apart from the ledger's columns above them. */ + .hands { + background: var(--slate); + border: 1px solid var(--rule-strong); + border-radius: 6px; + padding: 0.7rem 0.9rem 0.9rem; + } + + /* The turn about to be sealed, kept in view while the hands and orders scroll. */ + .commit { + position: sticky; + bottom: 0; + z-index: 4; + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto; + align-items: center; + gap: 0.4rem 1rem; + margin-top: 0.9rem; + padding: 0.6rem 0.2rem; + background: var(--slate-deep); + border-top: 1px solid var(--rule-strong); + } + + .pair { + display: flex; + flex-direction: column; + gap: 0.1rem; + } + + .hand-pair { + display: flex; + align-items: baseline; + gap: 0.35rem; + } + + .which { + font-size: 0.75rem; + font-style: italic; + color: var(--bone-faint); + } + + .pair .letter { + font-size: 1.5rem; + min-width: 1ch; + } + + .pair .letter.empty { + color: var(--bone-faint); + } + + .notes p { + margin: 0; + } + + .notes p + p { + margin-top: 0.2rem; + } + + .keys { + font-size: 0.8rem; } .verdict { @@ -407,8 +507,16 @@ grid-template-columns: minmax(0, 1fr); } - /* The sticky bar carries the reveal button on phones. */ - .move > .reveal { + /* On phones the bar at the foot of the screen carries the pair and the button. */ + .commit { + position: static; + grid-template-columns: minmax(0, 1fr); + border-top: 0; + } + + .commit .pair, + .commit .reveal, + .keys { display: none; } @@ -425,7 +533,6 @@ .waiting { color: var(--frost); - margin-top: 0.8rem; } .reveal.again { diff --git a/src/lib/components/Chronicle.svelte b/src/lib/components/Chronicle.svelte index 30a2a89..7ded357 100644 --- a/src/lib/components/Chronicle.svelte +++ b/src/lib/components/Chronicle.svelte @@ -1,4 +1,5 @@ -
+

Chronicle

{#if turns.length === 0} -

Nothing has happened yet. Choose a gesture for each hand and reveal them.

+

Nothing has happened yet. Choose a gesture for each hand and seal the turn.

{/if} {#each turns as [turn, entries] (turn)}
diff --git a/src/lib/components/Hall.svelte b/src/lib/components/Hall.svelte index 2d5dd84..4722154 100644 --- a/src/lib/components/Hall.svelte +++ b/src/lib/components/Hall.svelte @@ -264,7 +264,7 @@ {/each} -

The ledger: every gesture both wizards have made, and the spells they finished.

+

A few turns of a duel. Each row is a turn, each letter one hand's gesture, and a spell is named under the gesture that finished it.

{#if seats.length || local} @@ -357,7 +357,7 @@

A rule read wrong, a bug, a duel you would like to tell of: write to eric@ericwagoner.com, @kestrelsnest.social on Bluesky, or @eric@toots.kestrelsnest.social on Mastodon. The keeper of this hall roosts at kestrelsnest.social.

{/snippet} - +