diff --git a/src/core/state.js b/src/core/state.js index 530d590..19fffe3 100644 --- a/src/core/state.js +++ b/src/core/state.js @@ -151,6 +151,10 @@ export function setPendingDiceRoll(roll) { pendingDiceRoll = roll; } +export function getPendingDiceRoll() { + return pendingDiceRoll; +} + export function setPanelContainer($element) { $panelContainer = $element; } diff --git a/src/systems/features/dice.js b/src/systems/features/dice.js new file mode 100644 index 0000000..92e691b --- /dev/null +++ b/src/systems/features/dice.js @@ -0,0 +1,113 @@ +/** + * Dice System Module + * Handles dice rolling logic, display updates, and quick reply integration + */ + +import { + extensionSettings, + pendingDiceRoll, + setPendingDiceRoll +} from '../../core/state.js'; +import { saveSettings } from '../../core/persistence.js'; + +/** + * Rolls the dice and displays result. + * Works with the DiceModal class for UI updates. + * @param {DiceModal} diceModal - The DiceModal instance + */ +export async function rollDice(diceModal) { + if (!diceModal) return; + + const count = parseInt(String($('#rpg-dice-count').val())) || 1; + const sides = parseInt(String($('#rpg-dice-sides').val())) || 20; + + // Start rolling animation + diceModal.startRolling(); + + // Wait for animation (simulate rolling) + await new Promise(resolve => setTimeout(resolve, 1200)); + + // Execute /roll command + const rollCommand = `/roll ${count}d${sides}`; + const rollResult = await executeRollCommand(rollCommand); + + // Parse result + const total = rollResult.total || 0; + const rolls = rollResult.rolls || []; + + // Store result temporarily (not saved until "Save Roll" is clicked) + setPendingDiceRoll({ + formula: `${count}d${sides}`, + total: total, + rolls: rolls, + timestamp: Date.now() + }); + + // Show result + diceModal.showResult(total, rolls); + + // Don't update sidebar display yet - only update when user clicks "Save Roll" +} + +/** + * Executes a /roll command and returns the result. + * @param {string} command - The roll command (e.g., "/roll 2d20") + * @returns {Promise<{total: number, rolls: Array}>} The roll result + */ +export async function executeRollCommand(command) { + try { + // Parse the dice notation (e.g., "2d20") + const match = command.match(/(\d+)d(\d+)/); + if (!match) { + return { total: 0, rolls: [] }; + } + + const count = parseInt(match[1]); + const sides = parseInt(match[2]); + const rolls = []; + let total = 0; + + for (let i = 0; i < count; i++) { + const roll = Math.floor(Math.random() * sides) + 1; + rolls.push(roll); + total += roll; + } + + return { total, rolls }; + } catch (error) { + console.error('[RPG Companion] Error rolling dice:', error); + return { total: 0, rolls: [] }; + } +} + +/** + * Updates the dice display in the sidebar. + */ +export function updateDiceDisplay() { + const lastRoll = extensionSettings.lastDiceRoll; + if (lastRoll) { + $('#rpg-last-roll-text').text(`Last Roll (${lastRoll.formula}): ${lastRoll.total}`); + } else { + $('#rpg-last-roll-text').text('Last Roll: None'); + } +} + +/** + * Clears the last dice roll. + */ +export function clearDiceRoll() { + extensionSettings.lastDiceRoll = null; + saveSettings(); + updateDiceDisplay(); +} + +/** + * Adds the Roll Dice quick reply button. + */ +export function addDiceQuickReply() { + // Create quick reply button if Quick Replies exist + if (window.quickReplyApi) { + // Quick Reply API integration would go here + // For now, the dice display in the sidebar serves as the button + } +} diff --git a/src/systems/ui/modals.js b/src/systems/ui/modals.js index e2ed0dc..51b1827 100644 --- a/src/systems/ui/modals.js +++ b/src/systems/ui/modals.js @@ -8,14 +8,20 @@ import { extensionSettings, lastGeneratedData, committedTrackerData, - pendingDiceRoll, $infoBoxContainer, $thoughtsContainer, - setPendingDiceRoll + setPendingDiceRoll, + getPendingDiceRoll } from '../../core/state.js'; import { saveSettings, saveChatData } from '../../core/persistence.js'; import { renderUserStats } from '../rendering/userStats.js'; import { updateChatThoughts } from '../rendering/thoughts.js'; +import { + rollDice as rollDiceCore, + clearDiceRoll as clearDiceRollCore, + updateDiceDisplay as updateDiceDisplayCore, + addDiceQuickReply as addDiceQuickReplyCore +} from '../features/dice.js'; /** * Modern DiceModal ES6 Class @@ -283,16 +289,17 @@ export function setupDiceRoller() { // Roll dice button $('#rpg-dice-roll-btn').on('click', async function() { - await rollDice(); + await rollDiceCore(diceModal); }); // Save roll button (closes popup and saves the roll) $('#rpg-dice-save-btn').on('click', function() { // Save the pending roll - if (pendingDiceRoll) { - extensionSettings.lastDiceRoll = pendingDiceRoll; + const roll = getPendingDiceRoll(); + if (roll) { + extensionSettings.lastDiceRoll = roll; saveSettings(); - updateDiceDisplay(); + updateDiceDisplayCore(); setPendingDiceRoll(null); } closeDicePopup(); @@ -301,14 +308,14 @@ export function setupDiceRoller() { // Reset on Enter key $('#rpg-dice-count, #rpg-dice-sides').on('keypress', function(e) { if (e.which === 13) { - rollDice(); + rollDiceCore(diceModal); } }); // Clear dice roll button $('#rpg-clear-dice').on('click', function(e) { e.stopPropagation(); // Prevent opening the dice popup - clearDiceRoll(); + clearDiceRollCore(); }); return diceModal; @@ -402,7 +409,7 @@ export function setupSettingsPopup() { // Re-render user stats and dice display renderUserStats(); - updateDiceDisplay(); + updateDiceDisplayCore(); updateChatThoughts(); // Clear the thought bubble in chat // console.log('[RPG Companion] Chat cache cleared'); @@ -462,101 +469,26 @@ export function applyCustomThemeToPopup() { /** * Clears the last dice roll. + * Backwards compatible wrapper for dice module. */ export function clearDiceRoll() { - extensionSettings.lastDiceRoll = null; - saveSettings(); - updateDiceDisplay(); -} - -/** - * Rolls the dice and displays result. - * Refactored to use DiceModal class. - */ -async function rollDice() { - if (!diceModal) return; - - const count = parseInt(String($('#rpg-dice-count').val())) || 1; - const sides = parseInt(String($('#rpg-dice-sides').val())) || 20; - - // Start rolling animation - diceModal.startRolling(); - - // Wait for animation (simulate rolling) - await new Promise(resolve => setTimeout(resolve, 1200)); - - // Execute /roll command - const rollCommand = `/roll ${count}d${sides}`; - const rollResult = await executeRollCommand(rollCommand); - - // Parse result - const total = rollResult.total || 0; - const rolls = rollResult.rolls || []; - - // Store result temporarily (not saved until "Save Roll" is clicked) - setPendingDiceRoll({ - formula: `${count}d${sides}`, - total: total, - rolls: rolls, - timestamp: Date.now() - }); - - // Show result - diceModal.showResult(total, rolls); - - // Don't update sidebar display yet - only update when user clicks "Save Roll" -} - -/** - * Executes a /roll command and returns the result. - */ -async function executeRollCommand(command) { - try { - // Parse the dice notation (e.g., "2d20") - const match = command.match(/(\d+)d(\d+)/); - if (!match) { - return { total: 0, rolls: [] }; - } - - const count = parseInt(match[1]); - const sides = parseInt(match[2]); - const rolls = []; - let total = 0; - - for (let i = 0; i < count; i++) { - const roll = Math.floor(Math.random() * sides) + 1; - rolls.push(roll); - total += roll; - } - - return { total, rolls }; - } catch (error) { - console.error('[RPG Companion] Error rolling dice:', error); - return { total: 0, rolls: [] }; - } + clearDiceRollCore(); } /** * Updates the dice display in the sidebar. + * Backwards compatible wrapper for dice module. */ export function updateDiceDisplay() { - const lastRoll = extensionSettings.lastDiceRoll; - if (lastRoll) { - $('#rpg-last-roll-text').text(`Last Roll (${lastRoll.formula}): ${lastRoll.total}`); - } else { - $('#rpg-last-roll-text').text('Last Roll: None'); - } + updateDiceDisplayCore(); } /** * Adds the Roll Dice quick reply button. + * Backwards compatible wrapper for dice module. */ export function addDiceQuickReply() { - // Create quick reply button if Quick Replies exist - if (window.quickReplyApi) { - // Quick Reply API integration would go here - // For now, the dice display in the sidebar serves as the button - } + addDiceQuickReplyCore(); } /**