From 2c994ac78cd746cc1163ce0975e1415e1f615f78 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Tue, 21 Apr 2026 16:13:12 +0200 Subject: [PATCH] [Feature] Roll Rules Standardization (#1818) * Moved default hope/fear/advantage/disadvantage under system/rules/roll and changed them to be dice indexs * . * . * . --- lang/en.json | 17 ++-- module/applications/dialogs/d20RollDialog.mjs | 8 +- .../dialogs/levelupOptionsDialog.mjs | 18 +++- module/applications/levelup/levelup.mjs | 3 +- module/data/actor/character.mjs | 82 +++++++++++-------- module/data/actor/companion.mjs | 22 ++--- module/data/levelData.mjs | 1 + module/data/levelTier.mjs | 22 +++-- module/data/levelup.mjs | 6 +- module/dice/dualityRoll.mjs | 6 +- ...ary_Demon_of_Despair_kE4dfhqmIQpNd44e.json | 4 +- ...rsary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json | 4 +- ...ironment_Cult_Ritual_QAXXiOKBDmCTauHD.json | 4 +- .../levelupOptionsDialog/parts/tier.hbs | 15 ++-- templates/levelup/tabs/advancements.hbs | 3 +- 15 files changed, 132 insertions(+), 83 deletions(-) diff --git a/lang/en.json b/lang/en.json index d6452d30..5a8594f5 100755 --- a/lang/en.json +++ b/lang/en.json @@ -237,10 +237,6 @@ "unequip": "Unequip", "useItem": "Use Item" }, - "defaultHopeDice": "Default Hope Dice", - "defaultFearDice": "Default Fear Dice", - "defaultAdvantageDice": "Default Advantage Dice", - "defaultDisadvantageDice": "Default Disadvantage Dice", "disadvantageSources": { "label": "Disadvantage Sources", "hint": "Add single words or short text as reminders and hints of what a character has disadvantage on." @@ -306,6 +302,16 @@ }, "noPartner": "No Partner selected" }, + "Creature": { + "rules": { + "roll": { + "hope": { "label": "Default Hope Dice Index", "hint": "Index for the default hope dice. 0=d4, 1=d6, 2=d8, 3=d10, 4=d12, 5=d20" }, + "fear": { "label": "Default Fear Dice Index", "hint": "Index for the default fear dice. 0=d4, 1=d6, 2=d8, 3=d10, 4=d12, 5=d20" }, + "advantage": { "label": "Default Advantage Dice Index", "hint": "Index for the default advantage dice. 0=d4, 1=d6, 2=d8, 3=d10, 4=d12, 5=d20" }, + "disadvantage": { "label": "Default Disadvantage Dice Index", "hint": "Index for the default disadvantage dice. 0=d4, 1=d6, 2=d8, 3=d10, 4=d12, 5=d20" } + } + } + }, "Environment": { "FIELDS": { "description": { @@ -1268,7 +1274,7 @@ }, "LevelupData": { "checkboxSelections": "Checkboxes", - "minCost": "Cost Per Checkbox" + "minCost": "Minimum Boxes Picked" }, "Range": { "self": { @@ -2481,6 +2487,7 @@ "step": "Step", "stress": "Stress", "subclasses": "Subclasses", + "subType": "Subtype", "success": "Success", "summon": { "single": "Summon", diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs index 067aa473..926b3a80 100644 --- a/module/applications/dialogs/d20RollDialog.mjs +++ b/module/applications/dialogs/d20RollDialog.mjs @@ -176,11 +176,11 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio 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); + if (this.config.roll.advantage === 1 && this.config.data.rules.roll.advantageFaces) { + const faces = Number.parseInt(this.config.data.rules.roll.advantageFaces); 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); + } else if (this.config.roll.advantage === -1 && this.config.data.rules.roll.disadvantageFaces) { + const faces = Number.parseInt(this.config.data.rules.roll.disadvantageFaces); this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces; } diff --git a/module/applications/dialogs/levelupOptionsDialog.mjs b/module/applications/dialogs/levelupOptionsDialog.mjs index afc247b3..dfe098e1 100644 --- a/module/applications/dialogs/levelupOptionsDialog.mjs +++ b/module/applications/dialogs/levelupOptionsDialog.mjs @@ -53,8 +53,23 @@ export default class LevelupOptionsDialog extends HandlebarsApplicationMixin(App async _prepareContext(_options) { const context = await super._prepareContext(_options); - context.item = this.item; context.fields = this.item.system.schema.fields.levelupOptionTiers.element.element.fields; + context.item = this.item; + context.levelupOptionTiers = Object.keys(this.item.system.levelupOptionTiers).reduce((acc, key) => { + const tier = this.item.system.levelupOptionTiers[key]; + acc[key] = Object.keys(tier).reduce((acc, key) => { + const option = tier[key]; + acc[key] = { + ...option, + typeData: option.type ? LevelOptionType[option.type] : null + }; + + return acc; + }, {}); + + return acc; + }, {}) + context.optionTypes = LevelOptionType; context.selectedOption = this.selectedOption; @@ -63,6 +78,7 @@ export default class LevelupOptionsDialog extends HandlebarsApplicationMixin(App static async updateData(_event, _element, formData) { const data = foundry.utils.expandObject(formData.object); + await this.item.update(data) this.render(); } diff --git a/module/applications/levelup/levelup.mjs b/module/applications/levelup/levelup.mjs index c4616d9a..4a9afcac 100644 --- a/module/applications/levelup/levelup.mjs +++ b/module/applications/levelup/levelup.mjs @@ -527,7 +527,8 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) minCost: Number(button.dataset.cost), amount: button.dataset.amount ? Number(button.dataset.amount) : null, value: button.dataset.value, - type: button.dataset.type + type: button.dataset.type, + subType: button.dataset.subType }; if (button.dataset.type === 'domainCard') { diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 3e85c139..787c19b3 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -261,24 +261,6 @@ export default class DhCharacter extends DhCreature { }) } }), - dualityRoll: new fields.SchemaField({ - defaultHopeDice: new fields.NumberField({ - nullable: false, - required: true, - integer: true, - choices: CONFIG.DH.GENERAL.dieFaces, - initial: 12, - label: 'DAGGERHEART.ACTORS.Character.defaultHopeDice' - }), - defaultFearDice: new fields.NumberField({ - nullable: false, - required: true, - integer: true, - choices: CONFIG.DH.GENERAL.dieFaces, - initial: 12, - label: 'DAGGERHEART.ACTORS.Character.defaultFearDice' - }) - }), burden: new fields.SchemaField({ ignore: new fields.BooleanField({ label: 'DAGGERHEART.ACTORS.Character.burden.ignore.label' }) }), @@ -287,29 +269,49 @@ export default class DhCharacter extends DhCreature { label: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.label', hint: 'DAGGERHEART.ACTORS.Character.roll.guaranteedCritical.hint' }), - defaultAdvantageDice: new fields.NumberField({ - nullable: true, + hopeIndex: new fields.NumberField({ required: true, integer: true, - choices: CONFIG.DH.GENERAL.dieFaces, - initial: null, - label: 'DAGGERHEART.ACTORS.Character.defaultAdvantageDice' + min: 0, + max: 5, + initial: 4, + label: 'DAGGERHEART.ACTORS.Creature.rules.roll.hope.label', + hint: 'DAGGERHEART.ACTORS.Creature.rules.roll.hope.hint' }), - defaultDisadvantageDice: new fields.NumberField({ - nullable: true, + fearIndex: new fields.NumberField({ required: true, integer: true, - choices: CONFIG.DH.GENERAL.dieFaces, - initial: null, - label: 'DAGGERHEART.ACTORS.Character.defaultDisadvantageDice' + min: 0, + max: 5, + initial: 4, + label: 'DAGGERHEART.ACTORS.Creature.rules.roll.fear.label', + hint: 'DAGGERHEART.ACTORS.Creature.rules.roll.fear.hint' }), + advantageIndex: new fields.NumberField({ + required: true, + integer: true, + min: 0, + max: 5, + initial: 1, + label: 'DAGGERHEART.ACTORS.Creature.rules.roll.advantage.label', + hint: 'DAGGERHEART.ACTORS.Creature.rules.roll.advantage.hint' + }), + disadvantageIndex: new fields.NumberField({ + required: true, + integer: true, + min: 0, + max: 5, + initial: 1, + label: 'DAGGERHEART.ACTORS.Creature.rules.roll.disadvantage.label', + hint: 'DAGGERHEART.ACTORS.Creature.rules.roll.disadvantage.hint' + }), + comboDieIndex: new fields.NumberField({ + integer: true, + min: 0, + max: 5, + initial: 0, + }) }), - comboDieIndex: new fields.NumberField({ - integer: true, - min: 0, - max: 5, - initial: 0, - }) }) }; } @@ -764,8 +766,8 @@ export default class DhCharacter extends DhCreature { } }); break; - case 'comboStrikes': - this.rules.comboDieIndex += 1; + case 'dice': + this.rules.roll[selection.subType] += 1; break; } } @@ -824,6 +826,14 @@ export default class DhCharacter extends DhCreature { isReversed: true }; + /* Add convience Faces properties for all dice */ + const { hopeIndex, fearIndex, advantageIndex, disadvantageIndex, comboDieIndex } = this.rules.roll; + const dice = { hopeIndex, fearIndex, advantageIndex, disadvantageIndex, comboDieIndex }; + for (const dieKey of Object.keys(dice)) { + const diceBaseKey = dieKey.replace('Index', ''); + this.rules.roll[`${diceBaseKey}Faces`] = CONFIG.DH.GENERAL.dieFaces[dice[dieKey]]; + } + this.attack.damage.parts.hitPoints.value.custom.formula = `@prof${this.basicAttackDamageDice}${this.rules.attack.damage.bonus ? ` + ${this.rules.attack.damage.bonus}` : ''}`; } diff --git a/module/data/actor/companion.mjs b/module/data/actor/companion.mjs index a1fa1429..dd00a7e8 100644 --- a/module/data/actor/companion.mjs +++ b/module/data/actor/companion.mjs @@ -63,21 +63,23 @@ export default class DhCompanion extends DhCreature { }) }), roll: new fields.SchemaField({ - defaultAdvantageDice: new fields.NumberField({ - nullable: true, + advantage: new fields.NumberField({ required: true, integer: true, - choices: CONFIG.DH.GENERAL.dieFaces, - initial: null, - label: 'DAGGERHEART.ACTORS.Character.defaultAdvantageDice' + min: 0, + max: 5, + initial: 1, + label: 'DAGGERHEART.ACTORS.Creature.rules.roll.advantage.label', + hint: 'DAGGERHEART.ACTORS.Creature.rules.roll.advantage.hint' }), - defaultDisadvantageDice: new fields.NumberField({ - nullable: true, + disadvantage: new fields.NumberField({ required: true, integer: true, - choices: CONFIG.DH.GENERAL.dieFaces, - initial: null, - label: 'DAGGERHEART.ACTORS.Character.defaultDisadvantageDice' + min: 0, + max: 5, + initial: 1, + label: 'DAGGERHEART.ACTORS.Creature.rules.roll.disadvantage.label', + hint: 'DAGGERHEART.ACTORS.Creature.rules.roll.disadvantage.hint' }), }) }), diff --git a/module/data/levelData.mjs b/module/data/levelData.mjs index 4f55d9ee..ac5546c0 100644 --- a/module/data/levelData.mjs +++ b/module/data/levelData.mjs @@ -41,6 +41,7 @@ export default class DhLevelData extends foundry.abstract.DataModel { level: new fields.NumberField({ required: true, integer: true }), optionKey: new fields.StringField({ required: true }), type: new fields.StringField({ required: true, choices: LevelOptionType }), + subType: new fields.StringField({ nullable: true }), checkboxNr: new fields.NumberField({ required: true, integer: true }), value: new fields.NumberField({ integer: true }), minCost: new fields.NumberField({ integer: true }), diff --git a/module/data/levelTier.mjs b/module/data/levelTier.mjs index 6a9c13f6..d300756e 100644 --- a/module/data/levelTier.mjs +++ b/module/data/levelTier.mjs @@ -69,6 +69,10 @@ export class DhLevelOption extends foundry.abstract.DataModel { choices: LevelOptionType, label: 'DAGGERHEART.GENERAL.type' }), + subType: new fields.StringField({ + nullable: true, + label: 'DAGGERHEART.GENERAL.subType', + }), value: new fields.NumberField({ integer: true, label: 'DAGGERHEART.GENERAL.value' @@ -136,13 +140,6 @@ export const CompanionLevelOptionType = { } }; -export const ClassLevelOptionTypes = { - comboStrikes: { - id: 'comboStrikes', - label: 'Increase your Combo Die size', - }, -}; - export const LevelOptionType = { trait: { id: 'trait', @@ -192,7 +189,16 @@ export const LevelOptionType = { id: 'multiclass', label: 'Multiclass' }, - ...ClassLevelOptionTypes, + dice: { + id: 'dice', + label: 'Increase Dice Size', + subTypes: { + hopeIndex: { key: 'hopeIndex', label: 'DAGGERHEART.GENERAL.hope' }, + fearIndex: { key: 'fearIndex', label: 'DAGGERHEART.GENERAL.fear' }, + advantageIndex: { key: 'advantageIndex', label: 'DAGGERHEART.GENERAL.Advantage.full' }, + comboDieIndex:{ key: 'comboDieIndex', label: 'Combo Die' } // Translation pending actual useage + }, + }, ...CompanionLevelOptionType }; diff --git a/module/data/levelup.mjs b/module/data/levelup.mjs index 4dc1c058..37d579c8 100644 --- a/module/data/levelup.mjs +++ b/module/data/levelup.mjs @@ -90,6 +90,7 @@ export class DhLevelup extends foundry.abstract.DataModel { checkboxSelections: new fields.NumberField({ required: true, integer: true }), minCost: new fields.NumberField({ required: true, integer: true }), type: new fields.StringField({ required: true, choices: LevelOptionType }), + subType: new fields.StringField({ nullable: true }), value: new fields.NumberField({ integer: true }), amount: new fields.NumberField({ integer: true }) }) @@ -242,7 +243,7 @@ export class DhLevelup extends foundry.abstract.DataModel { const checkboxes = [...Array(option.checkboxSelections).keys()].flatMap(index => { const checkboxNr = index + 1; const checkboxData = selections[tierKey]?.[optionKey]?.[checkboxNr]; - const checkbox = { ...option, checkboxNr, tier: tierKey }; + const checkbox = { ...option, checkboxNr, tier: tierKey, option: optionKey }; if (checkboxData) { checkbox.level = checkboxData.level; @@ -343,7 +344,8 @@ export class DhLevelupLevel extends foundry.abstract.DataModel { value: new fields.StringField(), data: new fields.ArrayField(new fields.StringField()), secondaryData: new fields.TypedObjectField(new fields.StringField()), - type: new fields.StringField({ required: true }) + type: new fields.StringField({ required: true }), + subType: new fields.StringField({ nullable: true }), }) ) ) diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index 2448a16d..e16ac5f2 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -11,7 +11,7 @@ export default class DualityRoll extends D20Roll { this.rallyChoices = this.setRallyChoices(); this.guaranteedCritical = options.guaranteedCritical; - const advantageFaces = data.rules?.roll?.defaultAdvantageDice ? Number.parseInt(data.rules.roll.defaultAdvantageDice) : 6 + const advantageFaces = data.rules?.roll?.advantageFaces ? Number.parseInt(data.rules.roll.advantageFaces) : 6 this.advantageFaces = Number.isNaN(advantageFaces) ? 6 : advantageFaces; } @@ -137,11 +137,11 @@ export default class DualityRoll extends D20Roll { } this.terms[0] = new game.system.api.dice.diceTypes.HopeDie({ - faces: this.data.rules.dualityRoll?.defaultHopeDice ?? 12 + faces: this.data.rules.roll?.hopeFaces ?? 12 }); this.terms[1] = new foundry.dice.terms.OperatorTerm({ operator: '+' }); this.terms[2] = new game.system.api.dice.diceTypes.FearDie({ - faces: this.data.rules.dualityRoll?.defaultFearDice ?? 12 + faces: this.data.rules.roll?.fearFaces ?? 12 }); } diff --git a/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json b/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json index e2f58709..73c129d4 100644 --- a/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json +++ b/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json @@ -357,8 +357,8 @@ }, "changes": [ { - "key": "system.rules.dualityRoll.defaultHopeDice", - "value": "d8", + "key": "system.rules.roll.hopeIndex", + "value": 2, "priority": null, "type": "override" } diff --git a/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json b/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json index 201b17fd..005ae1ec 100644 --- a/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json +++ b/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json @@ -320,8 +320,8 @@ }, "changes": [ { - "key": "system.rules.dualityRoll.defaultFearDice", - "value": "d20", + "key": "system.rules.roll.fearIndex", + "value": 5, "priority": null, "type": "override" } diff --git a/src/packs/environments/environment_Cult_Ritual_QAXXiOKBDmCTauHD.json b/src/packs/environments/environment_Cult_Ritual_QAXXiOKBDmCTauHD.json index 1295db59..478a19de 100644 --- a/src/packs/environments/environment_Cult_Ritual_QAXXiOKBDmCTauHD.json +++ b/src/packs/environments/environment_Cult_Ritual_QAXXiOKBDmCTauHD.json @@ -192,9 +192,9 @@ }, "changes": [ { - "key": "system.rules.dualityRoll.defaultHopeDice", + "key": "system.rules.roll.hopeIndex", "mode": 5, - "value": "d10", + "value": 3, "priority": null } ], diff --git a/templates/dialogs/levelupOptionsDialog/parts/tier.hbs b/templates/dialogs/levelupOptionsDialog/parts/tier.hbs index 906e3407..f8f4cbab 100644 --- a/templates/dialogs/levelupOptionsDialog/parts/tier.hbs +++ b/templates/dialogs/levelupOptionsDialog/parts/tier.hbs @@ -5,18 +5,21 @@ - {{#with (lookup item.system.levelupOptionTiers tab.tier)}} + {{#with (lookup levelupOptionTiers tab.tier)}} {{#unless (empty this)}} {{#each this as |option key|}}
- {{formGroup @root.fields.label value=option.label name=(concat "system.levelOptionTiers." ../../tab.tier "." key ".label") localize=true }} - {{formGroup @root.fields.type value=option.type name=(concat "system.levelOptionTiers." ../../tab.tier "." key ".type") localize=true }} -
- {{formGroup @root.fields.checkboxSelections value=option.checkboxSelections name=(concat "system.levelOptionTiers." ../../tab.tier "." key ".checkboxSelections") localize=true }} - {{formGroup @root.fields.minCost value=option.minCost name=(concat "system.levelOptionTiers." ../../tab.tier "." key ".minCost") localize=true }} + {{formGroup @root.fields.label value=option.label name=(concat "system.levelupOptionTiers." ../../tab.tier "." key ".label") localize=true }} + {{formGroup @root.fields.type value=option.type name=(concat "system.levelupOptionTiers." ../../tab.tier "." key ".type") localize=true }} +
+ {{formGroup @root.fields.checkboxSelections value=option.checkboxSelections name=(concat "system.levelupOptionTiers." ../../tab.tier "." key ".checkboxSelections") localize=true }} + {{formGroup @root.fields.minCost value=option.minCost name=(concat "system.levelupOptionTiers." ../../tab.tier "." key ".minCost") localize=true }} + {{#if option.typeData}} + {{formGroup @root.fields.subType value=option.subType name=(concat "system.levelupOptionTiers." ../../tab.tier "." key ".subType") choices=option.typeData.subTypes localize=true }} + {{/if}}
diff --git a/templates/levelup/tabs/advancements.hbs b/templates/levelup/tabs/advancements.hbs index 5e65be34..f1b197b5 100644 --- a/templates/levelup/tabs/advancements.hbs +++ b/templates/levelup/tabs/advancements.hbs @@ -19,12 +19,13 @@ type="checkbox" class="selection-checkbox{{#if (gt this.cost 1)}} multi{{/if}}" {{checked this.selected}} {{#if this.disabled}}disabled{{/if}} data-tier="{{this.tier}}" data-level="{{this.level}}" - data-option="{{this.type}}" + data-option="{{this.option}}" data-checkbox-nr="{{this.checkboxNr}}" data-cost="{{this.minCost}}" data-amount="{{this.amount}}" data-value="{{this.value}}" data-type="{{this.type}}" + data-sub-type="{{this.subType}}" /> {{/each}}