Added support for d20 rolls

This commit is contained in:
WBHarry 2025-07-22 22:16:05 +02:00
parent 565f0f9e6f
commit e7c3bcc362
6 changed files with 107 additions and 55 deletions

View file

@ -1339,6 +1339,7 @@
"quantity": "Quantity", "quantity": "Quantity",
"range": "Range", "range": "Range",
"recovery": "Recovery", "recovery": "Recovery",
"reroll": "Reroll",
"rerollThing": "Reroll {thing}", "rerollThing": "Reroll {thing}",
"resource": "Resource", "resource": "Resource",
"roll": "Roll", "roll": "Roll",
@ -1616,6 +1617,10 @@
"title": "Heal - {healing}", "title": "Heal - {healing}",
"heal": "Heal" "heal": "Heal"
}, },
"reroll": {
"confirmTitle": "Reroll Dice",
"confirmText": "Are you sure you want to reroll?"
},
"resourceRoll": { "resourceRoll": {
"playerMessage": "{user} rerolled their {name}" "playerMessage": "{user} rerolled their {name}"
} }

View file

@ -1,5 +1,3 @@
import { getDiceSoNicePresets } from '../../config/generalConfig.mjs';
export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog { export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog {
constructor(options) { constructor(options) {
super(options); super(options);
@ -316,52 +314,23 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
}; };
rerollEvent = async (event, message) => { rerollEvent = async (event, message) => {
if (!event.shiftKey) {
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
title: game.i18n.localize('DAGGERHEART.UI.Chat.reroll.confirmTitle')
},
content: game.i18n.localize('DAGGERHEART.UI.Chat.reroll.confirmText')
});
if (!confirmed) return;
}
const target = event.target.closest('button[data-die-index]'); const target = event.target.closest('button[data-die-index]');
let originalRoll_parsed = message.rolls.map(roll => JSON.parse(roll))[0]; let originalRoll_parsed = message.rolls.map(roll => JSON.parse(roll))[0];
let parsedRoll = game.system.api.dice.DualityRoll.fromData({ ...originalRoll_parsed, evaluated: false }); const rollClass =
const term = parsedRoll.terms[target.dataset.dieIndex]; game.system.api.dice[
await term.reroll(`/r1=${term.total}`); message.type === 'dualityRoll' ? 'DualityRoll' : target.dataset.type === 'damage' ? 'DHRoll' : 'D20Roll'
if (game.modules.get('dice-so-nice')?.active) { ];
const diceSoNiceRoll = { const { newRoll, parsedRoll } = await rollClass.reroll(originalRoll_parsed, target, message);
_evaluated: true,
dice: [
new foundry.dice.terms.Die({
...term,
faces: term._faces,
results: term.results.filter(x => !x.rerolled)
})
],
options: { appearance: {} }
};
const diceSoNicePresets = getDiceSoNicePresets();
switch (target.dataset.type) {
case 'hope':
diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets.hope };
break;
case 'fear':
diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets.fear };
break;
case 'advantage':
diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets.advantage };
break;
case 'disadvantage':
diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets.disadvantage };
break;
}
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
}
await parsedRoll.evaluate();
const newRoll = game.system.api.dice.DualityRoll.postEvaluate(parsedRoll, {
targets: message.system.targets,
roll: {
advantage: message.system.roll.advantage?.type,
difficulty: message.system.roll.difficulty ? Number(message.system.roll.difficulty) : null
}
});
newRoll.extra = newRoll.extra.slice(2);
await game.messages.get(message._id).update({ await game.messages.get(message._id).update({
'system.roll': newRoll, 'system.roll': newRoll,

View file

@ -172,4 +172,22 @@ export default class D20Roll extends DHRoll {
resetFormula() { resetFormula() {
return (this._formula = this.constructor.getFormula(this.terms)); return (this._formula = this.constructor.getFormula(this.terms));
} }
static async reroll(rollString, _target, message) {
let parsedRoll = game.system.api.dice.D20Roll.fromData(rollString);
parsedRoll = await parsedRoll.reroll();
const newRoll = game.system.api.dice.D20Roll.postEvaluate(parsedRoll, {
targets: message.system.targets,
roll: {
advantage: message.system.roll.advantage?.type,
difficulty: message.system.roll.difficulty ? Number(message.system.roll.difficulty) : null
}
});
if (game.modules.get('dice-so-nice')?.active) {
await game.dice3d.showForRoll(parsedRoll, game.user, true);
}
return { newRoll, parsedRoll };
}
} }

View file

@ -1,6 +1,7 @@
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs'; import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
import D20Roll from './d20Roll.mjs'; import D20Roll from './d20Roll.mjs';
import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs'; import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
import { getDiceSoNicePresets } from '../config/generalConfig.mjs';
export default class DualityRoll extends D20Roll { export default class DualityRoll extends D20Roll {
_advantageFaces = 6; _advantageFaces = 6;
@ -193,4 +194,53 @@ export default class DualityRoll extends D20Roll {
return data; return data;
} }
static async reroll(rollString, target, message) {
let parsedRoll = game.system.api.dice.DualityRoll.fromData({ ...rollString, evaluated: false });
const term = parsedRoll.terms[target.dataset.dieIndex];
await term.reroll(`/r1=${term.total}`);
if (game.modules.get('dice-so-nice')?.active) {
const diceSoNiceRoll = {
_evaluated: true,
dice: [
new foundry.dice.terms.Die({
...term,
faces: term._faces,
results: term.results.filter(x => !x.rerolled)
})
],
options: { appearance: {} }
};
const diceSoNicePresets = getDiceSoNicePresets();
switch (target.dataset.type) {
case 'hope':
diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets.hope };
break;
case 'fear':
diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets.fear };
break;
case 'advantage':
diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets.advantage };
break;
case 'disadvantage':
diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets.disadvantage };
break;
}
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
}
await parsedRoll.evaluate();
const newRoll = game.system.api.dice.DualityRoll.postEvaluate(parsedRoll, {
targets: message.system.targets,
roll: {
advantage: message.system.roll.advantage?.type,
difficulty: message.system.roll.difficulty ? Number(message.system.roll.difficulty) : null
}
});
newRoll.extra = newRoll.extra.slice(2);
return { newRoll, parsedRoll };
}
} }

View file

@ -60,6 +60,19 @@
} }
} }
} }
&.rerollable {
.reroll-button {
border: none;
background: initial;
&:hover {
background: var(--button-background-color);
border: 1px solid var(--button-border-color);
}
}
}
// margin: 0; // margin: 0;
> .roll { > .roll {
display: flex; display: flex;

View file

@ -12,16 +12,13 @@
<span class="part-total">{{total}}</span> <span class="part-total">{{total}}</span>
</header> </header>
<div class="flexrow"> <div class="flexrow">
<ol class="dice-rolls"> <ol class="dice-rolls rerollable">
{{#each results}} <button type="checkbox" class="reroll-button" data-die-index="0" data-tooltip="{{localize "DAGGERHEART.GENERAL.reroll"}}">
<li class="roll die {{../dice}}{{#if discarded}} discarded{{/if}} min">{{result}}</li> {{#each results as |result index|}}
<li class="roll die {{../dice}}{{#if discarded}} discarded{{/if}} min">{{result.result}}</li>
{{/each}} {{/each}}
</button>
</ol> </ol>
{{#if (eq index 0)}}
<div class="attack-roll-advantage-container">
{{#if (eq ../roll.advantage.type 1)}}{{localize "DAGGERHEART.GENERAL.Advantage.full"}}{{/if}}{{#if (eq ../roll.advantage.type -1)}}{{localize "DAGGERHEART.GENERAL.Disadvantage.full"}}{{/if}}
</div>
{{/if}}
</div> </div>
{{/each}} {{/each}}
</div> </div>