From 91aff8b10df00fb5608923f2d463f408343e7eb2 Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Thu, 16 Apr 2026 09:55:12 +0200
Subject: [PATCH] [Feature] Advantage Default Dice (#1802)
---
module/applications/dialogs/d20RollDialog.mjs | 17 ++++++++++++++---
module/data/actor/character.mjs | 18 +++++++++++++++++-
module/data/actor/companion.mjs | 18 ++++++++++++++++++
module/dice/dualityRoll.mjs | 12 +++---------
templates/dialogs/dice-roll/rollSelection.hbs | 4 ++--
5 files changed, 54 insertions(+), 15 deletions(-)
diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs
index 64fa168a..d8317d70 100644
--- a/module/applications/dialogs/d20RollDialog.mjs
+++ b/module/applications/dialogs/d20RollDialog.mjs
@@ -123,6 +123,10 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
context.advantage = this.config.roll?.advantage;
context.disadvantage = this.config.roll?.disadvantage;
context.diceOptions = CONFIG.DH.GENERAL.diceTypes;
+ context.diceFaces = CONFIG.DH.GENERAL.dieFaces.reduce((acc, face) => {
+ acc[face] = `d${face}`;
+ return acc;
+ }, {});
context.isLite = this.config.roll?.lite;
context.extraFormula = this.config.extraFormula;
context.formula = this.roll.constructFormula(this.config);
@@ -152,9 +156,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
}
if (this.config.uses) this.config.uses = foundry.utils.mergeObject(this.config.uses, rest.uses);
if (rest.roll?.dice) {
- Object.entries(rest.roll.dice).forEach(([key, value]) => {
- this.roll[key] = value;
- });
+ this.roll = foundry.utils.mergeObject(this.roll, rest.roll.dice);
}
if (rest.hasOwnProperty('trait')) {
this.config.roll.trait = rest.trait;
@@ -173,6 +175,15 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
this.disadvantage = advantage === -1;
this.config.roll.advantage = this.config.roll.advantage === advantage ? 0 : advantage;
+
+ if (this.config.roll.advantage === 1 && this.config.data.rules.roll.defaultAdvantageDice) {
+ const faces = Number.parseInt(this.config.data.rules.roll.defaultAdvantageDice);
+ this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces;
+ } else if (this.config.roll.advantage === -1 && this.config.data.rules.roll.defaultDisadvantageDice) {
+ const faces = Number.parseInt(this.config.data.rules.roll.defaultDisadvantageDice);
+ this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces;
+ }
+
this.render();
}
diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs
index 13a47b45..e6bcd294 100644
--- a/module/data/actor/character.mjs
+++ b/module/data/actor/character.mjs
@@ -285,7 +285,23 @@ export default class DhCharacter extends DhCreature {
guaranteedCritical: new fields.BooleanField({
label: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.label',
hint: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.hint'
- })
+ }),
+ defaultAdvantageDice: new fields.NumberField({
+ nullable: true,
+ required: true,
+ integer: true,
+ choices: CONFIG.DH.GENERAL.dieFaces,
+ initial: null,
+ label: 'DAGGERHEART.ACTORS.Character.defaultAdvantageDice'
+ }),
+ defaultDisadvantageDice: new fields.NumberField({
+ nullable: true,
+ required: true,
+ integer: true,
+ choices: CONFIG.DH.GENERAL.dieFaces,
+ initial: null,
+ label: 'DAGGERHEART.ACTORS.Character.defaultDisadvantageDice'
+ }),
})
})
};
diff --git a/module/data/actor/companion.mjs b/module/data/actor/companion.mjs
index 9a21ba23..a1fa1429 100644
--- a/module/data/actor/companion.mjs
+++ b/module/data/actor/companion.mjs
@@ -61,6 +61,24 @@ export default class DhCompanion extends DhCreature {
initial: false,
label: 'DAGGERHEART.GENERAL.Rules.conditionImmunities.vulnerable'
})
+ }),
+ roll: new fields.SchemaField({
+ defaultAdvantageDice: new fields.NumberField({
+ nullable: true,
+ required: true,
+ integer: true,
+ choices: CONFIG.DH.GENERAL.dieFaces,
+ initial: null,
+ label: 'DAGGERHEART.ACTORS.Character.defaultAdvantageDice'
+ }),
+ defaultDisadvantageDice: new fields.NumberField({
+ nullable: true,
+ required: true,
+ integer: true,
+ choices: CONFIG.DH.GENERAL.dieFaces,
+ initial: null,
+ label: 'DAGGERHEART.ACTORS.Character.defaultDisadvantageDice'
+ }),
})
}),
attack: new ActionField({
diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs
index f9a06d37..2448a16d 100644
--- a/module/dice/dualityRoll.mjs
+++ b/module/dice/dualityRoll.mjs
@@ -3,7 +3,6 @@ import D20Roll from './d20Roll.mjs';
import { parseRallyDice, setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
export default class DualityRoll extends D20Roll {
- _advantageFaces = 6;
_advantageNumber = 1;
_rallyIndex;
@@ -11,6 +10,9 @@ export default class DualityRoll extends D20Roll {
super(formula, data, options);
this.rallyChoices = this.setRallyChoices();
this.guaranteedCritical = options.guaranteedCritical;
+
+ const advantageFaces = data.rules?.roll?.defaultAdvantageDice ? Number.parseInt(data.rules.roll.defaultAdvantageDice) : 6
+ this.advantageFaces = Number.isNaN(advantageFaces) ? 6 : advantageFaces;
}
static messageType = 'dualityRoll';
@@ -51,14 +53,6 @@ export default class DualityRoll extends D20Roll {
return this.dice[2] instanceof game.system.api.dice.diceTypes.DisadvantageDie ? this.dice[2] : null;
}
- get advantageFaces() {
- return this._advantageFaces;
- }
-
- set advantageFaces(faces) {
- this._advantageFaces = this.getFaces(faces);
- }
-
get advantageNumber() {
return this._advantageNumber;
}
diff --git a/templates/dialogs/dice-roll/rollSelection.hbs b/templates/dialogs/dice-roll/rollSelection.hbs
index 2deccaf2..c30b0786 100644
--- a/templates/dialogs/dice-roll/rollSelection.hbs
+++ b/templates/dialogs/dice-roll/rollSelection.hbs
@@ -157,8 +157,8 @@
{{/times}}
-