roll requests need multiplayer support

This commit is contained in:
Mike Bloy 2026-02-16 00:09:24 -06:00
parent f0ca43b989
commit fcab44f59d
2 changed files with 113 additions and 60 deletions

View File

@ -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 = {}) {

View File

@ -1,22 +1,33 @@
<div class="mb-chat-card mb-interactive mb-requestroll" data-roll-type="{{roll.type}}" data-roll-desc="{{roll.desc}}"
data-roll-target-number="{{roll.targetNumber}}" data-roll-flavour="{{roll.flavour}}" data-roll-title="{{roll.title}}"
data-roll-mods="{{roll.mods}}">
<div class="mb-chat-card mb-interactive mb-requestroll">
<div class="mb-card-content">
<p>Click the button to make your roll.</p>
<p>{{roll.title}}</p>
</div>
<div class="mb-card-buttons-results">
<table class="mb-rollrequest-results">
{{#each tokens}}
<tr data-resolved="false" data-value="" data-is-success="" data-token-id="{{id}}" data-token-uuid="{{uuid}}"
data-token-additional-mods="{{additionalMods}}">
<td>{{name}}</td>
<td>
<button class="mb-roll-button">Roll</button>
<span class="mb-roll-result-icon">?</span>
<span class="mb-roll-result">NA</span>
</td>
<td </tr>
{{/each}}
<thead>
<tr>
<th>Token</th>
<th>Result</th>
</tr>
</thead>
<tbody>
{{#each tokens}}
<tr data-token-uuid="{{uuid}}" class="mb-roll-{{resolveClass}}">
<td>{{name}}</td>
<td>
{{#unless resolved}}
<button class="mb-roll-button">Roll</button>
<span class="mb-roll-waiting">Waiting...</span>
{{/unless}}
{{#if resolved}}
<span class="mb-roll-total">{{total}}</span>
<span class="mb-roll-result-icon">{{icon}}</span>
<span class="mb-roll-result">{{value}}</span>
{{/if}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>