Embedding Duality Rolls (#52)

* Added DualityRoll direct rolls in chat
* Added button render to renderJournalEntryPageProseMirrorSheet and renderHandlebarsApplication
* Hope and Fear dice totals are now properly added together
* Added Colorful/Normal DualityRoll color settings
This commit is contained in:
WBHarry 2025-05-26 16:34:32 +02:00 committed by GitHub
parent cf51153432
commit d1a0a9ab24
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 1192 additions and 1264 deletions

View file

@ -10,6 +10,9 @@ import DhpChatLog from './module/ui/chatLog.mjs';
import DhpPlayers from './module/ui/players.mjs';
import DhpRuler from './module/ui/ruler.mjs';
import DhpTokenRuler from './module/ui/tokenRuler.mjs';
import { dualityRollEnricher, getDualityMessage } from './module/enrichers/DualityRollEnricher.mjs';
import { getCommandTarget, rollCommandToJSON } from './module/helpers/utils.mjs';
import { abilities } from './module/config/actorConfig.mjs';
globalThis.SYSTEM = SYSTEM;
@ -21,6 +24,11 @@ Hooks.once('init', () => {
documents
};
CONFIG.TextEditor.enrichers.push({
pattern: /\[\[\/dr\s?(.*?)\]\]/g,
enricher: dualityRollEnricher
});
CONFIG.statusEffects = Object.values(SYSTEM.GENERAL.conditions).map(x => ({
...x,
name: game.i18n.localize(x.name)
@ -90,7 +98,6 @@ Hooks.once('init', () => {
game.socket.on(`system.${SYSTEM.id}`, handleSocketEvent);
registerDHPSettings();
RegisterHandlebarsHelpers.registerHelpers();
return preloadHandlebarsTemplates();
@ -122,6 +129,134 @@ Hooks.on(socketEvent.GMUpdate, async (action, uuid, update) => {
}
});
const renderDualityButton = async event => {
const button = event.currentTarget;
const attributeValue = button.dataset.attribute?.toLowerCase();
const target = getCommandTarget();
if (!target) return;
const rollModifier = attributeValue ? target.system.attributes[attributeValue].data.value : null;
const { roll, hope, fear, advantage, disadvantage, modifiers } = await target.diceRoll({
title: button.dataset.label,
value: rollModifier
});
const cls = getDocumentClass('ChatMessage');
const msgData = {
type: 'dualityRoll',
sound: CONFIG.sounds.dice,
system: {
title: button.dataset.label,
origin: target.id,
roll: roll._formula,
modifiers: modifiers,
hope: hope,
fear: fear,
advantage: advantage,
disadvantage: disadvantage
},
user: game.user.id,
content: 'systems/daggerheart/templates/chat/duality-roll.hbs',
rolls: [roll]
};
await cls.create(msgData);
};
Hooks.on('renderChatMessageHTML', (_, element) => {
element
.querySelectorAll('.duality-roll-button')
.forEach(element => element.addEventListener('click', renderDualityButton));
});
Hooks.on('renderJournalEntryPageProseMirrorSheet', (_, element) => {
element
.querySelectorAll('.duality-roll-button')
.forEach(element => element.addEventListener('click', renderDualityButton));
});
Hooks.on('renderHandlebarsApplication', (_, element) => {
element
.querySelectorAll('.duality-roll-button')
.forEach(element => element.addEventListener('click', renderDualityButton));
});
Hooks.on('chatMessage', (_, message) => {
if (message.startsWith('/dr')) {
const rollCommand = rollCommandToJSON(message.replace(/\/dr\s?/, ''));
if (!rollCommand) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.DualityParsing'));
return false;
}
const attributeValue = rollCommand.attribute?.toLowerCase();
// Target not required if an attribute is not used.
const target = attributeValue ? getCommandTarget() : undefined;
if (target || !attributeValue) {
new Promise(async (resolve, reject) => {
const attribute = target ? target.system.attributes[attributeValue] : undefined;
if (attributeValue && !attribute) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.AttributeFaulty'));
reject();
return;
}
const title = attributeValue
? game.i18n.format('DAGGERHEART.Chat.DualityRoll.AbilityCheckTitle', {
ability: game.i18n.localize(abilities[attributeValue].label)
})
: game.i18n.localize('DAGGERHEART.General.Duality');
const hopeAndFearRoll = `1${rollCommand.hope ?? 'd12'}+1${rollCommand.fear ?? 'd12'}`;
const advantageRoll = `${rollCommand.advantage && !rollCommand.disadvantage ? '+d6' : rollCommand.disadvantage && !rollCommand.advantage ? '-d6' : ''}`;
const attributeRoll = `${attribute?.data?.value ? `${attribute.data.value > 0 ? `+${attribute.data.value}` : `${attribute.data.value}`}` : ''}`;
const roll = new Roll(`${hopeAndFearRoll}${advantageRoll}${attributeRoll}`);
await roll.evaluate();
resolve({
roll,
attribute: attribute
? {
value: attribute.data.value,
label: `${game.i18n.localize(abilities[attributeValue].label)} ${attribute.data.value >= 0 ? `+` : `-`}${attribute.data.value}`
}
: undefined,
title
});
}).then(({ roll, attribute, title }) => {
const cls = getDocumentClass('ChatMessage');
const msgData = {
type: 'dualityRoll',
sound: CONFIG.sounds.dice,
system: {
title: title,
origin: target?.id,
roll: roll._formula,
modifiers: attribute ? [attribute] : [],
hope: { dice: rollCommand.hope ?? 'd12', value: roll.dice[0].total },
fear: { dice: rollCommand.fear ?? 'd12', value: roll.dice[1].total },
advantage:
rollCommand.advantage && !rollCommand.disadvantage
? { dice: 'd6', value: roll.dice[2].total }
: undefined,
disadvantage:
rollCommand.disadvantage && !rollCommand.advantage
? { dice: 'd6', value: roll.dice[2].total }
: undefined
},
user: game.user.id,
content: 'systems/daggerheart/templates/chat/duality-roll.hbs',
rolls: [roll]
};
cls.create(msgData);
});
}
return false;
}
});
const preloadHandlebarsTemplates = async function () {
return foundry.applications.handlebars.loadTemplates([
'systems/daggerheart/templates/sheets/parts/attributes.hbs',