Player preferences: art set, auto-grab, flourishes
A ⚙ preferences slip in the masthead, saved to this device: - Token art, all or nothing: the photographed cardboard or the hand-drawn vectors (the workshop's per-category query params stay as the undocumented editing override). - Ending a turn on a lone grabbable treasure picks it up on the way out — never on your own home, never when two share the square. - Spell flourishes on or off, honored by the live board and the replay reel alike (trap notices still fire; they are information). Preferences change what you see or what routine commands the client sends for you — never what is legal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0138A8CjeQRpvzKxuMfz1Bqc
This commit is contained in:
co-authored by
Claude Fable 5
parent
5310fbdca9
commit
6aae40ad5f
@@ -9,6 +9,7 @@
|
||||
import FxGallery from "./FxGallery.svelte";
|
||||
import TokenGallery from "./TokenGallery.svelte";
|
||||
import { scheduleFx, type BoardFx } from "./fx";
|
||||
import { prefs, savePrefs } from "./prefs.svelte";
|
||||
import { CREATURE_ART, objectArt, TERRAIN_ART, tokenArt } from "./art";
|
||||
import { local } from "./local.svelte";
|
||||
import { allCardDefs, cardDef, isNumberCard, isPermanentDuration, SIDES, stepTarget, cellKey, edgeKey, isMovableObject, sightedCellsFor, stackSightTrace, type GameView, eligibleCellsFor } from "@wizwar/engine";
|
||||
@@ -56,6 +57,7 @@
|
||||
for (const e of events) {
|
||||
if (e.type === "trapSprung" && e.player === view.you && e.cardId) trapNotice = e.cardId;
|
||||
}
|
||||
if (!prefs.flourishes) return;
|
||||
fxCancels.push(scheduleFx(
|
||||
events, view,
|
||||
(fx) => (boardFx = [...boardFx, fx]),
|
||||
@@ -850,6 +852,13 @@
|
||||
|
||||
function endTurn() {
|
||||
clearSelection();
|
||||
// Preference: a lone grabbable treasure underfoot is picked up on the
|
||||
// way out (pickup ends actions; ending the turn is still legal after).
|
||||
if (prefs.autoGrab && view && me && !me.carriedTreasureId &&
|
||||
!view.turn.actionsEnded && treasuresHere.length === 1 &&
|
||||
cellKey(me.position) !== cellKey(me.home)) {
|
||||
dispatch({ type: "pickUpTreasure" });
|
||||
}
|
||||
dispatch({ type: "endTurn", draw: drawCount });
|
||||
}
|
||||
|
||||
@@ -942,6 +951,13 @@
|
||||
// Press-and-hold on a wall/door/fire segment (touch has no hover).
|
||||
let peekEdge = $state<string | null>(null);
|
||||
|
||||
/** The preferences slip (client-side taste, saved on this device). */
|
||||
let prefsOpen = $state(false);
|
||||
function setPref<K extends keyof typeof prefs>(k: K, v: (typeof prefs)[K]) {
|
||||
prefs[k] = v;
|
||||
savePrefs();
|
||||
}
|
||||
|
||||
// A shimmering illusion wall, tapped or bumped: offer the belief test.
|
||||
let illusionPrompt = $state<{ cell: { x: number; y: number }; side: Side } | null>(null);
|
||||
function illusionEdgeParts(key: string): { cell: { x: number; y: number }; side: Side } {
|
||||
@@ -1097,6 +1113,7 @@
|
||||
<header class="masthead">
|
||||
<span class="mast-title">Wiz-War</span>
|
||||
<span class="mast-sub">sixth edition</span>
|
||||
<button class="mast-leave" title="preferences" onclick={() => (prefsOpen = !prefsOpen)}>⚙ preferences</button>
|
||||
{#if local.active}
|
||||
<span class="mast-room">hotseat</span>
|
||||
<button class="mast-leave" onclick={() => local.leave()}>set the game aside</button>
|
||||
@@ -1180,6 +1197,37 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if prefsOpen}
|
||||
<div class="scrim attack-scrim" role="button" tabindex="-1"
|
||||
onclick={() => (prefsOpen = false)} onkeydown={() => {}}>
|
||||
<div class="attack-notice prefs-slip" role="dialog" aria-label="preferences" tabindex="-1"
|
||||
onclick={(ev) => ev.stopPropagation()} onkeydown={() => {}}>
|
||||
<div class="attack-title">Preferences</div>
|
||||
<div class="pref-row">
|
||||
<span>Token art</span>
|
||||
<button class="stamp tiny" class:lit={prefs.art === "photo"}
|
||||
onclick={() => setPref("art", "photo")}>cardboard</button>
|
||||
<button class="stamp tiny" class:lit={prefs.art === "drawn"}
|
||||
onclick={() => setPref("art", "drawn")}>hand-drawn</button>
|
||||
</div>
|
||||
<label class="pref-row">
|
||||
<input type="checkbox" checked={prefs.autoGrab}
|
||||
onchange={(e) => setPref("autoGrab", e.currentTarget.checked)} />
|
||||
<span>Ending my turn on a lone treasure picks it up (never on my own home)</span>
|
||||
</label>
|
||||
<label class="pref-row">
|
||||
<input type="checkbox" checked={prefs.flourishes}
|
||||
onchange={(e) => setPref("flourishes", e.currentTarget.checked)} />
|
||||
<span>Spell flourishes (the animated effects)</span>
|
||||
</label>
|
||||
<div class="pref-note">Saved on this device. Rules are never affected.</div>
|
||||
<div class="attack-actions">
|
||||
<button class="hint-cancel" onclick={() => (prefsOpen = false)}>done</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if faqCardId}
|
||||
<Faq cardId={faqCardId} onclose={() => (faqCardId = null)} />
|
||||
{/if}
|
||||
@@ -2394,6 +2442,15 @@
|
||||
.stamp.big { font-size: 0.95rem; padding: 0.6rem 1.3rem; }
|
||||
.stamp.tiny { font-size: 0.62rem; padding: 0.22rem 0.5rem; box-shadow: 1px 1px 0 rgba(0,0,0,0.3); }
|
||||
.stamp.discard-one { background: #8a7a5e; }
|
||||
.prefs-slip { text-align: left; }
|
||||
.pref-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin: 0.55rem 0;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
.pref-note { font-size: 0.72rem; color: #8a7a5e; margin-top: 0.6rem; }
|
||||
.slip.council { background: #ece4f1; border-color: #7a6f9a; }
|
||||
.council-head {
|
||||
font-weight: 700;
|
||||
|
||||
Reference in New Issue
Block a user