The whole punch list: rev 8, general FAQ, and four promoted UX items
Rules rev 8 closes the last two fidelity threads. A SPEED bonus turn burns a turn of duration spells on the hastened wizard — recipient- counted, per the FAQ's most obscure ruling — with self-cast durations already burning through the normal turn-start sweep (expiry extracted into expireEffect so both paths share the cleanup). And nothing can be created on a DIMENSIONAL WARP token, as the card face always said. The general-topic FAQ sections (Combat, Line of Sight, Monsters, Treasures, and five more) join the rules tab verbatim under a double- ruled divider, completing the FAQ's absorption: card rulings on the cards, general rulings in the rules. The four promoted UX items: cell-target spells now dim ineligible squares (creations mirror emptySquareTarget from the viewer's knowledge, summons want clear sighted squares, teleport BFSes its four spaces exactly as the engine does, stone-to-water lights only stone, dispel lights only creations); hotseat games get the full replay reel, each step seen from its actor's own seat; a "stop announcing attacks on this device" link in the fanfare modal (undo lives beside the notification toggle in the lobby); the corner inspector now serves only the selected casting card, with every examine-a-card peek unified on the centered treatment; and the dev socket URL keys on vite's port instead of hijacking every localhost. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
f657144127
commit
d6d34887e3
@@ -7,7 +7,7 @@
|
||||
import Help from "./Help.svelte";
|
||||
import Replay from "./Replay.svelte";
|
||||
import { local } from "./local.svelte";
|
||||
import { allCardDefs, cardDef, isNumberCard, SIDES, stepTarget, cellKey, isMovableObject, sightedCellsFor, type GameView } from "@wizwar/engine";
|
||||
import { allCardDefs, cardDef, isNumberCard, SIDES, stepTarget, cellKey, isMovableObject, sightedCellsFor, type GameView, eligibleCellsFor } from "@wizwar/engine";
|
||||
import type { CardInstance, Side } from "@wizwar/engine";
|
||||
|
||||
net.connect();
|
||||
@@ -19,6 +19,12 @@
|
||||
let showHelp = $state(false);
|
||||
/** The finished game whose victory fanfare has been dismissed. */
|
||||
let victorySeen = $state(false);
|
||||
/** Quiet mode: skip the attack announcement modal (device preference). */
|
||||
let noFanfare = $state(localStorage.getItem("wizwar-no-fanfare") === "1");
|
||||
function setFanfare(announce: boolean) {
|
||||
noFanfare = !announce;
|
||||
localStorage.setItem("wizwar-no-fanfare", announce ? "0" : "1");
|
||||
}
|
||||
/** Which pending attack the player has already acknowledged (modal dismissed). */
|
||||
let attackNoticeSeen = $state<string | null>(null);
|
||||
/** A face-up card being peeked at from the scoresheet (view-only). */
|
||||
@@ -641,6 +647,9 @@
|
||||
if (!view || !selectedDef || !isYourTurn) return null;
|
||||
if (ambushVia || ambushSpell || ambushTrigger) return null; // ambushes aim at the future
|
||||
if (!me) return null;
|
||||
if (CELL_CARDS.has(selectedCard!.cardId)) {
|
||||
return eligibleCellsFor(view, selectedCard!.cardId);
|
||||
}
|
||||
if (selectedDef.adjacent === true) {
|
||||
const { x, y } = me.position;
|
||||
return new Set(
|
||||
@@ -770,10 +779,12 @@
|
||||
{/if}
|
||||
|
||||
{#if peekCard}
|
||||
<div class="inspector" role="img" aria-label="displayed card, enlarged">
|
||||
<button class="inspector-close" onclick={() => { peekCard = null; peekCreatureId = null; }} aria-label="hide card">×</button>
|
||||
<div class="inspector-card"><Card card={peekCard} onfaq={(id) => (faqCardId = id)} /></div>
|
||||
{#if peekNote}<div class="peek-note">{peekNote}</div>{/if}
|
||||
<div class="big-peek-scrim" role="button" tabindex="-1"
|
||||
onclick={() => { peekCard = null; peekCreatureId = null; }} onkeydown={() => {}}>
|
||||
<div class="big-peek">
|
||||
<Card card={peekCard} onfaq={(id) => (faqCardId = id)} />
|
||||
{#if peekNote}<div class="peek-note big-peek-note">{peekNote}</div>{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -813,7 +824,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if view?.stack && attackNoticeKey && attackNoticeSeen !== attackNoticeKey}
|
||||
{#if view?.stack && attackNoticeKey && attackNoticeSeen !== attackNoticeKey && !noFanfare}
|
||||
<div class="scrim attack-scrim" role="alertdialog" aria-label="you are under attack">
|
||||
<div class="attack-notice">
|
||||
<div class="attack-headline">
|
||||
@@ -844,6 +855,8 @@
|
||||
|
||||
{#if net.catchUp && net.catchUp.length > 0}
|
||||
<Replay steps={net.catchUp} onclose={() => net.closeCatchUp()} />
|
||||
{:else if local.replaySteps && local.replaySteps.length > 0}
|
||||
<Replay steps={local.replaySteps} onclose={() => (local.replaySteps = null)} />
|
||||
{/if}
|
||||
|
||||
{#if net.error}
|
||||
@@ -967,6 +980,9 @@
|
||||
notify me on my turn
|
||||
</button>
|
||||
{/if}
|
||||
{#if noFanfare}
|
||||
<button class="hint-cancel" onclick={() => setFanfare(true)}>announce attacks</button>
|
||||
{/if}
|
||||
</div>
|
||||
{#each net.seats as seat (seat.roomId + seat.name)}
|
||||
{@const g = net.games.find((x) => x.roomId === seat.roomId && x.name === seat.name)}
|
||||
@@ -1357,9 +1373,9 @@
|
||||
<div class="final-reveal" aria-label="all hands revealed">
|
||||
<div class="reveal-head">
|
||||
The hands, face-up
|
||||
{#if !local.active}
|
||||
<button class="stamp tiny" onclick={() => net.requestFullReplay()}>⟲ Watch the whole game</button>
|
||||
{/if}
|
||||
<button class="stamp tiny"
|
||||
onclick={() => (local.active ? local.buildReplay() : net.requestFullReplay())}>
|
||||
⟲ Watch the whole game</button>
|
||||
</div>
|
||||
{#each view.players as p (p.id)}
|
||||
<div class="reveal-row">
|
||||
@@ -1963,6 +1979,8 @@
|
||||
box-shadow: 0 18px 50px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
.big-peek :global(.card:hover) { transform: scale(2.1); }
|
||||
.big-peek { display: flex; flex-direction: column; align-items: center; }
|
||||
.big-peek-note { margin-top: 6.8rem; max-width: 15rem; font-size: 0.8rem; }
|
||||
.discard-link {
|
||||
background: none;
|
||||
border: none;
|
||||
|
||||
Reference in New Issue
Block a user