From 1fe8965c0f7659571529017f1b52c0b275faf781 Mon Sep 17 00:00:00 2001 From: Dapoolp Date: Thu, 17 Jul 2025 00:14:13 +0200 Subject: [PATCH] Dardic Rally Dice --- module/applications/dialogs/d20RollDialog.mjs | 1 + module/data/actor/character.mjs | 5 +- module/dice/d20Roll.mjs | 10 ++- module/dice/dualityRoll.mjs | 76 +++++++++++++------ templates/dialogs/dice-roll/rollSelection.hbs | 26 +++++-- templates/ui/chat/duality-roll.hbs | 35 ++++++++- 6 files changed, 114 insertions(+), 39 deletions(-) diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs index 7987dd6b..67ca77e6 100644 --- a/module/applications/dialogs/d20RollDialog.mjs +++ b/module/applications/dialogs/d20RollDialog.mjs @@ -89,6 +89,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio if (this.roll) { context.roll = this.roll; context.rollType = this.roll?.constructor.name; + context.rallyDie = this.roll.rallyChoices; context.experiences = Object.keys(this.config.data.experiences).map(id => ({ id, ...this.config.data.experiences[id] diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 04d3f3da..51b4a7bf 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -123,10 +123,7 @@ export default class DhCharacter extends BaseDataActor { }) }), rally: new fields.ArrayField( - new fields.NumberField({ - integer: true, - initial: 6 - }), + new fields.StringField(), { label: 'DAGGERHEART.CLASS.Feature.rallyDice' } diff --git a/module/dice/d20Roll.mjs b/module/dice/d20Roll.mjs index 0c29fc42..58d45f95 100644 --- a/module/dice/d20Roll.mjs +++ b/module/dice/d20Roll.mjs @@ -39,11 +39,13 @@ export default class D20Roll extends DHRoll { } get hasAdvantage() { - return this.options.roll.advantage === this.constructor.ADV_MODE.ADVANTAGE; + const adv = this.options.roll.advantage.type ?? this.options.roll.advantage; + return adv === this.constructor.ADV_MODE.ADVANTAGE; } get hasDisadvantage() { - return this.options.roll.advantage === this.constructor.ADV_MODE.DISADVANTAGE; + const adv = this.options.roll.advantage.type ?? this.options.roll.advantage; + return adv === this.constructor.ADV_MODE.DISADVANTAGE; } static applyKeybindings(config) { @@ -90,8 +92,8 @@ export default class D20Roll extends DHRoll { configureModifiers() { this.applyAdvantage(); - - this.baseTerms = foundry.utils.deepClone(this.terms); + + this.baseTerms = foundry.utils.deepClone(this.dice); this.options.roll.modifiers = this.applyBaseBonus(); diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index d983b2d6..5fd71e6c 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -4,9 +4,12 @@ import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs'; export default class DualityRoll extends D20Roll { _advantageFaces = 6; + _advantageNumber = 1; + _rallyIndex; constructor(formula, data = {}, options = {}) { super(formula, data, options); + this.rallyChoices = this.setRallyChoices(); } static messageType = 'dualityRoll'; @@ -51,6 +54,35 @@ export default class DualityRoll extends D20Roll { this._advantageFaces = this.getFaces(faces); } + get advantageNumber() { + return this._advantageNumber; + } + + set advantageNumber(value) { + this._advantageNumber = Number(value); + } + + setRallyChoices() { + return this.data?.parent?.effects.reduce((a,c) => { + const change = c.changes.find(ch => ch.key === 'system.bonuses.rally'); + if(change) a.push({ value: c.id, label: change.value }); + return a; + }, []); + } + + get dRally() { + if(!this.rallyFaces) return null; + if(this.hasDisadvantage || this.hasAdvantage) + return this.dice[3]; + else + return this.dice[2]; + } + + get rallyFaces() { + const rallyChoice = this.rallyChoices?.find(r => r.value === this._rallyIndex)?.label; + return rallyChoice ? this.getFaces(rallyChoice) : null; + } + get isCritical() { if (!this.dHope._evaluated || !this.dFear._evaluated) return; return this.dHope.total === this.dFear.total; @@ -66,10 +98,6 @@ export default class DualityRoll extends D20Roll { return this.dHope.total < this.dFear.total; } - get hasBarRally() { - return null; - } - get totalLabel() { const label = this.withHope ? 'DAGGERHEART.GENERAL.hope' @@ -98,24 +126,20 @@ export default class DualityRoll extends D20Roll { } applyAdvantage() { - const dieFaces = this.advantageFaces, - bardRallyFaces = this.hasBarRally, - advDie = new foundry.dice.terms.Die({ faces: dieFaces }); - if (this.hasAdvantage || this.hasDisadvantage || bardRallyFaces) - this.terms.push(new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' })); - if (bardRallyFaces) { - const rallyDie = new foundry.dice.terms.Die({ faces: bardRallyFaces }); - if (this.hasAdvantage) { - this.terms.push( - new foundry.dice.terms.PoolTerm({ - terms: [advDie.formula, rallyDie.formula], - modifiers: ['kh'] - }) - ); - } else if (this.hasDisadvantage) { - this.terms.push(advDie, new foundry.dice.terms.OperatorTerm({ operator: '+' }), rallyDie); - } - } else if (this.hasAdvantage || this.hasDisadvantage) this.terms.push(advDie); + if (this.hasAdvantage || this.hasDisadvantage) { + const dieFaces = this.advantageFaces, + advDie = new foundry.dice.terms.Die({ faces: dieFaces, number: this.advantageNumber }); + if(this.advantageNumber > 1) advDie.modifiers = ['kh']; + this.terms.push( + new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }), + advDie + ); + } + if(this.rallyFaces) + this.terms.push( + new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }), + new foundry.dice.terms.Die({ faces: this.rallyFaces }) + ); } applyBaseBonus() { @@ -138,6 +162,7 @@ export default class DualityRoll extends D20Roll { static postEvaluate(roll, config = {}) { super.postEvaluate(roll, config); + config.roll.hope = { dice: roll.dHope.denomination, value: roll.dHope.total @@ -146,12 +171,19 @@ export default class DualityRoll extends D20Roll { dice: roll.dFear.denomination, value: roll.dFear.total }; + config.roll.rally = { + dice: roll.dRally?.denomination, + value: roll.dRally?.total + }; config.roll.result = { duality: roll.withHope ? 1 : roll.withFear ? -1 : 0, total: roll.dHope.total + roll.dFear.total, label: roll.totalLabel }; + if(roll._rallyIndex && roll.data?.parent) + roll.data.parent.deleteEmbeddedDocuments('ActiveEffect', [roll._rallyIndex]); + setDiceSoNiceForDualityRoll(roll, config.roll.advantage.type); } } diff --git a/templates/dialogs/dice-roll/rollSelection.hbs b/templates/dialogs/dice-roll/rollSelection.hbs index 11fce27a..5a17f6bc 100644 --- a/templates/dialogs/dice-roll/rollSelection.hbs +++ b/templates/dialogs/dice-roll/rollSelection.hbs @@ -88,7 +88,7 @@ {{/if}} {{/each}} -
+
Modifiers
- {{#unless (eq @root.rollType 'D20Roll')}} - + {{#times 10}} + + {{/times}} + + - {{/unless}} -
- + + {{/unless}} + {{#if @root.rallyDie.length}} + {{localize "DAGGERHEART.CLASS.Feature.rallyDice"}} + + {{/if}} + {{#if (eq @root.rollType 'DualityRoll')}}Situational Bonus{{/if}} +
{{/unless}} Formula: {{@root.formula}} diff --git a/templates/ui/chat/duality-roll.hbs b/templates/ui/chat/duality-roll.hbs index 66d32e95..8b44039b 100644 --- a/templates/ui/chat/duality-roll.hbs +++ b/templates/ui/chat/duality-roll.hbs @@ -16,6 +16,11 @@ {{localize "DAGGERHEART.GENERAL.Disadvantage.full"}} {{/if}} + {{#if roll.rally.dice}} +
+ {{localize "DAGGERHEART.CLASS.Feature.rallyDice"}} {{roll.rally.dice}} +
+ {{/if}}
{{roll.formula}}
@@ -38,7 +43,7 @@
{{localize "DAGGERHEART.GENERAL.hope"}}
- +
{{roll.hope.value}}
@@ -49,7 +54,7 @@
{{localize "DAGGERHEART.GENERAL.fear"}}
- +
{{roll.fear.value}}
@@ -72,7 +77,7 @@
- +
{{roll.advantage.value}}
@@ -82,6 +87,30 @@
{{/if}} + {{#if roll.rally.dice}} +
+
+ + 1{{roll.rally.dice}} + + {{roll.rally.value}} +
+
+
    +
  1. +
    +
    +
    + +
    +
    {{roll.rally.value}}
    +
    +
    +
  2. +
+
+
+ {{/if}} {{#each roll.extra as | extra | }}