The whiff darkens to slate and sweeps longer; the portal-cell opens a dark void behind its collapsing rings — an opening in space, not a selected square; the tacks grow half again and recoil harder; the streak's reveal slows so the travel reads even in a sampled frame; dust lifts its contrast a step without turning heavy. The sector ghosts stay board-machinery on purpose — the reviewer allowed it, and the maze grinding IS an operation, not a spell. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
90 lines
3.4 KiB
Svelte
90 lines
3.4 KiB
Svelte
<script lang="ts">
|
|
import type { FxOf } from "../fx";
|
|
import { CELL } from "./geom";
|
|
let { fx }: { fx: FxOf<"portal" | "portal-cell"> } = $props();
|
|
const uid = $props.id();
|
|
</script>
|
|
|
|
{#if fx.kind === "portal"}
|
|
{@const x1 = fx.side === "E" ? (fx.cell.x + 1) * CELL : fx.cell.x * CELL}
|
|
{@const y1 = fx.side === "S" ? (fx.cell.y + 1) * CELL : fx.cell.y * CELL}
|
|
{@const x2 = fx.side === "W" ? fx.cell.x * CELL : (fx.cell.x + 1) * CELL}
|
|
{@const y2 = fx.side === "N" ? fx.cell.y * CELL : (fx.cell.y + 1) * CELL}
|
|
<line {x1} {y1} {x2} {y2} class="curtain glow" />
|
|
<line {x1} {y1} {x2} {y2} class="curtain" pathLength="100" />
|
|
{:else}
|
|
<defs>
|
|
<radialGradient id={`portal-void-${uid}`}>
|
|
<stop offset="0" stop-color="#0d2430" stop-opacity="0.85" />
|
|
<stop offset="0.6" stop-color="#12414a" stop-opacity="0.5" />
|
|
<stop offset="1" stop-color="#9be3e0" stop-opacity="0.1" />
|
|
</radialGradient>
|
|
</defs>
|
|
<rect x={fx.at.x * CELL + 5} y={fx.at.y * CELL + 5}
|
|
width={CELL - 10} height={CELL - 10} rx="8"
|
|
fill={`url(#portal-void-${uid})`} class="void"
|
|
style={`transform-origin: ${fx.at.x * CELL + CELL / 2}px ${fx.at.y * CELL + CELL / 2}px`} />
|
|
<rect x={fx.at.x * CELL + 3} y={fx.at.y * CELL + 3}
|
|
width={CELL - 6} height={CELL - 6} rx="6" class="veil outer"
|
|
style={`transform-origin: ${fx.at.x * CELL + CELL / 2}px ${fx.at.y * CELL + CELL / 2}px`} />
|
|
<rect x={fx.at.x * CELL + 3} y={fx.at.y * CELL + 3}
|
|
width={CELL - 6} height={CELL - 6} rx="6" class="veil inner"
|
|
style={`transform-origin: ${fx.at.x * CELL + CELL / 2}px ${fx.at.y * CELL + CELL / 2}px`} />
|
|
{/if}
|
|
|
|
<style>
|
|
/* The threshold opens, light ripples along it, and it pinches shut. */
|
|
.curtain {
|
|
stroke: #9be3e0;
|
|
stroke-dasharray: 6 5;
|
|
stroke-linecap: round;
|
|
filter: drop-shadow(0 0 4px rgba(155, 227, 224, 0.9));
|
|
animation: open-ripple-pinch 0.95s ease-in-out forwards;
|
|
}
|
|
.curtain.glow {
|
|
stroke: rgba(180, 138, 224, 0.5);
|
|
stroke-dasharray: none;
|
|
filter: blur(2px);
|
|
animation: glow-swell 0.95s ease-in-out forwards;
|
|
}
|
|
@keyframes open-ripple-pinch {
|
|
0% { opacity: 0; stroke-width: 0.5; stroke-dashoffset: 0; }
|
|
22% { opacity: 1; stroke-width: 6; }
|
|
60% { stroke-width: 4.5; stroke-dashoffset: 22; }
|
|
88% { opacity: 0.9; stroke-width: 1.5; stroke-dashoffset: 34; }
|
|
100% { opacity: 0; stroke-width: 0.5; stroke-dashoffset: 38; }
|
|
}
|
|
@keyframes glow-swell {
|
|
0% { opacity: 0; stroke-width: 2; }
|
|
25% { opacity: 0.85; stroke-width: 14; }
|
|
70% { opacity: 0.6; stroke-width: 9; }
|
|
100% { opacity: 0; stroke-width: 2; }
|
|
}
|
|
/* Cell mouths: rings of energy collapse inward through the token. */
|
|
.veil {
|
|
fill: none;
|
|
stroke: #9be3e0;
|
|
stroke-dasharray: 7 5;
|
|
filter: drop-shadow(0 0 4px rgba(155, 227, 224, 0.8));
|
|
}
|
|
.void { animation: void-open 0.9s ease-out forwards; }
|
|
.veil.outer { stroke-width: 2.5; animation: veil-in 0.9s ease-in forwards; }
|
|
.veil.inner {
|
|
stroke: rgba(180, 138, 224, 0.8);
|
|
stroke-width: 2;
|
|
animation: veil-in 0.9s ease-in 0.2s forwards;
|
|
opacity: 0;
|
|
}
|
|
@keyframes void-open {
|
|
0% { opacity: 0; transform: scale(0.4); }
|
|
30% { opacity: 1; transform: scale(1); }
|
|
75% { opacity: 0.9; transform: scale(0.92); }
|
|
100% { opacity: 0; transform: scale(0.5); }
|
|
}
|
|
@keyframes veil-in {
|
|
0% { opacity: 0; transform: scale(1.25); }
|
|
25% { opacity: 1; }
|
|
100% { opacity: 0; transform: scale(0.45); }
|
|
}
|
|
</style>
|