Thumb of God gets its ceremony: prompt, sighted dimming, and the die

The cast always worked (click a square), but nothing guided it and
nothing marked its arrival. Now: a targeting prompt, eligibility
dimming to the caster's sighted squares (the engine demands LOS to the
aim point), and a flourish — the aiming mark, a shadow gathering, the
die crashing down out of a clear sky with a bounce and flung grit, a
drift line when fate moves it, and every scattered token streaking to
where it lands. In the /?fx gallery as die-drop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0138A8CjeQRpvzKxuMfz1Bqc
This commit is contained in:
Eric Wagoner
2026-08-17 23:09:40 -04:00
co-authored by Claude Fable 5
parent 5351e345c3
commit e412463aae
6 changed files with 140 additions and 1 deletions
+10 -1
View File
@@ -22,7 +22,8 @@ export type FxShape =
| { kind: "portal"; cell: Cell; side: Side }
| { kind: "sector-spin"; origin: Cell; clockwise: boolean }
| { kind: "sector-slide"; from: Cell; to: Cell }
| { kind: "edge-dust"; cell: Cell; side: Side };
| { kind: "edge-dust"; cell: Cell; side: Side }
| { kind: "die-drop"; at: Cell; aim: Cell };
export type BoardFx = FxShape & { id: number };
/** The narrow type of one effect kind (for sprite components). */
export type FxOf<K extends BoardFx["kind"]> = BoardFx & { kind: K };
@@ -37,6 +38,7 @@ export function fxTtl(kind: BoardFx["kind"]): number {
case "portal": case "portal-cell":
case "soul": case "fireworks": case "chaos-swirl":
case "sector-spin": case "sector-slide": return 1500;
case "die-drop": return 1600;
default: return 900;
}
}
@@ -271,6 +273,13 @@ export function fxForEvents(
push({ kind: "pit-fall", at: e.at });
beat++;
break;
case "thumbOfGod":
push({ kind: "die-drop", at: e.landedAt, aim: e.aimedAt });
beat += 2; // the scattered tokens' streaks follow the impact
break;
case "tokenScattered":
push({ kind: "streak", a: cellMid(e.from), b: cellMid(e.to) });
break;
case "climbedFromPit": {
if (e.success) {
const at = posOf(e.player);