Crowded squares: every token type gets its own zone, multiples fan out

A square can legally hold terrain, a wizard or three, two treasures
(the win condition itself), floor objects, creatures, a warp token,
and a boobytrap disc all at once. The cell is now zoned: terrain
underlays the whole square, wizards fan across the top, treasures fan
across the bottom, floor objects stack bottom-left, creatures fan in
from the right, the dimensional warp shrinks into the top-left
corner, and boobytrap discs sit top-right. Two treasures on a home
base — the winning tableau — now reads as two treasures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Eric Wagoner
2026-08-16 00:27:05 -04:00
co-authored by Claude Fable 5
parent bd8ca99dc0
commit 002dbd2547
+21 -8
View File
@@ -150,9 +150,15 @@
class="home-star" fill={playerColor(p.id)}
/>
{/each}
{#each view.treasures as t (t.id)}
{#each [...view.treasures.reduce((m, t) => {
if (!t.position) return m;
const k = `${t.position.x},${t.position.y}`;
m.set(k, [...(m.get(k) ?? []), t]);
return m;
}, new Map()).entries()] as [tKey, group] (tKey)}
{#each group as t, ti (t.id)}
{#if t.position}
{@const tx = t.position.x * CELL + CELL / 2}
{@const tx = t.position.x * CELL + CELL / 2 + (group.length > 1 ? (ti - (group.length - 1) / 2) * CELL * 0.3 : 0)}
{@const ty = t.position.y * CELL + CELL * 0.72}
{#if USE_TOKEN_ART}
<image
@@ -171,6 +177,7 @@
{/if}
{/if}
{/each}
{/each}
<!-- terrain: solid stone and thornbushes -->
{#each Object.entries(view.squareContents) as [key, content] (key)}
@@ -209,8 +216,8 @@
{#if USE_TOKEN_ART}
<image
href="/tokens/dimensional-warp.png"
x={tok.x * CELL + CELL * 0.16} y={tok.y * CELL + CELL * 0.16}
width={CELL * 0.68} height={CELL * 0.68}
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"
/>
{:else}
@@ -223,7 +230,7 @@
<!-- boobytrap tokens: face-down for everyone (the caster knows the real one) -->
{#each view.boobytraps as trap, ti (ti)}
{#each trap.cells as tc, i (i)}
<circle cx={tc.x * CELL + CELL * 0.26} cy={tc.y * CELL + CELL * 0.26} r="6"
<circle cx={tc.x * CELL + CELL * 0.82} cy={tc.y * CELL + CELL * 0.18} r="6"
class="trap-token" class:trap-real={trap.realCell != null && tc.x === trap.realCell.x && tc.y === trap.realCell.y} />
{/each}
{/each}
@@ -353,9 +360,14 @@
{/each}
{/each}
<!-- creatures -->
{#each view.creatures as c (c.id)}
{@const ccx = c.position.x * CELL + CELL * 0.72}
<!-- creatures (fanned when several share a square) -->
{#each [...view.creatures.reduce((m, c) => {
const k = `${c.position.x},${c.position.y}`;
m.set(k, [...(m.get(k) ?? []), c]);
return m;
}, new Map()).entries()] as [cKey, cGroup] (cKey)}
{#each cGroup as c, ci (c.id)}
{@const ccx = c.position.x * CELL + CELL * 0.72 - (cGroup.length > 1 ? ci * CELL * 0.26 : 0)}
{@const ccy = c.position.y * CELL + CELL * 0.7}
<g
role="button" tabindex="-1" class="creature"
@@ -394,6 +406,7 @@
{/if}
</g>
{/each}
{/each}
<!-- edge selection hitboxes -->
{#each edgeHitboxes as h (`${h.cell.x},${h.cell.y},${h.side}`)}