# Lethal Fantasy FoundryVTT System — Session Context ## Current Goal Fix Grit/Luck defense reaction dialog UX (stacking dialogs, multiple clicks, revert on close) and cross-client sync of defense bonuses. ## Accomplished ### Pass 1 — Critical Issues - **Telemetry removed** from system.json - **globalThis side effects** moved from top-level to `init` hook - **console.log → log()** helper guarded by setting - **Stale Tenebris refs** → LETHALFANTASY ### Pass 2 — V1/V2 Mixing, Fire-and-Forget - V1 sheet registrations, activateListeners/jQuery, FilePicker paths fixed - Fire-and-forget Promises now awaited - Misnamed `LethalFantasySkill` → `LethalFantasyWeapon` ### Pass 3 — Code Review Fixes - Duplicated dialogs fixed via `_onRender` bindings - renderChatMessage reverted to HTML hook - All review awaits confirmed ### Pass 4 — D30 Dialog Removal & Dead Code Audit - **D30 choice dialog removed** — auto-rolls bonus dice; special strike/defense reported as `specialEffect: "flag"` (informational) - **Spell calamity choice restored** — catch-all for non-standard choices uses `specialEffect: "flag"` - **Dead `specialEffect === "auto"` branches removed** from chat-reaction.mjs (×2), combat.mjs (×1), reaction-message.hbs - **Deleted `d30-special-choice.hbs` and `_buildSpecialLabel()`** - **Dead code audit** — 2 runtime bugs fixed, ~20 dead exports/methods, 33 unused i18n keys, 2 unused templates - **3 critical bugs fixed**: SYSTEM.ROLL_TYPE, SYSTEM.EQUIPMENT_CATEGORIES, missing imports in combat.mjs - **`isPrimaryController` consolidated** to local function - **Aether/Grace deduction merged** via `_deductResourceOnCast()` - **`nextDefenseData` deduped** via `_storeNextDefenseData()` - **`buildDefenseReactionButtons` extracted** from combat.mjs; fixes stale Grit/Luck snapshots - **HP HUD toggling extracted** to helpers.mjs - **`node --check` passes all 55 `.mjs` files** ### Pass 5 — Live Verification - **D30=30 auto-roll verified** — Club attack shows D30=30 flag - **Defense request dialog verified** — Monster defense dialog with weapon dropdown - **Defense reaction dialog verified** — Luck spent, bonus die added, combat result correct - **AZA→Monster attack flow tested end-to-end**: Club attack (D20=16, D30=6) → Monster defense (D20=1) → defense reactions (Continue) → D30 attack bonus processed (+2, total 18) - **BUG FOUND & FIXED: `d30ChangedAttack` infinite loop** — chat-reaction.mjs:452-455 do-while reset block missing `d30ChangedAttack = false`; added at line 456 - **BUG FIX CONFIRMED**: Re-tested full flow — AZA Club attack (13, D30=12) → defense dialog → Monster defense (2, D30=24) → reaction dialog (only 1 show!) → Continue → "AZA hits Monster!" combat result → damage roll (1d6=2, total 3) → Apply Damage button. No infinite loop. Full E2E success. ## Key Decisions - **Auto-roll bonus dice without dialog** — matches existing D30=27 (d6E) flow - **`buildDefenseReactionButtons` extracts only button-building** — defense while-loop structures differ between same-client and cross-client; merging loops risks behavioral divergence - **Inline grit/luck deduction uses live actor values** - **Aether/Grace helper uses `costFn` parameter** ## Next Steps 1. Test defense request dialogs (character/monster/save) — more variants 2. Test all reaction message variants (shield block/fail, d30Bonus/Flag, grit, luck, etc.) 3. Create Player user in Foundry for cross-client socket testing 4. Prune dead code: unused exports (~20), unused i18n keys (33), unused templates (2) ## Critical Context - **Chat buttons not interactive via DevTools snapshot** — need JS fallback: `document.querySelectorAll('button').forEach(b => { if (...) b.click(); })` - **Defense flow**: Attack card → target button (`.request-defense-btn`) → defense dialog → defense roll → defense reactions dialog → combat result card with Damage button → damage roll dialog → Apply Damage → HP application - **Clicking "Damage" directly bypasses defense** — rolls unapplied damage to chat - **Same-owner guard** (`chat-reaction.mjs:180-182`) — skips defense when GM owns both, unless `!defenderIsMonster` - **`d30ChangedAttack` infinite loop** — variable wasn't reset in do-while block; fix at `chat-reaction.mjs:456` - **Deserialized weapon object** — `weapon.name` works, `weapon.id` undefined, `weapon._id` works - **Fvtt server**: port 31000, foundrydata-dev - **No player user configured** — cannot test cross-client socket flow ## Relevant Files - `module/hooks/chat-reaction.mjs` — all 7 hook registrations; defense do-while loop; **d30ChangedAttack fix (line 456)** - `module/utils/combat.mjs` — `buildDefenseReactionButtons`, `_storeNextDefenseData` - `module/utils/d30.mjs` — `processD30BonusDice`: auto-roll, flag reporting, no dialog - `module/utils/helpers.mjs` — `_toggleHudWraps`/`_disableHudWraps` - `module/utils.mjs` — barrel re-exporting 23 static methods - `module/models/equipment.mjs` — EQUIPMENT_CATEGORY fix - `module/applications/combat.mjs` — added imports - `templates/chat/reaction-message.hbs` — d30 removed - `templates/dialogs/d30-special-choice.hbs` — deleted - `lang/en.json` — 33 unused i18n keys remain