[Feature] Roll Difficulty Support (#393)

* Added difficulty support in rollDialog/rollMessage and [[/dr]]

* Fixed /dr chat command
This commit is contained in:
WBHarry 2025-07-22 01:51:49 +02:00 committed by GitHub
parent 42a705a870
commit 3d723e7d8c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 89 additions and 54 deletions

View file

@ -8,7 +8,7 @@ import { getCommandTarget, rollCommandToJSON } from './module/helpers/utils.mjs'
import { NarrativeCountdowns } from './module/applications/ui/countdowns.mjs'; import { NarrativeCountdowns } from './module/applications/ui/countdowns.mjs';
import { DualityRollColor } from './module/data/settings/Appearance.mjs'; import { DualityRollColor } from './module/data/settings/Appearance.mjs';
import { DHRoll, DualityRoll, D20Roll, DamageRoll, DualityDie } from './module/dice/_module.mjs'; import { DHRoll, DualityRoll, D20Roll, DamageRoll, DualityDie } from './module/dice/_module.mjs';
import { renderDualityButton } from './module/enrichers/DualityRollEnricher.mjs'; import { enrichedDualityRoll, renderDualityButton } from './module/enrichers/DualityRollEnricher.mjs';
import { renderMeasuredTemplate } from './module/enrichers/TemplateEnricher.mjs'; import { renderMeasuredTemplate } from './module/enrichers/TemplateEnricher.mjs';
import { registerCountdownHooks } from './module/data/countdowns.mjs'; import { registerCountdownHooks } from './module/data/countdowns.mjs';
import { import {
@ -188,49 +188,21 @@ Hooks.on('chatMessage', (_, message) => {
} }
const traitValue = rollCommand.trait?.toLowerCase(); const traitValue = rollCommand.trait?.toLowerCase();
const advantageState = rollCommand.advantage ? true : rollCommand.disadvantage ? false : null; const advantage = rollCommand.advantage
? CONFIG.DH.ACTIONS.advandtageState.advantage.value
: rollCommand.disadvantage
? CONFIG.DH.ACTIONS.advandtageState.disadvantage.value
: undefined;
const difficulty = rollCommand.difficulty;
// Target not required if an attribute is not used. const target = getCommandTarget();
const target = traitValue ? getCommandTarget() : undefined; const title = traitValue
if (target || !traitValue) { ? game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
new Promise(async (resolve, reject) => { ability: game.i18n.localize(SYSTEM.ACTOR.abilities[traitValue].label)
const trait = target ? target.system.traits[traitValue] : undefined; })
if (traitValue && !trait) { : game.i18n.localize('DAGGERHEART.GENERAL.duality');
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.attributeFaulty'));
reject();
return;
}
const title = traitValue
? game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
ability: game.i18n.localize(SYSTEM.ACTOR.abilities[traitValue].label)
})
: game.i18n.localize('DAGGERHEART.GENERAL.duality');
const config = {
title: title,
roll: {
trait: traitValue
},
data: {
traits: {
[traitValue]: trait
}
},
source: target,
hasSave: false,
dialog: { configure: false },
evaluate: true,
advantage: rollCommand.advantage == true,
disadvantage: rollCommand.disadvantage == true
};
await CONFIG.Dice.daggerheart['DualityRoll'].build(config);
resolve();
});
}
enrichedDualityRoll({ traitValue, target, difficulty, title, label: 'test', actionType: null, advantage });
return false; return false;
} }
}); });

View file

@ -1196,6 +1196,7 @@
}, },
"Roll": { "Roll": {
"attack": "Attack Roll", "attack": "Attack Roll",
"difficulty": "Roll (Difficulty {difficulty})",
"primaryWeaponAttack": "Primary Weapon Attack Roll", "primaryWeaponAttack": "Primary Weapon Attack Roll",
"secondaryWeaponAttack": "Secondary Weapon Attack Roll", "secondaryWeaponAttack": "Secondary Weapon Attack Roll",
"spellcast": "Spellcast Roll", "spellcast": "Spellcast Roll",
@ -1310,6 +1311,7 @@
"single": "Experience", "single": "Experience",
"plural": "Experiences" "plural": "Experiences"
}, },
"failure": "Failure",
"fear": "Fear", "fear": "Fear",
"features": "Features", "features": "Features",
"formula": "Formula", "formula": "Formula",
@ -1345,6 +1347,7 @@
"scalable": "Scalable", "scalable": "Scalable",
"situationalBonus": "Situational Bonus", "situationalBonus": "Situational Bonus",
"stress": "Stress", "stress": "Stress",
"success": "Success",
"take": "Take", "take": "Take",
"Target": { "Target": {
"single": "Target", "single": "Target",

View file

@ -1,10 +1,10 @@
export default class DhpChatMessage extends foundry.documents.ChatMessage { export default class DhpChatMessage extends foundry.documents.ChatMessage {
async renderHTML() { async renderHTML() {
if (this.system.messageTemplate) if (this.system.messageTemplate)
this.content = await foundry.applications.handlebars.renderTemplate( this.content = await foundry.applications.handlebars.renderTemplate(this.system.messageTemplate, {
this.system.messageTemplate, ...this.system,
this.system _source: this.system._source
); });
/* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */ /* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */
const html = await super.renderHTML(); const html = await super.renderHTML();

View file

@ -21,19 +21,34 @@ function getDualityMessage(roll) {
? game.i18n.localize(abilities[roll.trait].label) ? game.i18n.localize(abilities[roll.trait].label)
: game.i18n.localize('DAGGERHEART.GENERAL.duality'); : game.i18n.localize('DAGGERHEART.GENERAL.duality');
const advantage = roll.advantage
? CONFIG.DH.ACTIONS.advandtageState.advantage.value
: roll.disadvantage
? CONFIG.DH.ACTIONS.advandtageState.disadvantage.value
: undefined;
const advantageLabel =
advantage === CONFIG.DH.ACTIONS.advandtageState.advantage.value
? 'Advantage'
: advantage === CONFIG.DH.ACTIONS.advandtageState.disadvantage.value
? 'Disadvantage'
: undefined;
const dualityElement = document.createElement('span'); const dualityElement = document.createElement('span');
dualityElement.innerHTML = ` dualityElement.innerHTML = `
<button class="duality-roll-button" <button class="duality-roll-button"
data-title="${label}" data-title="${label}"
data-label="${dataLabel}" data-label="${dataLabel}"
data-hope="${roll.hope ?? 'd12'}" data-hope="${roll.hope ?? 'd12'}"
data-fear="${roll.fear ?? 'd12'}" data-fear="${roll.fear ?? 'd12'}"
${advantage ? `data-advantage="${advantage}"` : ''}
${roll.difficulty !== undefined ? `data-difficulty="${roll.difficulty}"` : ''}
${roll.trait && abilities[roll.trait] ? `data-trait="${roll.trait}"` : ''} ${roll.trait && abilities[roll.trait] ? `data-trait="${roll.trait}"` : ''}
${roll.advantage ? 'data-advantage="true"' : ''} ${roll.advantage ? 'data-advantage="true"' : ''}
${roll.disadvantage ? 'data-disadvantage="true"' : ''} ${roll.disadvantage ? 'data-disadvantage="true"' : ''}
> >
<i class="fa-solid fa-circle-half-stroke"></i> <i class="fa-solid fa-circle-half-stroke"></i>
${label} ${label}
${roll.difficulty || advantageLabel ? `(${[roll.difficulty, game.i18n.localize(`DAGGERHEART.GENERAL.${advantageLabel}.short`)].filter(x => x).join(' ')})` : ''}
</button> </button>
`; `;
@ -43,16 +58,39 @@ function getDualityMessage(roll) {
export const renderDualityButton = async event => { export const renderDualityButton = async event => {
const button = event.currentTarget, const button = event.currentTarget,
traitValue = button.dataset.trait?.toLowerCase(), traitValue = button.dataset.trait?.toLowerCase(),
target = getCommandTarget(); target = getCommandTarget(),
difficulty = button.dataset.difficulty,
advantage = button.dataset.advantage ? Number(button.dataset.advantage) : undefined;
await enrichedDualityRoll(
{
traitValue,
target,
difficulty,
title: button.dataset.title,
label: button.dataset.label,
actionType: button.dataset.actionType,
advantage
},
event
);
};
export const enrichedDualityRoll = async (
{ traitValue, target, difficulty, title, label, actionType, advantage },
event
) => {
if (!target) return; if (!target) return;
const config = { const config = {
event: event, event: event ?? {},
title: button.dataset.title, title: title,
roll: { roll: {
modifier: traitValue ? target.system.traits[traitValue].value : null, modifier: traitValue ? target.system.traits[traitValue].value : null,
label: button.dataset.label, label: label,
type: button.dataset.actionType ?? null // Need check difficulty: difficulty,
advantage,
type: actionType ?? null // Need check,
}, },
chatMessage: { chatMessage: {
template: 'systems/daggerheart/templates/ui/chat/duality-roll.hbs' template: 'systems/daggerheart/templates/ui/chat/duality-roll.hbs'

View file

@ -131,13 +131,17 @@
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: 16px;
height: 32px;
.roll-mode-select { .roll-mode-select {
width: min-content; width: min-content;
height: 100%;
} }
button { button {
flex: 1; flex: 1;
height: 100%;
font-family: @font-body;
} }
} }
} }

View file

@ -136,14 +136,22 @@
<input type="text" value="{{extraFormula}}" name="extraFormula" placeholder="{{#if (eq @root.rollType 'DualityRoll')}}Ex: 1d6 + 5{{else}}Situational Bonus{{/if}}"> <input type="text" value="{{extraFormula}}" name="extraFormula" placeholder="{{#if (eq @root.rollType 'DualityRoll')}}Ex: 1d6 + 5{{else}}Situational Bonus{{/if}}">
</fieldset> </fieldset>
{{/unless}} {{/unless}}
<span class="formula-label"><b>{{localize "DAGGERHEART.GENERAL.formula"}}:</b> {{@root.formula}}</span> <span class="formula-label"><b>{{localize "DAGGERHEART.GENERAL.formula"}}:</b> {{@root.formula}}</span>
<div class="roll-dialog-controls"> <div class="roll-dialog-controls">
<select class="roll-mode-select" name="selectedRollMode"> <select class="roll-mode-select" name="selectedRollMode">
{{selectOptions rollModes selected=selectedRollMode valueAttr="action" labelAttr="label" localize=true}} {{selectOptions rollModes selected=selectedRollMode valueAttr="action" labelAttr="label" localize=true}}
</select> </select>
<button class="sunmit-btn" data-action="submitRoll"{{#unless canRoll}} disabled{{/unless}}> <button class="sunmit-btn" data-action="submitRoll"{{#unless canRoll}} disabled{{/unless}}>
<i class="fa-solid fa-dice"></i> <i class="fa-solid fa-dice"></i>
<span class="label">{{localize "DAGGERHEART.GENERAL.roll"}}</span> <span class="label">
{{#if @root.rollConfig.roll.difficulty}}
{{localize "DAGGERHEART.GENERAL.Roll.difficulty" difficulty=@root.rollConfig.roll.difficulty}}
{{else}}
{{localize "DAGGERHEART.GENERAL.roll"}}
{{/if}}
</span>
</button> </button>
</div> </div>
{{else}} {{else}}

View file

@ -140,7 +140,17 @@
</div> </div>
</div> </div>
<div class="dice-total duality {{#if (eq roll.result.duality 1)}}hope{{else}}{{#if (eq roll.result.duality -1)}}fear{{else}}critical{{/if}}{{/if}}"> <div class="dice-total duality {{#if (eq roll.result.duality 1)}}hope{{else}}{{#if (eq roll.result.duality -1)}}fear{{else}}critical{{/if}}{{/if}}">
<div class="dice-total-label">{{roll.result.label}}</div> <div class="dice-total-label">
{{#unless (eq _source.roll.success undefined)}}
{{#if _source.roll.success}}
{{localize "DAGGERHEART.GENERAL.success"}} {{localize "DAGGERHEART.GENERAL.withThing" thing=roll.result.label}}
{{else}}
{{localize "DAGGERHEART.GENERAL.failure"}} {{localize "DAGGERHEART.GENERAL.withThing" thing=roll.result.label}}
{{/if}}
{{else}}
{{roll.result.label}}
{{/unless}}
</div>
<div class="dice-total-value"> <div class="dice-total-value">
{{roll.total}} {{roll.total}}
</div> </div>