Receipts under resolved attacks; the cast line clears; the table holds still; teleport wraps
Each resolved attack now folds a receipt under its line, read off the engine's own record: the blow as it came in, what each counter left of it in the order they were weighed, what landed, and whose life moved. The resolution event carries the incoming damage and duration and the trail of counters for it. Hotseat chronicles carry the same. The line a cast was accepted on stayed on the board: its timer compared a reactive proxy to the object it held. Kept raw, it clears. The board jumped while walking. The "sending" note added a line beside End turn and took it away again, and the refusal toast stood in the flow above the table for five seconds; both now float and move nothing. Teleport reaches through the lettered openings in the engine, but the board dimmed the squares beyond them. The eligibility map follows the warps as the engine does. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
7d0c1c68a3
commit
648f6ab8cd
@@ -1653,7 +1653,7 @@
|
||||
/** The attack on the stack draws its sight line; a cast that resolved
|
||||
* at once (a creation, a curse) draws the line it was accepted on for a
|
||||
* few seconds, so the table can see how the aim was legal. */
|
||||
let castTrace = $state<SightLine | null>(null);
|
||||
let castTrace = $state.raw<SightLine | null>(null);
|
||||
let castTraceTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
const sightTrace = $derived((view ? stackSightTrace(view) : null) ?? castTrace);
|
||||
|
||||
@@ -3230,14 +3230,24 @@
|
||||
{/if}
|
||||
<div class="chronicle" class:tucked={castPanelOn} aria-label="game log" bind:this={chronicleEl}
|
||||
onscroll={onChronicleScroll}>
|
||||
{#snippet receipt(line: { text: string; receipt?: string[] })}
|
||||
{#if line.receipt}
|
||||
<details class="receipt">
|
||||
<summary>⚖ {line.text}</summary>
|
||||
<ul>{#each line.receipt as r, j (j)}<li>{r}</li>{/each}</ul>
|
||||
</details>
|
||||
{/if}
|
||||
{/snippet}
|
||||
{#if local.active}
|
||||
{#each local.log as line, i (i)}
|
||||
<div class:table-talk={line.startsWith("\u{1F4AC}")}>{line}</div>
|
||||
<div class:table-talk={line.text.startsWith("\u{1F4AC}")}>
|
||||
{#if line.receipt}{@render receipt(line)}{:else}{line.text}{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
{#each net.log as line, i (i)}
|
||||
<div class:table-talk={line.text.startsWith("\u{1F4AC}")}>
|
||||
<span class="log-mark" class:blank={!line.actor} style:background={line.actor ? playerColor(line.actor) : "transparent"}></span>{line.text}
|
||||
<span class="log-mark" class:blank={!line.actor} style:background={line.actor ? playerColor(line.actor) : "transparent"}></span>{#if line.receipt}{@render receipt(line)}{:else}{line.text}{/if}
|
||||
{#if line.notable && line.turn !== null && prefs.instantReplay && !net.spectating}
|
||||
<button class="moment-eye" title="relive this turn"
|
||||
onclick={() => net.requestMoment(line.turn!)}>👁</button>
|
||||
@@ -3689,14 +3699,22 @@
|
||||
}
|
||||
.count-btn.current { background: #43331f; color: #e9e1cb; }
|
||||
|
||||
/* The toast floats over the page: a refusal that came and went must
|
||||
* never push the table down and back. */
|
||||
.toast {
|
||||
position: fixed;
|
||||
top: 3.6rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 70;
|
||||
max-width: min(40rem, calc(100% - 2rem));
|
||||
background: #6d2119;
|
||||
color: #f2e6d8;
|
||||
border: 1px solid #96382c;
|
||||
padding: 0.5rem 0.85rem;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 0.7rem;
|
||||
font-size: 0.95rem;
|
||||
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
.boxlid {
|
||||
@@ -4246,7 +4264,19 @@
|
||||
}
|
||||
.offline-note { font-style: italic; color: #6b3a2f; }
|
||||
.game.offline .hand, .game.offline .board-zone, .game.offline .actions .stamp { pointer-events: none; opacity: 0.55; }
|
||||
.sending { font-family: "Courier Prime", monospace; font-size: 0.78rem; color: #6b5a41; font-style: italic; }
|
||||
.sending {
|
||||
font-family: "Courier Prime", monospace;
|
||||
font-size: 0.78rem;
|
||||
color: #6b5a41;
|
||||
font-style: italic;
|
||||
/* Floats above its row: a note that came and went must never move the board. */
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -1.2rem;
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
}
|
||||
.say-box .sending { position: static; }
|
||||
.sending.ok { color: #3f6b2f; font-style: normal; }
|
||||
.rematch-call { font-size: 0.9rem; }
|
||||
.seat.ghost .seat-name { font-style: italic; font-weight: 400; }
|
||||
@@ -4361,6 +4391,13 @@
|
||||
box-shadow: 0 2px 7px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
.chronicle div + div { margin-top: 0.05rem; }
|
||||
/* A resolved attack's account, folded under a one-line summary. */
|
||||
.receipt { display: inline; }
|
||||
.receipt summary { display: inline; cursor: pointer; color: #6b5a41; font-style: italic; }
|
||||
.receipt summary:hover { color: #43331f; }
|
||||
.receipt ul { margin: 0.15rem 0 0.3rem 1.1rem; padding: 0; list-style: none; }
|
||||
.receipt li { font-size: 0.86rem; color: #43331f; margin: 0.05rem 0; }
|
||||
.receipt li::before { content: "› "; color: #8a6d3f; }
|
||||
/* the acting wizard's color, so one player's lines can be picked out at a glance */
|
||||
.log-mark {
|
||||
display: inline-block;
|
||||
@@ -4424,7 +4461,7 @@
|
||||
color: #43331f;
|
||||
}
|
||||
|
||||
.actions { display: flex; align-items: center; gap: 0.55rem; flex-wrap: wrap; min-height: 2rem; }
|
||||
.actions { position: relative; display: flex; align-items: center; gap: 0.55rem; flex-wrap: wrap; min-height: 2rem; }
|
||||
.draw-pick { color: #a49c86; font-size: 0.9rem; }
|
||||
|
||||
.attack-scrim {
|
||||
|
||||
Reference in New Issue
Block a user