Card wave 3: terrain, thrown objects, drag, and control spells
Terrain layer: FILL SQUARE WITH STONE (impassable, blocks LOS via new cell-blocking sight checks), THORNBUSH (enter = 1 damage + turn ends + next turn lost; no attacking in or into a bush), WALL OF FIRE (new firewall edge state — passable for 4 magical damage, blocks LOS, expires with its duration), WATERWALL (instant wave: players within two spaces washed back two, 1 damage per blocked space), and DISPEL CREATION with provenance tracking (only conjured walls/fire/stone/ bushes dispel — printed maze is safe). Objects: DAGGER (3) and LARGE ROCK (2) are physical throws Full Shield cannot stop; they land on the floor and anyone may pick them up (ending their turn's actions, hand limit enforced); DROP OBJECT forces a named object or carried treasure to the ground; DRAG pulls floor objects, treasures, or players straight toward the caster. Control: LOCK IN PLACE (no moving or being moved — teleports, swaps, knockbacks and drags all respect it), BUDDY (a pact the caster breaks by attacking), MIST-BODY (through walls and doors, cannot attack or be attacked, still burns in firewalls), REUSE SPELL (retrieve your last spell). Client renders terrain, firewalls, and ground objects, with cell/edge/two-stage targeting and card-name inputs. 40 cards implemented; 63 tests pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
67743dd17e
commit
2d88b4ab40
@@ -101,19 +101,45 @@
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<!-- walls & doors -->
|
||||
<!-- terrain: solid stone and thornbushes -->
|
||||
{#each Object.entries(view.squareContents) as [key, content] (key)}
|
||||
{@const sx = Number(key.split(",")[0])}
|
||||
{@const sy = Number(key.split(",")[1])}
|
||||
{#if content.kind === "stone"}
|
||||
<rect x={sx * CELL + 2} y={sy * CELL + 2} width={CELL - 4} height={CELL - 4} class="stone" rx="4" />
|
||||
{:else}
|
||||
<circle cx={sx * CELL + CELL / 2} cy={sy * CELL + CELL / 2} r={CELL * 0.36} class="bush" />
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<!-- ground objects -->
|
||||
{#each Object.entries(view.groundObjects) as [key, objects] (key)}
|
||||
{@const gx = Number(key.split(",")[0])}
|
||||
{@const gy = Number(key.split(",")[1])}
|
||||
{#each objects as o, i (o.instanceId)}
|
||||
<rect
|
||||
x={gx * CELL + 6 + i * 8} y={gy * CELL + CELL - 16}
|
||||
width={12} height={10} rx="2" class="ground-object"
|
||||
>
|
||||
<title>{o.cardId}</title>
|
||||
</rect>
|
||||
{/each}
|
||||
{/each}
|
||||
|
||||
<!-- walls & doors & firewalls -->
|
||||
{#each edges as e (`${e.kind}:${e.x},${e.y}`)}
|
||||
{@const cls = e.state === "door" ? "door" : e.state === "firewall" ? "firewall" : "wall"}
|
||||
{#if e.kind === "V"}
|
||||
<rect
|
||||
x={(e.x + 1) * CELL - WALL / 2} y={e.y * CELL - WALL / 2}
|
||||
width={WALL} height={CELL + WALL}
|
||||
class={e.state === "door" ? "door" : "wall"}
|
||||
class={cls}
|
||||
/>
|
||||
{:else}
|
||||
<rect
|
||||
x={e.x * CELL - WALL / 2} y={(e.y + 1) * CELL - WALL / 2}
|
||||
width={CELL + WALL} height={WALL}
|
||||
class={e.state === "door" ? "door" : "wall"}
|
||||
class={cls}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
@@ -178,6 +204,10 @@
|
||||
.floor:hover { fill: #f2ecda; }
|
||||
.wall { fill: #4a4438; }
|
||||
.door { fill: #8b5a2b; }
|
||||
.firewall { fill: #e0442a; }
|
||||
.stone { fill: #6a6458; stroke: #3a362e; stroke-width: 2; }
|
||||
.bush { fill: #2e7d32; stroke: #1b4d1e; stroke-width: 2; }
|
||||
.ground-object { fill: #a6812e; stroke: #4a3a10; stroke-width: 1; }
|
||||
.home { font-size: 26px; text-anchor: middle; dominant-baseline: middle; opacity: 0.85; }
|
||||
.treasure { stroke: #111; stroke-width: 1.2; }
|
||||
.warp { font-size: 13px; text-anchor: middle; fill: #6a5f4b; font-weight: bold; }
|
||||
|
||||
Reference in New Issue
Block a user