The game has never used token art for edge effects — created walls, doors and their lock states, fire walls, illusions are all vector edge treatments. The ten unreferenced file pairs (wall, wall-2, door, fire-wall, illusion-wall, destroy-wall, jam-lock, lock-gone, redirection-a/b) are gone from both art sets, and the Token Workshop gains 'Walls & doors — as the maze draws them': every edge state (locked, ajar, held, removed, jammed), the standing fire, and both faces of an illusion, rendered by the same EdgeGlyph / FirewallEdge / IllusionShimmer components the board mounts. EdgeGlyph is newly extracted from Board so the display cannot drift. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138A8CjeQRpvzKxuMfz1Bqc
176 lines
6.2 KiB
Svelte
176 lines
6.2 KiB
Svelte
<script lang="ts">
|
|
// The token workshop: every token in both arts, side by side — the
|
|
// photographed physical set and the hand-drawn SVG contingency. Open
|
|
// with /?tokens under `npm run dev`; edits to public/tokens-svg/*.svg
|
|
// show on the next refresh. Board size and close-up for each.
|
|
import TokenArt from "./TokenArt.svelte";
|
|
|
|
const GROUPS: { title: string; files: string[] }[] = [
|
|
{ title: "Wizards", files: ["wizard-0", "wizard-1", "wizard-2", "wizard-3", "wizard-4", "wizard-5"] },
|
|
{ title: "Treasures", files: ["treasure-0", "treasure-1", "treasure-2", "treasure-3", "treasure-4", "treasure-5"] },
|
|
{
|
|
title: "Monsters",
|
|
files: ["troll", "skeleton", "wraith", "fire-imp", "democratic-monster", "shadow", "alter-ego"],
|
|
},
|
|
{
|
|
title: "Terrain",
|
|
files: [
|
|
"solid-stone", "thorn-bush", "rosebush", "killer-ooze", "slime", "dustcloud",
|
|
"tacks", "pit", "safe",
|
|
],
|
|
},
|
|
{
|
|
title: "Objects & markers",
|
|
files: [
|
|
"dagger", "rock", "magic-stone", "magic-wand", "master-key", "boobytrap",
|
|
"dimensional-warp",
|
|
],
|
|
},
|
|
];
|
|
|
|
// Wall-segment effects have never been tokens: the maze draws them as edge
|
|
// treatments. Shown here exactly as the board mounts them.
|
|
import EdgeGlyph from "./EdgeGlyph.svelte";
|
|
import FirewallEdge from "./FirewallEdge.svelte";
|
|
import IllusionShimmer from "./IllusionShimmer.svelte";
|
|
const EDGE_PIECES: {
|
|
caption: string;
|
|
state?: "wall" | "door";
|
|
lock?: "removed" | "jammed" | "held" | "ajar" | null;
|
|
special?: "firewall" | "shimmer" | "known-illusion";
|
|
}[] = [
|
|
{ caption: "wall", state: "wall" },
|
|
{ caption: "door (locked)", state: "door" },
|
|
{ caption: "door, picked open (ajar)", state: "door", lock: "ajar" },
|
|
{ caption: "door, held open", state: "door", lock: "held" },
|
|
{ caption: "door, lock removed", state: "door", lock: "removed" },
|
|
{ caption: "door, lock jammed", state: "door", lock: "jammed" },
|
|
{ caption: "wall of fire", special: "firewall" },
|
|
{ caption: "illusion wall (untested)", special: "shimmer" },
|
|
{ caption: "illusion wall (you know it's fake)", special: "known-illusion" },
|
|
];
|
|
</script>
|
|
|
|
<div class="gallery">
|
|
<h1>The Token Workshop</h1>
|
|
<p class="sub">
|
|
Every token, twice: the photographed physical set beside the hand-drawn
|
|
contingency. Edit <code>public/tokens-svg/</code> and refresh. Small is
|
|
board size; large is the close-up the peek modal shows.
|
|
<a href="/?fx">→ the flourish workshop</a>
|
|
</p>
|
|
{#each GROUPS as group (group.title)}
|
|
<h2>{group.title}</h2>
|
|
<div class="grid">
|
|
{#each group.files as f (f)}
|
|
<figure>
|
|
<div class="pair">
|
|
<div class="col">
|
|
<img class="big" src={`/tokens/${f}.png`} alt={`${f} (photo)`} />
|
|
<img class="small" src={`/tokens/${f}.png`} alt="" />
|
|
<span class="tag">photo</span>
|
|
</div>
|
|
<div class="col">
|
|
<svg viewBox="0 0 96 96" class="big">
|
|
<TokenArt href={`/tokens-svg/${f}.svg`} x={0} y={0} width={96} height={96} />
|
|
</svg>
|
|
<svg viewBox="0 0 28 28" class="small">
|
|
<TokenArt href={`/tokens-svg/${f}.svg`} x={0} y={0} width={28} height={28} />
|
|
</svg>
|
|
<span class="tag">drawn</span>
|
|
</div>
|
|
</div>
|
|
<figcaption>{f}</figcaption>
|
|
</figure>
|
|
{/each}
|
|
</div>
|
|
{/each}
|
|
|
|
<h2>Walls & doors — as the maze draws them</h2>
|
|
<p class="sub">
|
|
Wall-segment effects have never been tokens: locks picked, jammed, and
|
|
removed, held doors, standing fires, and illusions are edge treatments,
|
|
shown here with the very components the board mounts.
|
|
</p>
|
|
<div class="grid">
|
|
{#each EDGE_PIECES as piece (piece.caption)}
|
|
<figure>
|
|
<svg viewBox="20 20 104 104" class="edge-demo">
|
|
{#each [0, 1, 2] as cy (cy)}{#each [0, 1, 2] as cx (cx)}
|
|
<rect x={cx * 48} y={cy * 48} width="48" height="48" class="cell" />
|
|
{/each}{/each}
|
|
{#if piece.special === "firewall"}
|
|
<FirewallEdge x={1} y={0} kind="H" />
|
|
{:else if piece.special === "shimmer"}
|
|
<EdgeGlyph x={1} y={0} kind="H" state="wall" />
|
|
<IllusionShimmer x={1} y={0} kind="H" />
|
|
{:else if piece.special === "known-illusion"}
|
|
<line x1={48} y1={48} x2={96} y2={48} class="known-illusion" />
|
|
{:else}
|
|
<EdgeGlyph x={1} y={0} kind="H" state={piece.state!} lock={piece.lock ?? null} />
|
|
{/if}
|
|
</svg>
|
|
<figcaption>{piece.caption}</figcaption>
|
|
</figure>
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.gallery {
|
|
min-height: 100vh;
|
|
background: #171a20;
|
|
color: #d8d2c0;
|
|
font-family: "Archivo Narrow", system-ui, sans-serif;
|
|
padding: 1.5rem;
|
|
}
|
|
h1, h2 {
|
|
font-family: "Oswald", sans-serif;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.14em;
|
|
color: #e9e1cb;
|
|
margin: 0 0 0.2rem;
|
|
}
|
|
h1 { font-size: 1.2rem; }
|
|
h2 { font-size: 0.95rem; margin-top: 1.4rem; }
|
|
.sub { color: #8d8672; margin: 0 0 1rem; }
|
|
.sub code { color: #c9a72a; }
|
|
.sub a { color: #c9a72a; }
|
|
.grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
|
|
gap: 0.9rem;
|
|
}
|
|
figure {
|
|
margin: 0;
|
|
background: #efe8d4;
|
|
border-radius: 4px;
|
|
padding: 0.5rem;
|
|
}
|
|
.pair { display: flex; gap: 0.6rem; justify-content: center; }
|
|
.col { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; }
|
|
.big { width: 72px; height: 72px; display: block; }
|
|
.small { width: 28px; height: 28px; display: block; }
|
|
img.big, img.small { object-fit: cover; border-radius: 3px; }
|
|
.tag {
|
|
font-family: "Courier Prime", monospace;
|
|
font-size: 0.6rem;
|
|
color: #8a7a5e;
|
|
}
|
|
.edge-demo { width: 100%; display: block; }
|
|
.cell {
|
|
fill: #efe8d4;
|
|
stroke: rgba(95, 74, 51, 0.25);
|
|
stroke-width: 1;
|
|
}
|
|
/* Mirrors Board's known-illusion ghost: dashed, violet, see-through. */
|
|
.known-illusion { stroke: #7a6f9a; stroke-width: 4; stroke-dasharray: 6 5; opacity: 0.7; }
|
|
figcaption {
|
|
font-family: "Courier Prime", monospace;
|
|
font-size: 0.72rem;
|
|
color: #43331f;
|
|
text-align: center;
|
|
padding-top: 0.35rem;
|
|
}
|
|
</style>
|