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:
co-authored by
Claude Fable 5
parent
5351e345c3
commit
e412463aae
@@ -0,0 +1,122 @@
|
||||
<script lang="ts">
|
||||
import type { FxOf } from "../fx";
|
||||
import { center } from "./geom";
|
||||
let { fx }: { fx: FxOf<"die-drop"> } = $props();
|
||||
const c = $derived(center(fx.at));
|
||||
const aim = $derived(center(fx.aim));
|
||||
const drifted = $derived(fx.at.x !== fx.aim.x || fx.at.y !== fx.aim.y);
|
||||
const DEBRIS = [15, 80, 150, 210, 275, 340];
|
||||
</script>
|
||||
|
||||
<!-- The aiming mark: where the caster prayed it would land -->
|
||||
<circle cx={aim.x} cy={aim.y} r="15" class="aim" />
|
||||
{#if drifted}
|
||||
<line x1={aim.x} y1={aim.y} x2={c.x} y2={c.y} class="drift" />
|
||||
{/if}
|
||||
<!-- The gathering shadow, then the die itself, out of a clear sky -->
|
||||
<ellipse cx={c.x} cy={c.y + 8} rx="16" ry="7" class="die-shadow" />
|
||||
<g class="die" style={`transform-origin: ${c.x}px ${c.y}px`}>
|
||||
<g transform={`translate(${c.x} ${c.y})`}>
|
||||
<rect x="-15" y="-15" width="30" height="30" rx="6" class="die-body" />
|
||||
<!-- a four: the drift roll made manifest -->
|
||||
<circle cx="-7" cy="-7" r="3.2" class="pip" />
|
||||
<circle cx="7" cy="-7" r="3.2" class="pip" />
|
||||
<circle cx="-7" cy="7" r="3.2" class="pip" />
|
||||
<circle cx="7" cy="7" r="3.2" class="pip" />
|
||||
</g>
|
||||
</g>
|
||||
<!-- Impact: ring and flung grit -->
|
||||
<circle cx={c.x} cy={c.y} r="14" class="impact" />
|
||||
{#each DEBRIS as deg, i (deg)}
|
||||
<line
|
||||
x1={c.x + 14 * Math.cos((deg * Math.PI) / 180)}
|
||||
y1={c.y + 14 * Math.sin((deg * Math.PI) / 180)}
|
||||
x2={c.x + 26 * Math.cos((deg * Math.PI) / 180)}
|
||||
y2={c.y + 26 * Math.sin((deg * Math.PI) / 180)}
|
||||
class={`grit g${i % 3}`}
|
||||
style={`transform-origin: ${c.x}px ${c.y}px`}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<style>
|
||||
.aim {
|
||||
fill: none;
|
||||
stroke: #8d2f23;
|
||||
stroke-width: 2;
|
||||
stroke-dasharray: 5 4;
|
||||
opacity: 0;
|
||||
animation: aim-mark 0.5s ease-out forwards;
|
||||
}
|
||||
.drift {
|
||||
stroke: #8d2f23;
|
||||
stroke-width: 1.5;
|
||||
stroke-dasharray: 3 4;
|
||||
opacity: 0;
|
||||
animation: drift-line 0.5s ease-out 0.55s forwards;
|
||||
}
|
||||
.die-shadow {
|
||||
fill: rgba(43, 34, 24, 0.5);
|
||||
animation: shadow-loom 0.55s ease-in forwards;
|
||||
transform-box: fill-box;
|
||||
transform-origin: center;
|
||||
}
|
||||
.die {
|
||||
animation: die-fall 0.55s cubic-bezier(0.5, 0, 0.9, 0.6) forwards,
|
||||
die-settle 0.9s ease-out 0.55s forwards;
|
||||
}
|
||||
.die-body {
|
||||
fill: #efe8d4;
|
||||
stroke: #2b2218;
|
||||
stroke-width: 2.5;
|
||||
}
|
||||
.pip { fill: #2b2218; }
|
||||
.impact {
|
||||
fill: none;
|
||||
stroke: #7c6a4f;
|
||||
stroke-width: 3;
|
||||
opacity: 0;
|
||||
transform-box: fill-box;
|
||||
transform-origin: center;
|
||||
animation: impact-ring 0.5s ease-out 0.55s forwards;
|
||||
}
|
||||
.grit {
|
||||
stroke: #7c6a4f;
|
||||
stroke-width: 2;
|
||||
stroke-linecap: round;
|
||||
opacity: 0;
|
||||
animation: grit-fly 0.4s ease-out 0.58s forwards;
|
||||
}
|
||||
.grit.g1 { animation-delay: 0.62s; }
|
||||
.grit.g2 { animation-delay: 0.66s; }
|
||||
@keyframes aim-mark {
|
||||
0% { opacity: 0; transform: scale(1.6); transform-origin: center; transform-box: fill-box; }
|
||||
100% { opacity: 0.8; transform: scale(1); }
|
||||
}
|
||||
@keyframes drift-line {
|
||||
0% { opacity: 0; }
|
||||
100% { opacity: 0.7; }
|
||||
}
|
||||
@keyframes shadow-loom {
|
||||
0% { opacity: 0; transform: scale(0.2); }
|
||||
100% { opacity: 1; transform: scale(1); }
|
||||
}
|
||||
@keyframes die-fall {
|
||||
0% { opacity: 0; transform: translateY(-150px) scale(2.4) rotate(35deg); }
|
||||
30% { opacity: 1; }
|
||||
100% { transform: translateY(0) scale(1) rotate(0deg); }
|
||||
}
|
||||
@keyframes die-settle {
|
||||
0% { transform: translateY(0) scale(1.15, 0.85); }
|
||||
25% { transform: translateY(-6px) scale(1); }
|
||||
45% { transform: translateY(0) scale(1.05, 0.95); }
|
||||
100% { transform: translateY(0) scale(1); }
|
||||
}
|
||||
@keyframes impact-ring {
|
||||
0% { opacity: 0.9; transform: scale(0.4); }
|
||||
100% { opacity: 0; transform: scale(2.4); }
|
||||
}
|
||||
@keyframes grit-fly {
|
||||
0% { opacity: 0.9; transform: scale(0.7); }
|
||||
100% { opacity: 0; transform: scale(1.8); }
|
||||
}
|
||||
</style>
|
||||
@@ -8,6 +8,7 @@ import Bolt from "./Bolt.svelte";
|
||||
import Burst from "./Burst.svelte";
|
||||
import ChaosSwirl from "./ChaosSwirl.svelte";
|
||||
import Claw from "./Claw.svelte";
|
||||
import DieDrop from "./DieDrop.svelte";
|
||||
import DustPuff from "./DustPuff.svelte";
|
||||
import Fireball from "./Fireball.svelte";
|
||||
import Fireworks from "./Fireworks.svelte";
|
||||
@@ -49,6 +50,7 @@ export const FX_SPRITES: { [K in BoardFx["kind"]]: Component<{ fx: FxOf<K> }> }
|
||||
soul: Soul,
|
||||
fireworks: Fireworks,
|
||||
"chaos-swirl": ChaosSwirl,
|
||||
"die-drop": DieDrop,
|
||||
"pit-fall": PitFall,
|
||||
"ooze-slip": OozeSlip,
|
||||
"tacks-ow": TacksOw,
|
||||
|
||||
Reference in New Issue
Block a user