Telepath's glimpse gets held up to the light
The engine sent the caster a private handRevealedPrivate event carrying the victim's cards; the client logged the public line and dropped the private one on the floor. The stolen mind is now a modal — the cards fanned out, once, until dismissed — in the trap-notice pattern. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
39ddbb452d
commit
bde68aa85c
@@ -54,10 +54,16 @@
|
||||
let trapNotice = $state<string | null>(null);
|
||||
/** A Ward sprung on YOU earns the same courtesy. */
|
||||
let wardBite = $state<{ owner: string; amount: number } | null>(null);
|
||||
/** A mind YOU read: the stolen glimpse arrives once, in an event — hold
|
||||
* it up until dismissed, or it is lost to the scrollback. */
|
||||
let mindRead = $state<{ player: string; cards: CardInstance[] } | null>(null);
|
||||
function playFx(events: Parameters<typeof scheduleFx>[0]) {
|
||||
if (!view) return;
|
||||
for (const e of events) {
|
||||
if (e.type === "trapSprung" && e.player === view.you && e.cardId) trapNotice = e.cardId;
|
||||
if (e.type === "handRevealedPrivate" && e.visibleTo === view.you) {
|
||||
mindRead = { player: e.player, cards: e.cards };
|
||||
}
|
||||
if (e.type === "wardSprung" && e.victim === view.you) {
|
||||
const dmg = events.find((d) => d.type === "damaged" && d.player === e.victim && d.source === "warded treasure");
|
||||
wardBite = { owner: e.owner, amount: dmg?.type === "damaged" ? dmg.amount : 0 };
|
||||
@@ -1332,6 +1338,28 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if mindRead}
|
||||
<div class="scrim attack-scrim" role="alertdialog" aria-label="a mind read">
|
||||
<div class="attack-notice mind-read">
|
||||
<div class="attack-headline">{mindRead.player}'s mind, laid bare</div>
|
||||
<div class="mind-cards">
|
||||
{#each mindRead.cards as card (card.instanceId)}
|
||||
<Card {card} onfaq={(id) => (faqCardId = id)} />
|
||||
{/each}
|
||||
{#if mindRead.cards.length === 0}
|
||||
<div class="attack-power">Nothing — their thoughts are empty-handed.</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="attack-power">
|
||||
{mindRead.cards.length > 0
|
||||
? `${mindRead.cards.length} card${mindRead.cards.length === 1 ? "" : "s"} — commit them to memory; the glimpse does not linger.`
|
||||
: ""}
|
||||
</div>
|
||||
<button class="stamp big" onclick={() => (mindRead = null)}>Enough</button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if wardBite}
|
||||
<div class="scrim attack-scrim" role="alertdialog" aria-label="a warded treasure">
|
||||
<div class="attack-notice">
|
||||
@@ -2260,6 +2288,16 @@
|
||||
.mast-room { margin-left: auto; font-size: 0.85rem; color: #a49c86; }
|
||||
.mast-room b { color: #e9e1cb; letter-spacing: 0.12em; }
|
||||
.mast-audience { font-size: 0.85rem; color: #a49c86; white-space: nowrap; }
|
||||
.mind-read { max-width: min(92vw, 46rem); }
|
||||
.mind-cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
justify-content: center;
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
padding: 0.4rem 0;
|
||||
}
|
||||
.gallery-note { font-size: 0.85rem; color: #8d8672; font-style: italic; padding: 0.4rem 0; }
|
||||
.gallery-row { margin-top: 0.35rem; text-align: center; }
|
||||
.gallery-row button:disabled { opacity: 0.4; cursor: default; }
|
||||
|
||||
Reference in New Issue
Block a user