add request trait and damage roll enrichers

This commit is contained in:
Mike Bloy 2026-04-26 21:33:28 -05:00
parent 7f2c4c56ba
commit 6580328746
2 changed files with 154 additions and 0 deletions

152
src/module/enrichers.js Normal file
View File

@ -0,0 +1,152 @@
import { requestRollFromTokens } from './helpers.js';
const enrichers = [
{
id: 'mb-swade-damage',
pattern: /@SWADEDamage\[\s*(?<roll>[\d+-dx]+)\s*\](?:\(\s*(?<ap>\d+)\s*\))?(?:\{(?<flavor>[^}]+)\})?/g,
enricher: swadeDamageEnricher,
},
{
id: 'mb-swade-trait',
pattern:
/@SWADERequestRoll\[\s*(?<traitType>skill|attribute),(?<traitName>[\w\s-]+)(?:,(?<tn>\d+))?\s*\](?:\(\s*(?<modVal>[-+]?\d+)(?:,(?<modDesc>[^)]+))?\s*\))?(?:\{(?<flavor>[^}]+)\})?/g,
enricher: swadeTraitRollEnricher,
},
];
function swadeDamageEnricher(match, options) {
if (!match.groups) {
return null;
}
const roll = match.groups.roll;
const ap = parseInt(match.groups.ap ?? '0');
const flavor = match.groups.flavor ?? '';
const dataset = { roll, ap, flavor, rollType: 'damage' };
let text = '<strong>Damage</strong>';
if (flavor) {
text += ` ${flavor}`;
}
text += `: ${roll}`;
if (ap) {
text += ` AP ${ap}`;
}
if (!game.user.isGM) {
const span = document.createElement('span');
span.innerHTML = text;
return span;
}
const anchor = document.createElement('a');
const classes = ['content-link', 'mb-swade-roll-damage-link', 'mb-swade-roll-link'];
for (const cls of classes) {
anchor.classList.add(cls);
}
for (let [k, v] of Object.entries(dataset)) {
anchor.dataset[k] = v;
}
anchor.innerHTML = `<i class="fas fa-heart-crack"></i> ${text}`;
return anchor;
}
function swadeTraitRollEnricher(match, options) {
if (!match.groups) {
return null;
}
const traitType = match.groups.traitType;
const traitName = match.groups.traitName;
if (!traitType || !traitName) {
return null;
}
const tn = parseInt(match.groups.tn ?? '4');
const modVal = parseInt(match.groups.modVal ?? '0');
const modDesc = match.groups.modDesc ?? 'Circumstance';
const flavor = match.groups.flavor ?? '';
const dataset = { traitType, traitName, tn, modVal, modDesc, flavor, rollType: 'trait' };
const displayTraitName = (traitName[0].toUpperCase() + traitName.slice(1).toLowerCase()).replace(/[-_]/, ' ');
let text = `<strong>Request Roll:</strong> ${displayTraitName}`;
if (modVal != 0) {
text += ` ${modVal}`;
}
if (tn !== 4) {
text += ` TN: ${tn}`;
}
if (flavor) {
text += ` (${flavor})`;
}
const anchor = document.createElement('a');
const classes = ['content-link', 'mb-swade-roll-request-link', 'mb-swade-roll-link'];
for (const cls of classes) {
anchor.classList.add(cls);
}
for (let [k, v] of Object.entries(dataset)) {
anchor.dataset[k] = v;
}
anchor.innerHTML = `<i class="fas fa-question"></i> <i class="fas fa-dice"></i> ${text}`;
return anchor;
}
function onSwadeRollLink(ev) {
console.log('SWADE ROLL', ev);
const targetElement = ev.target;
if (!targetElement.closest('a.mb-swade-roll-link')) return;
ev.preventDefault();
const rollType = targetElement.dataset.rollType;
switch (rollType) {
case 'trait':
return onRequestTraitRollLink(targetElement);
case 'damage':
return onDamageRollLink(targetElement);
}
}
function onRequestTraitRollLink(targetElement) {
const tokens = game.user.targets.size > 0 ? Array.from(game.user.targets) : canvas.tokens.controlled;
console.log(tokens);
if (tokens.length < 1) {
foundry.ui.notifications.error('Please target or select some tokens.');
return;
}
const { traitType, traitName, modDesc, flavor } = targetElement.dataset;
const tn = parseInt(targetElement.dataset.tn);
const modVal = parseInt(targetElement.dataset.modVal);
const options = { targetNumber: 4, flavor };
if (tn != 4) {
options.targetNumber = tn;
}
if (modVal != 0) {
options.mods = [{ label: modDesc, value: modVal }];
}
requestRollFromTokens(tokens, traitType, traitName, options);
}
function onDamageRollLink(targetElement) {
const roll = targetElement.dataset.roll;
const ap = parseInt(targetElement.dataset.ap);
let flavor = targetElement.dataset.flavor;
const options = {};
if (ap > 0) {
flavor = `${flavor ? flavor + ' - ' : ''}AP: ${ap}`;
options.ap = ap;
}
new CONFIG.Dice.DamageRoll(roll, null, options).toMessage({ flavor });
}
export function setupEnrichers() {
Hooks.once('ready', async () => {
for (const enricher of enrichers) {
CONFIG.TextEditor.enrichers.push(enricher);
}
});
Hooks.on('renderApplicationV1', (application, html, data) => {
$(html)
.find('a.mb-swade-roll-link')
.each((i, el) => $(el).click(onSwadeRollLink));
});
Hooks.on('renderApplicationV2', (application, element, context, options) => {
element.querySelectorAll('a.mb-swade-roll-link').forEach((el) => el.addEventListener('click', onSwadeRollLink));
});
Hooks.on('renderChatMessageHTML', (message, html, context) => {
html.querySelectorAll('a.mb-swade-roll-link').forEach((el) => el.addEventListener('click', onSwadeRollLink));
});
}

View File

@ -1,6 +1,7 @@
/* globals socketlib */
// Import JavaScript modules
import { registerSettings } from './settings.js';
import { setupEnrichers } from './enrichers.js';
import { preloadTemplates } from './preloadTemplates.js';
import { api } from './api.js';
import { initVisionModes } from './visionModes.js';
@ -32,6 +33,7 @@ Hooks.once('init', async () => {
});
// Setup module
setupEnrichers();
Hooks.once('setup', async () => {
api.registerFunctions();
// Register custom module settings