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",
"range": "Range",
"recovery": "Recovery",
"reroll": "Reroll",
"rerollThing": "Reroll {thing}",
"resource": "Resource",
"roll": "Roll",
@ -1616,6 +1617,10 @@
"title": "Heal - {healing}",
"heal": "Heal"
},
"reroll": {
"confirmTitle": "Reroll Dice",
"confirmText": "Are you sure you want to reroll?"
},
"resourceRoll": {
"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 {
constructor(options) {
super(options);
@ -316,52 +314,23 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
};
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]');
let originalRoll_parsed = message.rolls.map(roll => JSON.parse(roll))[0];
let parsedRoll = game.system.api.dice.DualityRoll.fromData({ ...originalRoll_parsed, 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);
const rollClass =
game.system.api.dice[
message.type === 'dualityRoll' ? 'DualityRoll' : target.dataset.type === 'damage' ? 'DHRoll' : 'D20Roll'
];
const { newRoll, parsedRoll } = await rollClass.reroll(originalRoll_parsed, target, message);
await game.messages.get(message._id).update({
'system.roll': newRoll,

View file

@ -172,4 +172,22 @@ export default class D20Roll extends DHRoll {
resetFormula() {
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 D20Roll from './d20Roll.mjs';
import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
import { getDiceSoNicePresets } from '../config/generalConfig.mjs';
export default class DualityRoll extends D20Roll {
_advantageFaces = 6;
@ -193,4 +194,53 @@ export default class DualityRoll extends D20Roll {
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;
> .roll {
display: flex;

View file

@ -12,16 +12,13 @@
<span class="part-total">{{total}}</span>
</header>
<div class="flexrow">
<ol class="dice-rolls">
{{#each results}}
<li class="roll die {{../dice}}{{#if discarded}} discarded{{/if}} min">{{result}}</li>
<ol class="dice-rolls rerollable">
<button type="checkbox" class="reroll-button" data-die-index="0" data-tooltip="{{localize "DAGGERHEART.GENERAL.reroll"}}">
{{#each results as |result index|}}
<li class="roll die {{../dice}}{{#if discarded}} discarded{{/if}} min">{{result.result}}</li>
{{/each}}
</button>
</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>
{{/each}}
</div>