From fcab44f59d68f5244af4a3a2a419de65b7ec31ca Mon Sep 17 00:00:00 2001 From: Mike Bloy Date: Mon, 16 Feb 2026 00:09:24 -0600 Subject: [PATCH] roll requests need multiplayer support --- src/module/helpers.js | 132 ++++++++++++++++-------- src/templates/rollRequest/chatcard.html | 41 +++++--- 2 files changed, 113 insertions(+), 60 deletions(-) diff --git a/src/module/helpers.js b/src/module/helpers.js index 6f06a75..c21b45b 100644 --- a/src/module/helpers.js +++ b/src/module/helpers.js @@ -51,54 +51,82 @@ export function firstGM() { } export function helpersRenderChatMessage(message, html, data) { - const messageId = message._id; const requestContentDiv = html.querySelector('.mb-requestroll'); if (!requestContentDiv) { return; } - const rollType = requestContentDiv.dataset?.rollType; - const rollDesc = requestContentDiv.dataset?.rollDesc; - const targetNumber = requestContentDiv.dataset?.rollTargetNumber; - const flavour = requestContentDiv.dataset?.rollFlavour; - const title = requestContentDiv.dataset?.rollTitle; - const mods = JSON.parse(requestContentDiv.dataset?.rollMods ?? '[]'); - const rows = requestContentDiv.querySelectorAll('.mb-rollrequest-results tr'); + const allData = message.getFlag('swade-mb-helpers', 'rollRequest'); + const rollType = allData.roll.type; + const rollDesc = allData.roll.desc; + const targetNumber = allData.roll.targetNumber; + const flavour = allData.roll.flavour; + const title = allData.roll.title; + const mods = allData.roll.mods; + const rows = requestContentDiv.querySelectorAll('.mb-rollrequest-results tbody tr'); for (const row of rows) { - const resolved = row.dataset?.resolved === 'true'; - const value = row.dataset?.value ?? 'None'; const tokenUuid = row.dataset?.tokenUuid; - const rowMods = JSON.parse(row.dataset?.tokenAdditonalMods ?? '[]'); const token = foundry.utils.fromUuidSync(tokenUuid); + const tokenId = token.id; const actor = token?.actor; - const iconSpan = row.querySelector('.mb-roll-result-icon'); - const valueSpan = row.querySelector('.mb-roll-result'); + const tokenData = allData.tokens[tokenId]; + const resolved = tokenData?.resolved ?? false; + const rowMods = tokenData?.additionalMods ?? []; const buttonSpan = row.querySelector('.mb-roll-button'); - if (resolved) { - buttonSpan.remove(); - } else { - iconSpan.remove(); - valueSpan.remove(); - buttonSpan.addEventListener('click', async () => { - const options = { - title: `${title} - ${token.name}`, - isMbRequestedRoll: true, - mbRequestMessageId: messageId, - targetNumber, - flavour, - additionalMods: [...mods, ...rowMods], - }; - let rollFunc = 'rollAttribute'; - let rollId = rollDesc.toLowerCase(); - if (rollType === 'skill') { - rollFunc = 'rollSkill'; - rollId = actor.items - .filter((i) => i.type === 'skill') - .find( - (i) => i.system.swid === rollDesc.toLowerCase() || i.name.toLowerCase() === rollDesc.toLowerCase(), - )?.id; - } - const result = await actor[rollFunc](rollId, options); - }); + const waitingSpan = row.querySelector('.mb-roll-waiting'); + if (!resolved) { + if (token.isOwner) { + waitingSpan.remove(); + buttonSpan.addEventListener('click', async () => { + const options = { + title: `${title} - ${token.name}`, + targetNumber, + flavour, + additionalMods: [...mods, ...rowMods], + }; + let rollFunc = 'rollAttribute'; + let rollId = rollDesc.toLowerCase(); + if (rollType === 'skill') { + rollFunc = 'rollSkill'; + rollId = actor.items + .filter((i) => i.type === 'skill') + .find( + (i) => i.system.swid === rollDesc.toLowerCase() || i.name.toLowerCase() === rollDesc.toLowerCase(), + )?.id; + } + const result = await actor[rollFunc](rollId, options); + tokenData.resolved = true; + tokenData.total = result.total; + if (result.successes == -1) { + tokenData.icon = '⛝'; + tokenData.resolveClass = 'critFail'; + tokenData.value = 'Critical Failure!'; + } else if (result.successes == 0) { + tokenData.icon = '⛒'; + tokenData.value = 'Failure'; + tokenData.resolveClass = 'failure'; + } else if (result.successes == 1) { + tokenData.icon = '✓'; + tokenData.value = 'Success'; + tokenData.resolveClass = 'success'; + } else if (result.successes == 2) { + tokenData.icon = '✓✓'; + tokenData.value = `Success and a raise!`; + tokenData.resolveClass = 'raise'; + } else { + tokenData.icon = '✓✓'; + tokenData.value = `Success and ${result.successes - 1} raises!`; + tokenData.resolveClass = 'raise'; + } + await message.setFlag('swade-mb-helpers', `rollRequest.tokens.${tokenId}`, tokenData); + log('result', result); + const newData = message.getFlag('swade-mb-helpers', 'rollRequest'); + const template = templates['rollRequest/chatcard.html']; + const newContent = await foundry.applications.handlebars.renderTemplate(template, newData); + await message.update({ content: newContent }); + }); + } else { + buttonSpan.remove(); + } } } } @@ -123,7 +151,7 @@ export async function requestRollFromTokens(tokens, rollType, rollDesc, options const title = options?.title || `${requestingUser.name} requests a ${rollDesc} roll`; const flavour = options?.flavour || options?.flavor || title; const targetNumber = options?.targetNumber || 4; - const tokenData = []; + const tokenData = {}; for (const token of tokens) { const additionalMods = []; if ('modCallback' in options) { @@ -132,12 +160,17 @@ export async function requestRollFromTokens(tokens, rollType, rollDesc, options additionalMods.push(tm); } } - tokenData.push({ + tokenData[token.id] = { name: token.name, id: token.id, + resolved: false, + icon: null, + value: null, + total: null, + resolveClass: 'waiting', uuid: token.document.uuid, - additionalMods: JSON.stringify(additionalMods), - }); + additionalMods: additionalMods, + }; } const rollData = { type: rollType, @@ -145,15 +178,24 @@ export async function requestRollFromTokens(tokens, rollType, rollDesc, options title, flavour, targetNumber, - mods: JSON.stringify(options.mods ? options.mods : []), + mods: options.mods ? options.mods : [], }; const template = templates['rollRequest/chatcard.html']; const chatData = { user: game.user._id, flavor: flavour, + flags: { + 'swade-mb-helpers': { + rollRequest: { + tokens: tokenData, + roll: rollData, + }, + }, + }, content: await foundry.applications.handlebars.renderTemplate(template, { roll: rollData, tokens: tokenData }), }; - return ChatMessage.create(chatData); + let message = await ChatMessage.create(chatData); + console.log(message); } export async function oldRequestRollFromTokens(tokens, rollType, rollDesc, options = {}) { diff --git a/src/templates/rollRequest/chatcard.html b/src/templates/rollRequest/chatcard.html index 736cfb7..7d51568 100644 --- a/src/templates/rollRequest/chatcard.html +++ b/src/templates/rollRequest/chatcard.html @@ -1,22 +1,33 @@ -
+
-

Click the button to make your roll.

+

{{roll.title}}

- {{#each tokens}} - - - - + + + + + + + {{#each tokens}} + + + + + {{/each}} +
{{name}} - - ? - NA - - {{/each}} +
TokenResult
{{name}} + {{#unless resolved}} + + Waiting... + {{/unless}} + {{#if resolved}} + {{total}} + {{icon}} + {{value}} + {{/if}} +