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);
|
let trapNotice = $state<string | null>(null);
|
||||||
/** A Ward sprung on YOU earns the same courtesy. */
|
/** A Ward sprung on YOU earns the same courtesy. */
|
||||||
let wardBite = $state<{ owner: string; amount: number } | null>(null);
|
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]) {
|
function playFx(events: Parameters<typeof scheduleFx>[0]) {
|
||||||
if (!view) return;
|
if (!view) return;
|
||||||
for (const e of events) {
|
for (const e of events) {
|
||||||
if (e.type === "trapSprung" && e.player === view.you && e.cardId) trapNotice = e.cardId;
|
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) {
|
if (e.type === "wardSprung" && e.victim === view.you) {
|
||||||
const dmg = events.find((d) => d.type === "damaged" && d.player === e.victim && d.source === "warded treasure");
|
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 };
|
wardBite = { owner: e.owner, amount: dmg?.type === "damaged" ? dmg.amount : 0 };
|
||||||
@@ -1332,6 +1338,28 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/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}
|
{#if wardBite}
|
||||||
<div class="scrim attack-scrim" role="alertdialog" aria-label="a warded treasure">
|
<div class="scrim attack-scrim" role="alertdialog" aria-label="a warded treasure">
|
||||||
<div class="attack-notice">
|
<div class="attack-notice">
|
||||||
@@ -2260,6 +2288,16 @@
|
|||||||
.mast-room { margin-left: auto; font-size: 0.85rem; color: #a49c86; }
|
.mast-room { margin-left: auto; font-size: 0.85rem; color: #a49c86; }
|
||||||
.mast-room b { color: #e9e1cb; letter-spacing: 0.12em; }
|
.mast-room b { color: #e9e1cb; letter-spacing: 0.12em; }
|
||||||
.mast-audience { font-size: 0.85rem; color: #a49c86; white-space: nowrap; }
|
.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-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 { margin-top: 0.35rem; text-align: center; }
|
||||||
.gallery-row button:disabled { opacity: 0.4; cursor: default; }
|
.gallery-row button:disabled { opacity: 0.4; cursor: default; }
|
||||||
|
|||||||
Reference in New Issue
Block a user