{#if fxWorkshop} {:else}
Wiz-War sixth edition {#if local.active} hotseat {:else if net.roomId} room {net.roomId} {/if} {net.status === "connected" ? "" : "reconnecting…"}
{#if showDiscards && view}
(showDiscards = false)} onkeydown={() => {}}>
{/if} {#if peekCard || discardPeek}
{ peekCard = null; peekCreatureId = null; peekToken = null; discardPeek = null; }} onkeydown={() => {}}>
{#if peekCard && peekToken} {/if}
(faqCardId = id)} />
{#if peekNote}
{peekNote}
{/if}
{/if} {#if faqCardId} (faqCardId = null)} /> {/if} {#if showHelp} net.requestStats()} onclose={() => (showHelp = false)} /> {/if} {#if view?.phase === "finished" && view.winner && victoryCurtain && !victorySeen}
🏆
{view.winner}
{#if view.winReason === "treasures"} carried two stolen treasures home — the maze is theirs {:else if view.winReason === "lastStanding"} the last wizard standing {:else} wins the game {/if}
{/if} {#if trapNotice}
A cruel draw!
{trapNotice === "gift-from-below" ? "It bites for 3 points, then the deck deals you another." : "The trap snaps shut — you lose a turn."}
{/if} {#if view && view.outOfTurnWindow?.playerId === view.you && !momentSeen}
The maze holds its breath
Time parts for your {cardDef(view.outOfTurnWindow.kind).name} — pick an attack card and a target, or let the moment pass.
{/if} {#if openingRolls}
The dice decide who casts first
{#each openingRolls.players as p (p)}
{p === openingRolls.first ? "🏆 " : ""}{p} {#each openingRolls.rolls[p] ?? [] as r, i (i)}🎲{r}{/each}
{/each}
{#if Object.values(openingRolls.rolls).some((r) => r.length > 1)}
Tied wizards rolled again.
{/if}
{/if} {#if view?.stack && attackNoticeKey && attackNoticeSeen !== attackNoticeKey && !noFanfare}
{#if view.stack.defenderId === view.you && attackingCreature} {view.stack.attackerId}'s {cardDef(attackingCreature.kind).name} attacks you! {:else if view.stack.defenderId === view.you} {view.stack.attackerId} attacks you! {:else} {view.stack.counters[view.stack.counters.length - 1]?.player} counters your spell! {/if}
{#if view.stack.defenderId === view.you && attackingCreature}
{:else if view.stack.defenderId === view.you && view.stack.attackCard} {@const illusionOf = view.stack.attackCard.cardId === "illusionary-attack" ? (view.stack.params?.cardId ?? null) : null} {#if illusionOf}
(faqCardId = id)} /> (faqCardId = id)} />
an ILLUSIONARY {cardDef(illusionOf).name} — it bites only if believed
{:else}
{/if} {#if view.stack.numberValue != null || view.stack.amplifyFactor > 1}
{view.stack.numberValue != null ? `powered by a ${view.stack.numberValue}` : ""} {view.stack.amplifyFactor > 1 ? ` — AMPLIFIED ×${view.stack.amplifyFactor}` : ""}
{/if} {:else if view.stack.defenderId === view.you}
👊 a bare-knuckled punch
{:else if view.stack.counters.length > 0}
{/if} {#if view.stack.defenderId === view.you && view.stack.counters.length > 0} {@const standing = view.stack.counters.filter((c) => c.player === view.you && !c.nullified)} {@const broken = view.stack.counters.filter((c) => c.player === view.you && c.nullified)} {#if broken.length > 0 && standing.length === 0}
Your {broken.map((c) => cardDef(c.card.cardId).name).join(" and ")} {broken.length === 1 ? "was" : "were"} nullified — the attack comes on unchecked.
{:else if standing.length > 0}
Your {standing.map((c) => cardDef(c.card.cardId).name).join(" and ")} stand{standing.length === 1 ? "s" : ""} — only what slips past {standing.length === 1 ? "it" : "them"} will land. Counter again, or let it resolve.
{/if} {/if}
{/if} {#if net.catchUp && net.catchUp.length > 0} net.closeCatchUp()} /> {:else if local.replaySteps && local.replaySteps.length > 0} (local.replaySteps = null)} /> {/if} {#if net.error} {/if} {#if net.transferCode}
Speak this phrase into your other device (good for 10 minutes, one use): {net.transferCode.code}
{/if} {#if local.setup}
wizard {local.setup.names.length + 1} of {local.setup.count}
What is your name?
{#each [0, 1, 2, 3, 4, 5] as c (c)} {@const taken = local.setup.colors.includes(c)} {/each}
{ e.preventDefault(); const err = local.submitName(setupName, setupColor); if (err) net.error = err; else setupName = ""; }}>
{#if local.setup.names.length > 0}
so far: {local.setup.names.join(", ")}
{/if}
{:else if local.handoffTo}
local.takeSeat()} onkeydown={(e) => e.key === "Enter" && local.takeSeat()}>
pass the device to
{local.handoffTo}
tap when only they can see the screen
{/if} {#if !net.roomId && !local.active}
Wiz-War
A game of magical combat in a stone labyrinth
or join one
or play here, passing the device:
{#each [2, 3, 4, 5, 6] as n (n)} {/each}
{#if local.hasSave()} {/if}
{#if net.seats.length > 0}
your games {#if !net.notificationsEnabled} {/if} {#if noFanfare} {/if}
{#each net.seats as seat (seat.roomId + seat.name)} {@const g = net.games.find((x) => x.roomId === seat.roomId && x.name === seat.name)}
{/each}
{/if}
{:else if !local.active && !net.started}
Room {net.roomId}
Share the code. Two to six wizards enter the maze.
    {#each net.players as p (p)} {@const chosen = net.roomColors[p]}
  • {#if chosen !== undefined} {:else} {/if} {p}{p === net.hostId ? " — host" : ""}{net.roomBots[p] ? ` ⚙ ${net.roomBots[p]}` : chosen === undefined ? " — choosing…" : ""}
  • {/each}
{#each [0, 1, 2, 3, 4, 5] as c (c)} {@const takenBy = Object.entries(net.roomColors).find(([, v]) => v === c)?.[0]} {/each}
{#if net.you === net.hostId} {#if net.players.length < 6} ⚙ seat a {/if} {:else}

Waiting for {net.hostId} to flip the boards…

{/if}
{:else if view}
0 ? trapCells : null} effects={boardFx} markedSector={pendingSectorOrigin} ghostSlots={relocateGhosts} onGhostClick={clickGhostSlot} onCellPeek={(c) => peekAt(c)} {litCells} {sightTrace} />
{#if ambushVia}
{cardDef(ambushVia.cardId).name} — set an ambush {#if !ambushTrigger} springs when an opponent… {:else if !ambushSpell} — now tap the attack card to commit {:else} {cardDef(ambushSpell.cardId).name}{attachedNumber ? ` with a ${numberTotal}` : " (tap a number to power it)"} {/if}
{/if} {#if youMustRespond || selectedDef || selectedCreature || youMustDiscard || discardMode || discardSelection.size > 0}
{#if selectedCreature && !selectedDef} {@const sc = view.creatures.find((c) => c.id === selectedCreature)} {#if sc} Commanding {cardDef(sc.kind).name} — {creatureStats(sc)}. Tap a square beside it to march, a target in its square to attack. {/if} {/if} {#if youMustRespond && view.stack} {#if view.stack.defenderId === view.you && selectedCard?.cardId === "teleport"} {pendingTeleport ? "Escaping by teleport — tap the marked square again to jump." : "Escaping by teleport — tap a square within four spaces to mark it."} {:else if view.stack.defenderId === view.you} {view.stack.attackerId} attacks with {attackingCreature ? `the ${cardDef(attackingCreature.kind).name}` : view.stack.attackCard?.cardId === "illusionary-attack" && view.stack.params?.cardId ? `an illusionary ${cardDef(view.stack.params.cardId).name}` : view.stack.attackCard ? cardDef(view.stack.attackCard.cardId).name : "a punch"} — tap a counteraction card, or {:else} {view.stack.waitingOn === view.you ? "They counter your spell — counter back, or" : ""} {/if} {/if} {#if youMustDiscard} Hand over the limit — mark cards and discard. {:else if discardMode && discardSelection.size === 0} Mark the cards to throw away, then confirm. {/if} {#if discardSelection.size > 0} {/if} {#if discardMode || (discardSelection.size > 0 && !youMustDiscard)} {/if} {#if selectedDef} {selectedDef.name} {#if selectedCard && EDGE_CARDS.has(selectedCard.cardId)} — {EDGE_HINTS[selectedCard.cardId] ?? "click a wall line"} {/if} {#if selectedCard && WAND_IDS.has(selectedCard.cardId) && view.wandCharges[selectedCard.instanceId] == null && !attachedNumber} — first use: tap a NUMBER card to set the wand's charges, then click a target {:else if selectedDef.cardType === "attack" && !cellSelectMode && !EDGE_CARDS.has(selectedCard?.cardId ?? "")} — click a target, or a wall line to batter it{attachedNumber ? ` (powered by a ${numberTotal})` : ""} {/if} {#if attachedMods.length > 0} [+ {attachedMods.map((m) => cardDef(m.cardId).name).join(", ")}] {/if} {#if selectedCard?.cardId === "boobytrap"} — place 4 tokens ({trapCells.length}/4; the first is real) {/if} {#if selectedCard?.cardId === "redirection"} {tradeFrom ? "— now the exit it should connect TO" : "— click the first exit"} {:else if selectedCard && TWO_CELL_CARDS.has(selectedCard.cardId)} {tradeFrom ? "— now the second square" : "— click the first square"} {/if} {#if selectedCard?.cardId === "teleport" && !youMustRespond} {pendingTeleport ? "— tap the marked square again to jump, or pick another" : "— tap a destination (up to four squares, walls ignored)"} {#if pendingTeleport} {/if} {/if} {#if selectedCard?.cardId === "mega-monster"} — double its — then tap the monster {/if} {#if selectedCard?.cardId === "pick-lock" || selectedCard?.cardId === "master-key"} {/if} {#if selectedCard?.cardId === "rotate-sector"} — click the sector {/if} {#if selectedCard?.cardId === "relocate-sector"} {pendingSectorFrom ? (relocateGhosts?.length ? "— now tap a dashed landing beside the maze" : "— that sector has nowhere legal to go; pick another") : "— click the sector to move"} {/if} {#if selectedCard && NAMED_CARDS.has(selectedCard.cardId)} {#each nameSuggestions as n (n)}{/each} {#if selectedCard.cardId === "deja-vu"} {:else} — then click the target {/if} {/if} {#if selectedCard?.cardId === "waterbolt"} (knockback {numberTotal - wbDamage}) {/if} {#if selectedCard?.cardId === "power-run"} {/if} {#if selectedCard && CONFIRM_CAST.has(selectedCard.cardId)} {/if} {#if selectedCard && isMovableObject(selectedCard.cardId) && isYourTurn} {/if} {#if selectedCard?.cardId === "add"} — ADD rides a second NUMBER: play one number for movement, then play another, and the Add spends itself {/if} {#if selectedCard && isNumberCard(selectedCard.cardId)} {@const needsAdd = view.turn.numberPlayedForMovement} {@const haveAdd = view.yourHand.some((c) => c.cardId === "add")} {#if needsAdd && !haveAdd} — a second movement number needs an ADD card {:else} {/if} {/if} {/if}
{/if}
{#if isYourTurn && onWarpToken} {/if} {#if isYourTurn} {#if pickChoice} — whose? {#each treasuresHere as t (t.id)} {/each} {/if} {#each objectsHere as obj (obj.instanceId)} {/each} {#if holdingWard} {/if} {/if}
{#if selectedCard && !inspectorHidden} {/if} {#if view.phase === "finished" && view.revealedHands}
The hands, face-up
{#each view.players as p (p.id)}
{p.id === view.winner ? "🏆 " : ""}{p.id} {#if !p.alive}as they fell{/if}
{#each view.revealedHands[p.id] ?? [] as card (card.instanceId)} (peekCard = card)} onfaq={(id) => (faqCardId = id)} /> {/each} {#if (view.revealedHands[p.id] ?? []).length === 0} empty-handed {/if}
{/each}
{/if}
{#each view.phase === "finished" ? [] : view.yourHand as card (card.instanceId)} m.instanceId === card.instanceId)} marked={discardSelection.has(card.instanceId)} displayed={view.players.find((p) => p.id === view.you)?.displayed.some((c) => c.instanceId === card.instanceId) ?? false} glowing={interruptCards.some((c) => c.instanceId === card.instanceId)} charges={view.wandCharges[card.instanceId] ?? null} onclick={() => selectCard(card)} onfaq={(id) => (faqCardId = id)} /> {/each}
{/if}
{/if}