The art workshop's side door
The hand-drawn SVG token set wires in behind deliberately undocumented query params — svgPlayers, svgCreatures, svgTerrain, svgObjects, each =true — swapping that category's art everywhere it draws: board tokens, lobby standees, roster. public/tokens-svg symlinks research/tokens-svg, so under the dev server an edit to the source art shows on the next refresh, and the production build copies real files through the link. Nobody finds the door unless handed the key; the photographed set remains the face of the game. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
93bdbb403a
commit
e0e96ca5ca
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../../research/tokens-svg
|
||||
@@ -8,6 +8,7 @@
|
||||
import Replay from "./Replay.svelte";
|
||||
import FxGallery from "./FxGallery.svelte";
|
||||
import { fxForEvents, fxTtl, type BoardFx } from "./fx";
|
||||
import { tokenArt } from "./art";
|
||||
import { local } from "./local.svelte";
|
||||
import { allCardDefs, cardDef, isNumberCard, SIDES, stepTarget, cellKey, isMovableObject, sightedCellsFor, type GameView, eligibleCellsFor } from "@wizwar/engine";
|
||||
import type { CardInstance, Side } from "@wizwar/engine";
|
||||
@@ -1076,7 +1077,7 @@
|
||||
onclick={() => (setupColor = c)}
|
||||
aria-label={`wizard color ${c + 1}${taken ? " (taken)" : ""}`}
|
||||
>
|
||||
<img src={`/tokens/wizard-${c}.png`} alt="" />
|
||||
<img src={tokenArt(`wizard-${c}`, "players")} alt="" />
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -1211,7 +1212,7 @@
|
||||
{@const chosen = net.roomColors[p]}
|
||||
<li>
|
||||
{#if chosen !== undefined}
|
||||
<img class="roster-standee" src={`/tokens/wizard-${chosen}.png`} alt="" />
|
||||
<img class="roster-standee" src={tokenArt(`wizard-${chosen}`, "players")} alt="" />
|
||||
{:else}
|
||||
<span class="dot" style:background="#b3a687"></span>
|
||||
{/if}
|
||||
@@ -1231,7 +1232,7 @@
|
||||
onclick={() => net.pickColor(c)}
|
||||
aria-label={`wizard color ${c + 1}${takenBy && takenBy !== net.you ? ` (taken by ${takenBy})` : ""}`}
|
||||
>
|
||||
<img src={`/tokens/wizard-${c}.png`} alt="" />
|
||||
<img src={tokenArt(`wizard-${c}`, "players")} alt="" />
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import type { GameView } from "@wizwar/engine";
|
||||
import type { Side } from "@wizwar/engine";
|
||||
import { colorIndexOf as sharedColorIndex, wizardColor } from "./colors";
|
||||
import { tokenArt } from "./art";
|
||||
import { FX_SPRITES } from "./fx-sprites";
|
||||
|
||||
const CELL = 48;
|
||||
@@ -119,10 +120,10 @@
|
||||
return 1;
|
||||
}
|
||||
function wizardArt(id: string): string {
|
||||
return `/tokens/wizard-${sharedColorIndex(view, id) % 6}.png`;
|
||||
return tokenArt(`wizard-${sharedColorIndex(view, id) % 6}`, "players");
|
||||
}
|
||||
function treasureArt(owner: string): string {
|
||||
return `/tokens/treasure-${sharedColorIndex(view, owner) % 6}.png`;
|
||||
return tokenArt(`treasure-${sharedColorIndex(view, owner) % 6}`, "objects");
|
||||
}
|
||||
|
||||
function playerColor(id: string): string {
|
||||
@@ -298,7 +299,7 @@
|
||||
{@const sy = Number(key.split(",")[1])}
|
||||
{#if USE_TOKEN_ART && TERRAIN_ART[content.kind]}
|
||||
<image
|
||||
href={`/tokens/${TERRAIN_ART[content.kind]}.png`}
|
||||
href={tokenArt(TERRAIN_ART[content.kind]!, "terrain")}
|
||||
x={sx * CELL + 3} y={sy * CELL + 3}
|
||||
width={CELL - 6} height={CELL - 6}
|
||||
preserveAspectRatio="xMidYMid slice"
|
||||
@@ -328,7 +329,7 @@
|
||||
{#each [w.a, w.b] as tok, i (i)}
|
||||
{#if USE_TOKEN_ART}
|
||||
<image
|
||||
href="/tokens/dimensional-warp.png"
|
||||
href={tokenArt("dimensional-warp", "terrain")}
|
||||
x={tok.x * CELL + CELL * 0.04} y={tok.y * CELL + CELL * 0.04}
|
||||
width={CELL * 0.44} height={CELL * 0.44}
|
||||
preserveAspectRatio="xMidYMid slice" class="token-art"
|
||||
@@ -356,7 +357,7 @@
|
||||
{@const art = USE_TOKEN_ART ? objectArt(o.cardId) : null}
|
||||
{#if art}
|
||||
<image
|
||||
href={`/tokens/${art}.png`}
|
||||
href={tokenArt(art, "objects")}
|
||||
x={gx * CELL + 4 + i * 9} y={gy * CELL + CELL - CELL * 0.42 - 3}
|
||||
width={CELL * 0.4} height={CELL * 0.4}
|
||||
preserveAspectRatio="xMidYMid slice"
|
||||
@@ -556,7 +557,7 @@
|
||||
>
|
||||
{#if USE_TOKEN_ART && CREATURE_ART[c.kind]}
|
||||
<image
|
||||
href={`/tokens/${CREATURE_ART[c.kind]}.png`}
|
||||
href={tokenArt(CREATURE_ART[c.kind]!, "creatures")}
|
||||
x={-CELL * 0.26} y={-CELL * 0.26}
|
||||
width={CELL * 0.52} height={CELL * 0.52}
|
||||
preserveAspectRatio="xMidYMid slice"
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// The art workshop's side door (deliberately undocumented): query params
|
||||
// like ?svgPlayers=true&svgTerrain=true swap token categories to the
|
||||
// hand-drawn SVG set. public/tokens-svg symlinks research/tokens-svg, so
|
||||
// under `npm run dev` an edit to the source art shows on refresh.
|
||||
export type ArtCategory = "players" | "creatures" | "terrain" | "objects";
|
||||
|
||||
const q = new URLSearchParams(typeof location === "undefined" ? "" : location.search);
|
||||
const SVG_ART: Record<ArtCategory, boolean> = {
|
||||
players: q.get("svgPlayers") === "true",
|
||||
creatures: q.get("svgCreatures") === "true",
|
||||
terrain: q.get("svgTerrain") === "true",
|
||||
objects: q.get("svgObjects") === "true",
|
||||
};
|
||||
|
||||
export function tokenArt(file: string, cat: ArtCategory): string {
|
||||
return SVG_ART[cat] ? `/tokens-svg/${file}.svg` : `/tokens/${file}.png`;
|
||||
}
|
||||
Reference in New Issue
Block a user