From 33032bcd07bab22afc21bfe0f9c60739b332572c Mon Sep 17 00:00:00 2001 From: Aaron Wood Date: Fri, 10 Apr 2026 14:41:22 -0400 Subject: [PATCH] Add torch timer, fog atmosphere, DM card redesign, and avatars - Torch timer: 60min countdown per character, visual warnings at 10m/5m/1m - Fog overlay: CSS radial gradient layers with seamless infinite drift - Fog synced across all clients via socket, adapts to light/dark themes - DM card redesign: compact layout with HP/AC/luck/torch + modifier row - Grid changed to 3-up (from 4) with larger fonts - DiceBear avatars on cards and character sheets with style picker - Campaign name shown in header - Server: JSON.stringify fix for object fields in PATCH handler Co-Authored-By: Claude Opus 4.6 (1M context) --- client/public/textures/fog1.png | Bin 0 -> 35372 bytes client/public/textures/fog2.png | Bin 0 -> 27943 bytes .../src/components/CharacterCard.module.css | 82 +++++++-- client/src/components/CharacterCard.tsx | 72 +++++--- .../src/components/CharacterSheet.module.css | 10 ++ client/src/components/CharacterSheet.tsx | 63 ++++++- client/src/components/DiceTray.module.css | 5 + client/src/components/FogOverlay.module.css | 163 ++++++++++++++++++ client/src/components/FogOverlay.tsx | 17 ++ client/src/components/TorchTimer.module.css | 79 +++++++++ client/src/components/TorchTimer.tsx | 70 ++++++++ client/src/pages/CampaignView.module.css | 33 +++- client/src/pages/CampaignView.tsx | 34 +++- client/src/theme.css | 3 + client/src/types.ts | 1 + server/src/db.ts | 1 + server/src/routes/characters.ts | 7 +- server/src/socket.ts | 9 + 18 files changed, 601 insertions(+), 48 deletions(-) create mode 100644 client/public/textures/fog1.png create mode 100644 client/public/textures/fog2.png create mode 100644 client/src/components/FogOverlay.module.css create mode 100644 client/src/components/FogOverlay.tsx create mode 100644 client/src/components/TorchTimer.module.css create mode 100644 client/src/components/TorchTimer.tsx diff --git a/client/public/textures/fog1.png b/client/public/textures/fog1.png new file mode 100644 index 0000000000000000000000000000000000000000..3a420d737d750c5c46b479731ccc67f8365b3669 GIT binary patch literal 35372 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 void; - onStatChange: (characterId: number, statName: string, value: number) => void; + onUpdate: (characterId: number, data: Partial) => void; onClick: (characterId: number) => void; } export default function CharacterCard({ character, onHpChange, - onStatChange, + onUpdate, onClick, }: CharacterCardProps) { - const totalSlots = character.gear.reduce((sum, g) => sum + g.slot_count, 0); - return (
- - {character.name} - {character.title ? ` ${character.title}` : ""} - - Lvl {character.level} + +
+ + {character.name} + {character.title ? ` ${character.title}` : ""} + + Lvl {character.level} +
{character.ancestry} {character.class}
-
e.stopPropagation()}> + +
e.stopPropagation()}>
-
-
e.stopPropagation()}> - - onStatChange(character.id, statName, value) - } + + {character.luck_token ? "\u2605" : "\u2606"} + + { + const isLit = character.torch_lit_at !== null; + onUpdate(character.id, { + torch_lit_at: isLit ? null : new Date().toISOString(), + } as Partial); + }} />
-
- {totalSlots} gear slot{totalSlots !== 1 ? "s" : ""} used + +
+ {STATS.map((stat) => { + const value = getEffectiveStat(character, stat); + const mod = getModifier(value); + return ( + + {stat} + {formatModifier(mod)} + + ); + })}
+
- XP: {character.xp} / {character.level * 10} + XP {character.xp} / {character.level * 10}
); diff --git a/client/src/components/CharacterSheet.module.css b/client/src/components/CharacterSheet.module.css index 18ade56..246c380 100644 --- a/client/src/components/CharacterSheet.module.css +++ b/client/src/components/CharacterSheet.module.css @@ -18,6 +18,16 @@ inset 0 0 20px rgba(var(--shadow-rgb), 0.15); } +.avatar { + width: 3.5rem; + height: 3.5rem; + border-radius: 50%; + border: 2px solid rgba(var(--gold-rgb), 0.3); + background: var(--bg-inset); + margin-right: 0.75rem; + flex-shrink: 0; +} + .identity { flex: 1; } diff --git a/client/src/components/CharacterSheet.tsx b/client/src/components/CharacterSheet.tsx index fdd8a58..9343e12 100644 --- a/client/src/components/CharacterSheet.tsx +++ b/client/src/components/CharacterSheet.tsx @@ -4,11 +4,31 @@ import { calculateAC } from "../utils/derived-ac"; import { getTalentHpBonus } from "../utils/talent-effects"; import AcDisplay from "./AcDisplay"; import InlineNumber from "./InlineNumber"; +import SelectDropdown from "./SelectDropdown"; +import TorchTimer from "./TorchTimer"; import StatsPanel from "./StatsPanel"; import InfoPanel from "./InfoPanel"; import GearPanel from "./GearPanel"; import styles from "./CharacterSheet.module.css"; +const AVATAR_STYLES = [ + "micah", + "adventurer", + "adventurer-neutral", + "lorelei", + "lorelei-neutral", + "pixel-art", + "thumbs", + "personas", + "big-smile", +]; + +function getAvatarUrl(character: Character): string { + const style = (character.overrides?.avatar_style as string) || "micah"; + const seed = encodeURIComponent(character.name || "hero"); + return `https://api.dicebear.com/9.x/${style}/svg?seed=${seed}`; +} + interface CharacterSheetProps { character: Character; mode: "view" | "edit"; @@ -86,6 +106,7 @@ export default function CharacterSheet({ className={styles.banner} style={{ borderLeft: `3px solid ${character.color}` }} > +
{mode === "edit" ? (
@@ -105,12 +126,29 @@ export default function CharacterSheet({ title="Character color" />
- handleNameField("title", e.target.value)} - /> +
+ handleNameField("title", e.target.value)} + /> + { + const overrides = { + ...(character.overrides || {}), + avatar_style: v, + }; + onUpdate(character.id, { + overrides, + } as Partial); + }} + /> +
) : (
@@ -199,6 +237,19 @@ export default function CharacterSheet({ {character.luck_token ? "\u2605" : "\u2606"}
+ + {/* Torch timer */} +
+ { + const isLit = character.torch_lit_at !== null; + onUpdate(character.id, { + torch_lit_at: isLit ? null : new Date().toISOString(), + } as Partial); + }} + /> +
diff --git a/client/src/components/DiceTray.module.css b/client/src/components/DiceTray.module.css index 69ed0ed..c7ec80c 100644 --- a/client/src/components/DiceTray.module.css +++ b/client/src/components/DiceTray.module.css @@ -15,4 +15,9 @@ .tray :global(canvas) { width: 100% !important; height: 100% !important; + pointer-events: none !important; +} + +.tray.active :global(canvas) { + pointer-events: auto !important; } diff --git a/client/src/components/FogOverlay.module.css b/client/src/components/FogOverlay.module.css new file mode 100644 index 0000000..e8f4bc0 --- /dev/null +++ b/client/src/components/FogOverlay.module.css @@ -0,0 +1,163 @@ +.overlay { + position: fixed; + inset: 0; + z-index: 9998; + pointer-events: none; + overflow: hidden; + animation: fadeIn 3s ease-in; + filter: var(--fog-filter); +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.layer1, +.layer2, +.layer3 { + position: absolute; + width: 200%; + height: 100%; + top: 0; + left: 0; +} + +/* Each layer has blobs duplicated at +50% offset so translating by -50% is seamless */ + +.layer1 { + background: + radial-gradient( + ellipse 18% 30% at 8% 50%, + rgba(220, 220, 235, 0.45), + transparent + ), + radial-gradient( + ellipse 13% 40% at 22% 40%, + rgba(210, 215, 230, 0.35), + transparent + ), + radial-gradient( + ellipse 15% 35% at 38% 55%, + rgba(220, 220, 240, 0.4), + transparent + ), + radial-gradient( + ellipse 18% 30% at 58% 50%, + rgba(220, 220, 235, 0.45), + transparent + ), + radial-gradient( + ellipse 13% 40% at 72% 40%, + rgba(210, 215, 230, 0.35), + transparent + ), + radial-gradient( + ellipse 15% 35% at 88% 55%, + rgba(220, 220, 240, 0.4), + transparent + ); + animation: drift1 50s linear infinite; +} + +.layer2 { + background: + radial-gradient( + ellipse 20% 25% at 5% 60%, + rgba(200, 205, 220, 0.35), + transparent + ), + radial-gradient( + ellipse 15% 35% at 25% 35%, + rgba(215, 215, 235, 0.3), + transparent + ), + radial-gradient( + ellipse 18% 30% at 42% 50%, + rgba(205, 210, 225, 0.35), + transparent + ), + radial-gradient( + ellipse 20% 25% at 55% 60%, + rgba(200, 205, 220, 0.35), + transparent + ), + radial-gradient( + ellipse 15% 35% at 75% 35%, + rgba(215, 215, 235, 0.3), + transparent + ), + radial-gradient( + ellipse 18% 30% at 92% 50%, + rgba(205, 210, 225, 0.35), + transparent + ); + animation: drift2 70s linear infinite; +} + +.layer3 { + background: + radial-gradient( + ellipse 22% 20% at 12% 45%, + rgba(220, 220, 240, 0.3), + transparent + ), + radial-gradient( + ellipse 15% 30% at 32% 55%, + rgba(210, 215, 230, 0.25), + transparent + ), + radial-gradient( + ellipse 13% 35% at 45% 40%, + rgba(220, 220, 235, 0.3), + transparent + ), + radial-gradient( + ellipse 22% 20% at 62% 45%, + rgba(220, 220, 240, 0.3), + transparent + ), + radial-gradient( + ellipse 15% 30% at 82% 55%, + rgba(210, 215, 230, 0.25), + transparent + ), + radial-gradient( + ellipse 13% 35% at 95% 40%, + rgba(220, 220, 235, 0.3), + transparent + ); + animation: drift3 40s linear infinite; +} + +/* Translate exactly -50% = one full pattern repeat = seamless loop */ +@keyframes drift1 { + from { + transform: translateX(0); + } + to { + transform: translateX(-50%); + } +} + +@keyframes drift2 { + from { + transform: translateX(-50%); + } + to { + transform: translateX(0); + } +} + +@keyframes drift3 { + from { + transform: translateX(0); + } + to { + transform: translateX(-50%); + } +} diff --git a/client/src/components/FogOverlay.tsx b/client/src/components/FogOverlay.tsx new file mode 100644 index 0000000..16d7962 --- /dev/null +++ b/client/src/components/FogOverlay.tsx @@ -0,0 +1,17 @@ +import styles from "./FogOverlay.module.css"; + +interface FogOverlayProps { + active: boolean; +} + +export default function FogOverlay({ active }: FogOverlayProps) { + if (!active) return null; + + return ( +
+
+
+
+
+ ); +} diff --git a/client/src/components/TorchTimer.module.css b/client/src/components/TorchTimer.module.css new file mode 100644 index 0000000..64c80a7 --- /dev/null +++ b/client/src/components/TorchTimer.module.css @@ -0,0 +1,79 @@ +.torch { + display: flex; + align-items: center; + gap: 0.2rem; + background: none; + border: none; + cursor: pointer; + padding: 0.1rem 0.3rem; + border-radius: 3px; + transition: background 0.15s; +} + +.torch:hover { + background: rgba(var(--gold-rgb), 0.1); +} + +.icon { + font-size: 1.1rem; + line-height: 1; + opacity: 0.4; + transition: opacity 0.3s; +} + +.lit .icon { + opacity: 1; +} + +.out .icon { + opacity: 0.25; +} + +.time { + font-family: "Cinzel", Georgia, serif; + font-size: 0.65rem; + font-weight: 600; + color: var(--gold); + min-width: 1.8rem; +} + +.warning .time { + color: var(--warning); +} + +.danger .time { + color: var(--danger); + animation: pulse 1s ease-in-out infinite; +} + +.critical .time { + color: var(--danger); + animation: pulse 0.5s ease-in-out infinite; +} + +.critical .icon { + animation: flicker 0.3s ease-in-out infinite; +} + +@keyframes pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.4; + } +} + +@keyframes flicker { + 0%, + 100% { + opacity: 1; + } + 25% { + opacity: 0.3; + } + 75% { + opacity: 0.8; + } +} diff --git a/client/src/components/TorchTimer.tsx b/client/src/components/TorchTimer.tsx new file mode 100644 index 0000000..a767d35 --- /dev/null +++ b/client/src/components/TorchTimer.tsx @@ -0,0 +1,70 @@ +import { useState, useEffect } from "react"; +import styles from "./TorchTimer.module.css"; + +const TORCH_DURATION_MS = 60 * 60 * 1000; // 60 minutes + +interface TorchTimerProps { + torchLitAt: string | null; + onToggle: () => void; +} + +export default function TorchTimer({ torchLitAt, onToggle }: TorchTimerProps) { + const [remaining, setRemaining] = useState(null); + + useEffect(() => { + if (!torchLitAt) { + setRemaining(null); + return; + } + + function tick() { + const elapsed = Date.now() - new Date(torchLitAt!).getTime(); + const left = TORCH_DURATION_MS - elapsed; + setRemaining(left > 0 ? left : 0); + } + + tick(); + const interval = setInterval(tick, 1000); + return () => clearInterval(interval); + }, [torchLitAt]); + + const isLit = torchLitAt !== null && remaining !== null && remaining > 0; + const isBurnedOut = torchLitAt !== null && remaining === 0; + + let urgency = ""; + if (isLit && remaining !== null) { + const mins = remaining / 60000; + if (mins <= 1) urgency = styles.critical; + else if (mins <= 5) urgency = styles.danger; + else if (mins <= 10) urgency = styles.warning; + } + + function formatTime(ms: number): string { + const totalSec = Math.ceil(ms / 1000); + const m = Math.floor(totalSec / 60); + const s = totalSec % 60; + if (m === 0) return `${s}s`; + return `${m}m`; + } + + return ( + + ); +} diff --git a/client/src/pages/CampaignView.module.css b/client/src/pages/CampaignView.module.css index 78f7034..f1bd6bd 100644 --- a/client/src/pages/CampaignView.module.css +++ b/client/src/pages/CampaignView.module.css @@ -84,6 +84,37 @@ text-shadow: 0 1px 3px rgba(var(--shadow-rgb), 0.4); } +.headerBtns { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.fogBtn { + padding: 0.4rem 0.5rem; + background: none; + border: 1px solid rgba(var(--gold-rgb), 0.2); + border-radius: 4px; + cursor: pointer; + font-size: 1.1rem; + line-height: 1; + opacity: 0.5; + transition: + opacity 0.15s, + border-color 0.15s; +} + +.fogBtn:hover { + opacity: 0.8; + border-color: rgba(var(--gold-rgb), 0.4); +} + +.fogBtnActive { + opacity: 1; + border-color: rgba(var(--gold-rgb), 0.5); + background: rgba(var(--gold-rgb), 0.1); +} + .addBtn { padding: 0.5rem 1rem; background: var(--btn-gold-bg); @@ -111,7 +142,7 @@ .grid { display: grid; - grid-template-columns: repeat(4, 1fr); + grid-template-columns: repeat(3, 1fr); gap: 1rem; } diff --git a/client/src/pages/CampaignView.tsx b/client/src/pages/CampaignView.tsx index ea455d8..c236d9a 100644 --- a/client/src/pages/CampaignView.tsx +++ b/client/src/pages/CampaignView.tsx @@ -19,6 +19,7 @@ import CharacterCard from "../components/CharacterCard"; import CharacterDetail from "../components/CharacterDetail"; import RollLog from "../components/RollLog"; import DiceTray from "../components/DiceTray"; +import FogOverlay from "../components/FogOverlay"; import SelectDropdown from "../components/SelectDropdown"; import styles from "./CampaignView.module.css"; @@ -48,6 +49,7 @@ export default function CampaignView() { id: number; } | null>(null); const pendingRollRef = useRef(null); + const [fogActive, setFogActive] = useState(false); // Fetch characters and join socket room useEffect(() => { @@ -240,6 +242,9 @@ export default function CampaignView() { socket.on("talent:added", onTalentAdded); socket.on("talent:removed", onTalentRemoved); socket.on("roll:result", onRollResult); + socket.on("atmosphere:update", (data: { fog: boolean }) => { + setFogActive(data.fog); + }); return () => { socket.off("character:created", onCharacterCreated); @@ -251,6 +256,7 @@ export default function CampaignView() { socket.off("talent:added", onTalentAdded); socket.off("talent:removed", onTalentRemoved); socket.off("roll:result", onRollResult); + socket.off("atmosphere:update"); }; }, []); @@ -337,9 +343,28 @@ export default function CampaignView() { {campaignName || "Campaign"} - +
+ + +
@@ -353,7 +378,7 @@ export default function CampaignView() { key={char.id} character={char} onHpChange={handleHpChange} - onStatChange={handleStatChange} + onUpdate={handleUpdate} onClick={setSelectedId} /> ))} @@ -448,6 +473,7 @@ export default function CampaignView() {
+
); } diff --git a/client/src/theme.css b/client/src/theme.css index 6e107ff..2f19d31 100644 --- a/client/src/theme.css +++ b/client/src/theme.css @@ -55,6 +55,7 @@ --texture-surface-size: 256px 256px; --texture-speckle-size: 128px 128px; --texture-body-size: 512px 512px; + --fog-filter: none; } /* ============================================================ @@ -95,6 +96,7 @@ --crit: #b8960a; --copper: #8a5520; --silver: #707070; + --fog-filter: invert(0.85) sepia(0.2); --texture-surface: url("/textures/parchment-noise-light.png"); --texture-speckle: url("/textures/speckle-light.png"); @@ -139,6 +141,7 @@ --crit: #8a7010; --copper: #8a5520; --silver: #707070; + --fog-filter: invert(0.85) sepia(0.2); --texture-surface: none; --texture-speckle: none; diff --git a/client/src/types.ts b/client/src/types.ts index 7b33926..7ba0950 100644 --- a/client/src/types.ts +++ b/client/src/types.ts @@ -55,6 +55,7 @@ export interface Character { overrides: Record; color: string; luck_token: number; + torch_lit_at: string | null; stats: Stat[]; gear: Gear[]; talents: Talent[]; diff --git a/server/src/db.ts b/server/src/db.ts index eb19117..5049f41 100644 --- a/server/src/db.ts +++ b/server/src/db.ts @@ -112,6 +112,7 @@ const v2Columns: Array<[string, string, string]> = [ ["character_talents", "game_talent_id", "INTEGER"], ["characters", "color", "TEXT DEFAULT ''"], ["characters", "luck_token", "INTEGER DEFAULT 1"], + ["characters", "torch_lit_at", "TEXT"], ["roll_log", "nat20", "INTEGER DEFAULT 0"], ["roll_log", "character_color", "TEXT DEFAULT ''"], ]; diff --git a/server/src/routes/characters.ts b/server/src/routes/characters.ts index d5a2863..dc58e3d 100644 --- a/server/src/routes/characters.ts +++ b/server/src/routes/characters.ts @@ -150,6 +150,7 @@ router.patch("/:id", (req, res) => { "overrides", "color", "luck_token", + "torch_lit_at", ]; const updates: string[] = []; @@ -158,7 +159,11 @@ router.patch("/:id", (req, res) => { for (const field of allowedFields) { if (req.body[field] !== undefined) { updates.push(`${field} = ?`); - values.push(req.body[field]); + const val = req.body[field]; + // JSON-stringify object fields for SQLite TEXT storage + values.push( + typeof val === "object" && val !== null ? JSON.stringify(val) : val, + ); } } diff --git a/server/src/socket.ts b/server/src/socket.ts index 1db2b4b..39827ce 100644 --- a/server/src/socket.ts +++ b/server/src/socket.ts @@ -90,6 +90,15 @@ export function setupSocket(io: Server) { }, ); + socket.on( + "atmosphere:update", + (data: { campaignId: number; fog: boolean }) => { + io.to(`campaign:${data.campaignId}`).emit("atmosphere:update", { + fog: data.fog, + }); + }, + ); + socket.on("disconnect", () => { // Rooms are cleaned up automatically by Socket.IO });