From 002dbd2547daeeab9ec8452fd6707f56c1c8f950 Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Sun, 16 Aug 2026 00:27:05 -0400 Subject: [PATCH] Crowded squares: every token type gets its own zone, multiples fan out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- packages/web/src/Board.svelte | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/packages/web/src/Board.svelte b/packages/web/src/Board.svelte index 3870a85..d9317b7 100644 --- a/packages/web/src/Board.svelte +++ b/packages/web/src/Board.svelte @@ -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} {#each Object.entries(view.squareContents) as [key, content] (key)} @@ -209,8 +216,8 @@ {#if USE_TOKEN_ART} {:else} @@ -223,7 +230,7 @@ {#each view.boobytraps as trap, ti (ti)} {#each trap.cells as tc, i (i)} - {/each} {/each} @@ -353,9 +360,14 @@ {/each} {/each} - - {#each view.creatures as c (c.id)} - {@const ccx = c.position.x * CELL + CELL * 0.72} + + {#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} {/each} + {/each} {#each edgeHitboxes as h (`${h.cell.x},${h.cell.y},${h.side}`)}