The peeked token joins its card — and stops hiding behind it
Clicking a board token now shows the thing itself, enlarged, above its card in the peek modal — whichever art is active, photograph or SVG. The art tables move to art.ts so the board renderer and the modal share one source. First cut overlapped: the card scales 2.1x out of its layout box, so flow neighbors sat underneath its visual footprint (the note had been compensating with a 6.8rem margin hack). A card-stage now reserves the card's TRUE visual size; token above, note below, nothing buried. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
1d015681d3
commit
4528c66057
@@ -8,7 +8,7 @@
|
||||
import Replay from "./Replay.svelte";
|
||||
import FxGallery from "./FxGallery.svelte";
|
||||
import { fxForEvents, fxTtl, type BoardFx } from "./fx";
|
||||
import { tokenArt } from "./art";
|
||||
import { CREATURE_ART, objectArt, TERRAIN_ART, 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";
|
||||
@@ -63,6 +63,8 @@
|
||||
let peekCard = $state<CardInstance | null>(null);
|
||||
/** When the peeked card is a creature on the board, its live stats ride along. */
|
||||
let peekCreatureId = $state<string | null>(null);
|
||||
/** The clicked token itself, enlarged beside its card. */
|
||||
let peekToken = $state<string | null>(null);
|
||||
/** Bare-knuckle demolition: click a wall to punch it. */
|
||||
let punchWallMode = $state(false);
|
||||
/** Leafing through the face-up discard pile. */
|
||||
@@ -342,6 +344,7 @@
|
||||
if (!view) return;
|
||||
peekCard = null;
|
||||
peekCreatureId = null;
|
||||
peekToken = null;
|
||||
if (youMustRespond || youMustShield) {
|
||||
if (youMustRespond && card.cardId === "teleport") {
|
||||
selectedCard = card; // then click the escape square
|
||||
@@ -667,6 +670,13 @@
|
||||
if (!cardId) return false;
|
||||
peekCard = { instanceId: `peek-${cardId}`, cardId };
|
||||
peekCreatureId = creature?.id ?? null;
|
||||
peekToken =
|
||||
creature && CREATURE_ART[creature.kind] ? tokenArt(CREATURE_ART[creature.kind]!, "creatures")
|
||||
: content && TERRAIN_ART[content.kind] ? tokenArt(TERRAIN_ART[content.kind]!, "terrain")
|
||||
: objects.length > 0 && objectArt(cardId) ? tokenArt(objectArt(cardId)!, "objects")
|
||||
: cardId === "dimensional-warp" ? tokenArt("dimensional-warp", "terrain")
|
||||
: cardId === "boobytrap" ? tokenArt("boobytrap", "terrain")
|
||||
: null;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -942,9 +952,14 @@
|
||||
|
||||
{#if peekCard || discardPeek}
|
||||
<div class="big-peek-scrim" role="button" tabindex="-1"
|
||||
onclick={() => { peekCard = null; peekCreatureId = null; discardPeek = null; }} onkeydown={() => {}}>
|
||||
onclick={() => { peekCard = null; peekCreatureId = null; peekToken = null; discardPeek = null; }} onkeydown={() => {}}>
|
||||
<div class="big-peek">
|
||||
<Card card={(peekCard ?? discardPeek)!} onfaq={(id) => (faqCardId = id)} />
|
||||
{#if peekCard && peekToken}
|
||||
<img class="big-peek-token" src={peekToken} alt="" />
|
||||
{/if}
|
||||
<div class="card-stage">
|
||||
<Card card={(peekCard ?? discardPeek)!} onfaq={(id) => (faqCardId = id)} />
|
||||
</div>
|
||||
{#if peekNote}<div class="peek-note big-peek-note">{peekNote}</div>{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -2276,7 +2291,22 @@
|
||||
}
|
||||
.big-peek :global(.card:hover) { transform: scale(2.1); }
|
||||
.big-peek { display: flex; flex-direction: column; align-items: center; }
|
||||
.big-peek-note { margin-top: 6.8rem; max-width: 15rem; font-size: 0.8rem; }
|
||||
.big-peek-note { margin-top: 0.7rem; max-width: 15rem; font-size: 0.8rem; }
|
||||
/* The card scales 2.1x out of its layout box; the stage reserves its
|
||||
TRUE visual footprint so neighbors never sit underneath it. */
|
||||
.card-stage {
|
||||
width: calc(8.2rem * 2.1);
|
||||
height: calc(11.4rem * 2.1);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
.big-peek-token {
|
||||
width: 6.5rem;
|
||||
height: 6.5rem;
|
||||
object-fit: contain;
|
||||
margin-bottom: 0.8rem;
|
||||
filter: drop-shadow(2px 4px 8px rgba(0, 0, 0, 0.6));
|
||||
}
|
||||
.creature-dock {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -2,7 +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 { CREATURE_ART, objectArt, TERRAIN_ART, tokenArt } from "./art";
|
||||
import TokenArt from "./TokenArt.svelte";
|
||||
import { FX_SPRITES } from "./fx-sprites";
|
||||
|
||||
@@ -90,24 +90,6 @@
|
||||
// Real token art, cropped from the owner's physical set (public/tokens/).
|
||||
// Anything without an entry falls back to the vector rendering below.
|
||||
const USE_TOKEN_ART = true;
|
||||
const TERRAIN_ART: Record<string, string> = {
|
||||
stone: "solid-stone", thornbush: "thorn-bush", rosebush: "rosebush",
|
||||
ooze: "killer-ooze", dust: "dustcloud", slime: "slime",
|
||||
tacks: "tacks", pit: "pit", safe: "safe",
|
||||
};
|
||||
const CREATURE_ART: Record<string, string> = {
|
||||
skeleton: "skeleton", troll: "troll", wraith: "wraith",
|
||||
"fire-imp": "fire-imp", "democratic-monster": "democratic-monster",
|
||||
shadow: "shadow", "alter-ego": "alter-ego",
|
||||
};
|
||||
function objectArt(cardId: string): string | null {
|
||||
if (cardId === "dagger") return "dagger";
|
||||
if (cardId === "large-rock") return "rock";
|
||||
if (cardId === "master-key") return "master-key";
|
||||
if (cardId.endsWith("stone")) return "magic-stone";
|
||||
if (cardId.endsWith("-wand")) return "magic-wand";
|
||||
return null;
|
||||
}
|
||||
// Ongoing spells wear their look: ghostly when unseen, webbed when caught,
|
||||
// stone-gray under Medusa's gaze.
|
||||
function hasSpell(id: string, cardId: string): boolean {
|
||||
|
||||
@@ -15,3 +15,24 @@ const SVG_ART: Record<ArtCategory, boolean> = {
|
||||
export function tokenArt(file: string, cat: ArtCategory): string {
|
||||
return SVG_ART[cat] ? `/tokens-svg/${file}.svg` : `/tokens/${file}.png`;
|
||||
}
|
||||
|
||||
// Which token file depicts each board thing (shared by the board renderer
|
||||
// and the card-peek modal). Anything without an entry has no token art.
|
||||
export const TERRAIN_ART: Record<string, string> = {
|
||||
stone: "solid-stone", thornbush: "thorn-bush", rosebush: "rosebush",
|
||||
ooze: "killer-ooze", dust: "dustcloud", slime: "slime",
|
||||
tacks: "tacks", pit: "pit", safe: "safe",
|
||||
};
|
||||
export const CREATURE_ART: Record<string, string> = {
|
||||
skeleton: "skeleton", troll: "troll", wraith: "wraith",
|
||||
"fire-imp": "fire-imp", "democratic-monster": "democratic-monster",
|
||||
shadow: "shadow", "alter-ego": "alter-ego",
|
||||
};
|
||||
export function objectArt(cardId: string): string | null {
|
||||
if (cardId === "dagger") return "dagger";
|
||||
if (cardId === "large-rock") return "rock";
|
||||
if (cardId === "master-key") return "master-key";
|
||||
if (cardId.endsWith("stone")) return "magic-stone";
|
||||
if (cardId.endsWith("-wand")) return "magic-wand";
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user