A slime shows how many spells wait in it, and a peek names them
Every cast into the gel was seen at the table, so the count is public: a badge on the slime, and the peek lists each spell with its caster. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
16fac5e6ca
commit
44c7ff8379
@@ -60,6 +60,8 @@ export interface GameView {
|
|||||||
/** Duration spells in play (public knowledge). */
|
/** Duration spells in play (public knowledge). */
|
||||||
sustained: SustainedEffect[];
|
sustained: SustainedEffect[];
|
||||||
squareContents: Record<string, SquareContent>;
|
squareContents: Record<string, SquareContent>;
|
||||||
|
/** Spells lodged in slime, oldest first (public: each cast was seen). */
|
||||||
|
slimeTraps: Record<string, { cardId: string; casterId: PlayerId }[]>;
|
||||||
/** Cells whose contents are glued down (public: the cast was seen). */
|
/** Cells whose contents are glued down (public: the cast was seen). */
|
||||||
gluedCells: Record<string, true>;
|
gluedCells: Record<string, true>;
|
||||||
/** Safes standing open (their combination entered this turn). */
|
/** Safes standing open (their combination entered this turn). */
|
||||||
@@ -156,6 +158,9 @@ export function viewFor(state: GameState, playerId: PlayerId): GameView {
|
|||||||
pendingDiscard: state.pendingDiscard,
|
pendingDiscard: state.pendingDiscard,
|
||||||
sustained: state.sustained.map((s) => ({ ...s })),
|
sustained: state.sustained.map((s) => ({ ...s })),
|
||||||
squareContents: { ...state.squareContents },
|
squareContents: { ...state.squareContents },
|
||||||
|
slimeTraps: Object.fromEntries(
|
||||||
|
Object.entries(state.slimeTraps).map(([k, traps]) => [k, traps.map((t) => ({ cardId: t.card.cardId, casterId: t.casterId }))]),
|
||||||
|
),
|
||||||
gluedCells: { ...state.gluedCells },
|
gluedCells: { ...state.gluedCells },
|
||||||
openSafes: [...state.openSafes],
|
openSafes: [...state.openSafes],
|
||||||
groundObjects: Object.fromEntries(
|
groundObjects: Object.fromEntries(
|
||||||
|
|||||||
@@ -1003,10 +1003,21 @@
|
|||||||
const atk = c.justCreated ? "no attack the turn it appears" : c.attackUsed ? "attack spent" : "attack ready";
|
const atk = c.justCreated ? "no attack the turn it appears" : c.attackUsed ? "attack spent" : "attack ready";
|
||||||
return `${hp} · ${moves} · ${atk}`;
|
return `${hp} · ${moves} · ${atk}`;
|
||||||
}
|
}
|
||||||
|
/** The square a peek is looking at, for notes the card alone cannot carry. */
|
||||||
|
let peekCell = $state<{ x: number; y: number } | null>(null);
|
||||||
const peekNote = $derived.by(() => {
|
const peekNote = $derived.by(() => {
|
||||||
if (!view || !peekCreatureId) return null;
|
if (!view) return null;
|
||||||
const c = view.creatures.find((c) => c.id === peekCreatureId);
|
if (peekCreatureId) {
|
||||||
return c ? creatureStats(c) : null;
|
const c = view.creatures.find((c) => c.id === peekCreatureId);
|
||||||
|
return c ? creatureStats(c) : null;
|
||||||
|
}
|
||||||
|
if (peekCell && view.squareContents[cellKey(peekCell)]?.kind === "slime") {
|
||||||
|
const waiting = view.slimeTraps?.[cellKey(peekCell)] ?? [];
|
||||||
|
if (waiting.length > 0) {
|
||||||
|
return `Waiting in the gel for the next wizard in: ${waiting.map((t) => `${cardDef(t.cardId).name} (${t.casterId})`).join(", ")}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Show the card behind whatever occupies this square (view-only). */
|
/** Show the card behind whatever occupies this square (view-only). */
|
||||||
@@ -1026,6 +1037,7 @@
|
|||||||
if (!cardId) return false;
|
if (!cardId) return false;
|
||||||
peekCard = { instanceId: `peek-${cardId}`, cardId };
|
peekCard = { instanceId: `peek-${cardId}`, cardId };
|
||||||
peekCreatureId = creature?.id ?? null;
|
peekCreatureId = creature?.id ?? null;
|
||||||
|
peekCell = { ...cell };
|
||||||
peekToken =
|
peekToken =
|
||||||
creature && CREATURE_ART[creature.kind] ? tokenArt(CREATURE_ART[creature.kind]!, "creatures")
|
creature && CREATURE_ART[creature.kind] ? tokenArt(CREATURE_ART[creature.kind]!, "creatures")
|
||||||
: content && TERRAIN_ART[content.kind] ? tokenArt(TERRAIN_ART[content.kind]!, "terrain")
|
: content && TERRAIN_ART[content.kind] ? tokenArt(TERRAIN_ART[content.kind]!, "terrain")
|
||||||
|
|||||||
@@ -373,6 +373,13 @@
|
|||||||
{:else if content.kind === "safe"}
|
{:else if content.kind === "safe"}
|
||||||
<rect x={sx * CELL + 10} y={sy * CELL + 12} width={CELL - 20} height={CELL - 22} rx="3" class="safe" />
|
<rect x={sx * CELL + 10} y={sy * CELL + 12} width={CELL - 20} height={CELL - 22} rx="3" class="safe" />
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if content.kind === "slime" && (view.slimeTraps?.[key]?.length ?? 0) > 0}
|
||||||
|
<!-- spells waiting in the gel: everyone saw them cast -->
|
||||||
|
<g class="slime-charge" transform={`translate(${sx * CELL + CELL - 9}, ${sy * CELL + 9})`}>
|
||||||
|
<circle r="7" />
|
||||||
|
<text y="3.5">{view.slimeTraps[key]!.length}</text>
|
||||||
|
</g>
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
<!-- dimensional warp tokens -->
|
<!-- dimensional warp tokens -->
|
||||||
@@ -765,6 +772,9 @@
|
|||||||
.rose { fill: #2e7d32; stroke: #b0245a; stroke-width: 3; }
|
.rose { fill: #2e7d32; stroke: #b0245a; stroke-width: 3; }
|
||||||
.ooze { fill: #58a12b; opacity: 0.85; }
|
.ooze { fill: #58a12b; opacity: 0.85; }
|
||||||
.slime { fill: #8ec52e; opacity: 0.85; }
|
.slime { fill: #8ec52e; opacity: 0.85; }
|
||||||
|
.slime-charge { pointer-events: none; }
|
||||||
|
.slime-charge circle { fill: #3b2a6b; stroke: #f2cf5b; stroke-width: 1.5; }
|
||||||
|
.slime-charge text { fill: #f7ecc8; font-size: 10px; font-weight: 700; text-anchor: middle; font-family: "Oswald", sans-serif; }
|
||||||
.dust { fill: #9b9184; opacity: 0.75; }
|
.dust { fill: #9b9184; opacity: 0.75; }
|
||||||
.pit { fill: #171512; }
|
.pit { fill: #171512; }
|
||||||
.tacks { font-size: 15px; text-anchor: middle; fill: #444; }
|
.tacks { font-size: 15px; text-anchor: middle; fill: #444; }
|
||||||
|
|||||||
Reference in New Issue
Block a user