Press-and-hold peeks a segment — the tooltip, for hands without a mouse
The same 450ms hold that peeks a square now works on walls, doors, and fire walls: the segment's full description (state, lock, hitpoints, the fire's burn warning) rises in the peek scrim. Shimmering illusions keep their richer tap modal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138A8CjeQRpvzKxuMfz1Bqc
This commit is contained in:
co-authored by
Claude Fable 5
parent
497c2245a3
commit
0f91d30a3e
@@ -938,6 +938,9 @@
|
||||
clearSelection();
|
||||
}
|
||||
|
||||
// Press-and-hold on a wall/door/fire segment (touch has no hover).
|
||||
let peekEdge = $state<string | null>(null);
|
||||
|
||||
// A shimmering illusion wall, tapped or bumped: offer the belief test.
|
||||
let illusionPrompt = $state<{ cell: { x: number; y: number }; side: Side } | null>(null);
|
||||
function illusionEdgeParts(key: string): { cell: { x: number; y: number }; side: Side } {
|
||||
@@ -1130,11 +1133,13 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if peekCard || discardPeek || peekTreasure}
|
||||
{#if peekCard || discardPeek || peekTreasure || peekEdge}
|
||||
<div class="big-peek-scrim" role="button" tabindex="-1"
|
||||
onclick={() => { peekCard = null; peekCreatureId = null; peekToken = null; discardPeek = null; peekTreasure = null; }} onkeydown={() => {}}>
|
||||
onclick={() => { peekCard = null; peekCreatureId = null; peekToken = null; discardPeek = null; peekTreasure = null; peekEdge = null; }} onkeydown={() => {}}>
|
||||
<div class="big-peek">
|
||||
{#if peekTreasure}
|
||||
{#if peekEdge}
|
||||
<div class="peek-note big-peek-note">{peekEdge}</div>
|
||||
{:else if peekTreasure}
|
||||
<img class="big-peek-token" src={peekTreasure.src} alt="" />
|
||||
<div class="peek-note big-peek-note">{peekTreasure.note}</div>
|
||||
{:else}
|
||||
@@ -1559,6 +1564,7 @@
|
||||
onGhostClick={clickGhostSlot}
|
||||
onCellPeek={(c) => peekAt(c)}
|
||||
onIllusionClick={(cell, side) => (illusionPrompt = { cell, side })}
|
||||
onEdgePeek={(tip) => (peekEdge = tip)}
|
||||
{litCells}
|
||||
{sightTrace}
|
||||
/>
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
onWarpClick,
|
||||
onCellPeek,
|
||||
onIllusionClick,
|
||||
onEdgePeek,
|
||||
markedCell = null,
|
||||
markedCells = null,
|
||||
litCells = null,
|
||||
@@ -47,6 +48,8 @@
|
||||
onCellPeek?: (cell: { x: number; y: number }) => void;
|
||||
/** Tap on a shimmering (untested) illusion wall: offer the belief test. */
|
||||
onIllusionClick?: (cell: { x: number; y: number }, side: Side) => void;
|
||||
/** Press-and-hold a wall/door/fire segment: its tooltip, for touch. */
|
||||
onEdgePeek?: (tip: string) => void;
|
||||
/** First square of a two-square spell: marked so the click reads as taken. */
|
||||
markedCell?: { x: number; y: number } | null;
|
||||
/** A multi-square placement in progress (boobytrap tokens), in click
|
||||
@@ -387,7 +390,7 @@
|
||||
<!-- walls & doors & firewalls -->
|
||||
{#each edges as e (`${e.kind}:${e.x},${e.y}`)}
|
||||
{#if e.state === "firewall"}
|
||||
<FirewallEdge x={e.x} y={e.y} kind={e.kind === "V" ? "V" : "H"} />
|
||||
<FirewallEdge x={e.x} y={e.y} kind={e.kind === "V" ? "V" : "H"} onpeek={onEdgePeek} />
|
||||
{:else}
|
||||
{@const lockTitle = e.lock === "removed" ? "lock removed — swings free"
|
||||
: e.lock === "jammed" ? "lock jammed — sealed for good"
|
||||
@@ -396,7 +399,8 @@
|
||||
<EdgeGlyph x={e.x} y={e.y} kind={e.kind === "V" ? "V" : "H"}
|
||||
state={e.state === "door" ? "door" : "wall"}
|
||||
lock={(e.lock ?? null) as "removed" | "jammed" | "held" | "ajar" | null}
|
||||
title={lockTitle} damage={view.wallDamage[`${e.kind}:${e.x},${e.y}`] ?? 0} />
|
||||
title={lockTitle} damage={view.wallDamage[`${e.kind}:${e.x},${e.y}`] ?? 0}
|
||||
onpeek={onEdgePeek} />
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
// A wall or door on an edge, exactly as the board draws it. Wall-segment
|
||||
// effects (locks picked, jammed, removed; doors held) are color states of
|
||||
// this one glyph — the game has never used tokens for them.
|
||||
let { x, y, kind, state, lock = null, title = null, damage = 0 }: {
|
||||
let { x, y, kind, state, lock = null, title = null, damage = 0, onpeek }: {
|
||||
x: number; y: number; kind: "V" | "H";
|
||||
state: "wall" | "door";
|
||||
lock?: "removed" | "jammed" | "held" | "ajar" | null;
|
||||
title?: string | null;
|
||||
/** Battle damage taken so far (walls fall at 20, doors at 15). */
|
||||
damage?: number;
|
||||
/** Press-and-hold (touch has no hover): report the tooltip's text. */
|
||||
onpeek?: (tip: string) => void;
|
||||
} = $props();
|
||||
const CELL = 48;
|
||||
const WALL = 7;
|
||||
@@ -19,6 +21,17 @@
|
||||
? `${Math.max(0, needed - damage)} of ${needed} hitpoints left`
|
||||
: `${needed} hitpoints`);
|
||||
const tip = $derived(`${title ?? (state === "door" ? "a locked door" : "a wall")} — ${hp}`);
|
||||
|
||||
// Press-and-hold peeks the segment, mirroring the board's cell gesture.
|
||||
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||
function press() {
|
||||
if (!onpeek) return;
|
||||
timer = setTimeout(() => onpeek?.(tip), 450);
|
||||
}
|
||||
function release() {
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if kind === "V"}
|
||||
@@ -26,12 +39,14 @@
|
||||
x={(x + 1) * CELL - WALL / 2} y={y * CELL - WALL / 2}
|
||||
width={WALL} height={CELL + WALL}
|
||||
class={cls}
|
||||
role="img" onpointerdown={press} onpointerup={release} onpointerleave={release}
|
||||
><title>{tip}</title></rect>
|
||||
{:else}
|
||||
<rect
|
||||
x={x * CELL - WALL / 2} y={(y + 1) * CELL - WALL / 2}
|
||||
width={CELL + WALL} height={WALL}
|
||||
class={cls}
|
||||
role="img" onpointerdown={press} onpointerup={release} onpointerleave={release}
|
||||
><title>{tip}</title></rect>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -2,7 +2,21 @@
|
||||
// A standing WALL OF FIRE: board furniture, not a flourish — it burns for
|
||||
// as long as the spell holds, so the animation is pure CSS on a handful
|
||||
// of shapes (no turbulence filters; those are budgeted for one-shot fx).
|
||||
let { x, y, kind }: { x: number; y: number; kind: "V" | "H" } = $props();
|
||||
let { x, y, kind, onpeek }: {
|
||||
x: number; y: number; kind: "V" | "H";
|
||||
/** Press-and-hold (touch has no hover): report what this fire is. */
|
||||
onpeek?: (tip: string) => void;
|
||||
} = $props();
|
||||
const TIP = "a wall of fire — passing through burns for 4";
|
||||
let peekTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
function press() {
|
||||
if (!onpeek) return;
|
||||
peekTimer = setTimeout(() => onpeek?.(TIP), 450);
|
||||
}
|
||||
function release() {
|
||||
if (peekTimer) clearTimeout(peekTimer);
|
||||
peekTimer = null;
|
||||
}
|
||||
const uid = $props.id();
|
||||
const CELL = 48;
|
||||
const WALL = 7;
|
||||
@@ -42,10 +56,12 @@
|
||||
<!-- the burning bar itself, seated on the wall line -->
|
||||
{#if kind === "V"}
|
||||
<rect x={-WALL / 2} y={-CELL / 2 - 2} width={WALL} height={CELL + 4} rx="3"
|
||||
fill={`url(#fw-bar-${uid})`} class="fw-bar" />
|
||||
fill={`url(#fw-bar-${uid})`} class="fw-bar"
|
||||
role="img" onpointerdown={press} onpointerup={release} onpointerleave={release} />
|
||||
{:else}
|
||||
<rect x={-CELL / 2 - 2} y={-WALL / 2} width={CELL + 4} height={WALL} rx="3"
|
||||
fill={`url(#fw-bar-${uid})`} class="fw-bar" />
|
||||
fill={`url(#fw-bar-${uid})`} class="fw-bar"
|
||||
role="img" onpointerdown={press} onpointerup={release} onpointerleave={release} />
|
||||
{/if}
|
||||
<!-- tongues of flame, each licking on its own beat -->
|
||||
{#each TONGUES as f (f.t)}
|
||||
|
||||
Reference in New Issue
Block a user