Skip to content

Commit

Permalink
Add roll mode to abilities and remove attributes.death.ability. M…
Browse files Browse the repository at this point in the history
…ove derived value `abilities.<x>.save` to `abilities.<x>.save.value`.
  • Loading branch information
krbz999 committed Dec 25, 2024
1 parent b072d40 commit 0744b69
Show file tree
Hide file tree
Showing 9 changed files with 41 additions and 11 deletions.
1 change: 1 addition & 0 deletions module/data/actor/character.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export default class CharacterData extends CreatureTemplate {
})
}, { label: "DND5E.HitPoints" }),
death: new RollConfigField({
ability: false,
success: new NumberField({
required: true, nullable: false, integer: true, min: 0, initial: 0, label: "DND5E.DeathSaveSuccesses"
}),
Expand Down
1 change: 1 addition & 0 deletions module/data/actor/npc.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export default class NPCData extends CreatureTemplate {
formula: new FormulaField({required: true, label: "DND5E.HPFormula"})
}, {label: "DND5E.HitPoints"}),
death: new RollConfigField({
ability: false,
success: new NumberField({
required: true, nullable: false, integer: true, min: 0, initial: 0, label: "DND5E.DeathSaveSuccesses"
}),
Expand Down
2 changes: 1 addition & 1 deletion module/data/actor/templates/attributes.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export default class AttributesFields {
const abilityId = concentration.ability || CONFIG.DND5E.defaultAbilities.concentration;
const ability = this.abilities?.[abilityId] || {};
const bonus = simplifyBonus(concentration.bonuses.save, rollData);
concentration.save = (ability.save ?? 0) + bonus;
concentration.save = (ability.save?.value ?? 0) + bonus;
}

/* -------------------------------------------- */
Expand Down
30 changes: 26 additions & 4 deletions module/data/actor/templates/common.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ActorDataModel } from "../../abstract.mjs";
import FormulaField from "../../fields/formula-field.mjs";
import MappingField from "../../fields/mapping-field.mjs";
import CurrencyTemplate from "../../shared/currency.mjs";
import RollConfigField from "../../shared/roll-config-field.mjs";

const { NumberField, SchemaField } = foundry.data.fields;

Expand All @@ -15,6 +16,16 @@ const { NumberField, SchemaField } = foundry.data.fields;
* @property {object} bonuses Bonuses that modify ability checks and saves.
* @property {string} bonuses.check Numeric or dice bonus to ability checks.
* @property {string} bonuses.save Numeric or dice bonus to ability saving throws.
* @property {object} check Properties related to ability checks.
* @property {object} check.roll
* @property {number} check.roll.mode The advantage mode of ability checks.
* @property {number} check.roll.min The minimum that can be rolled on the d20.
* @property {number} check.roll.max The maximum that can be rolled on the d20.
* @property {object} save Properties related to ability saving throws.
* @property {object} save.roll
* @property {number} save.roll.mode The advantage mode of ability saving throws.
* @property {number} save.roll.min The minimum that can be rolled on the d20.
* @property {number} save.roll.max The maximum that can be rolled on the d20.
*/

/**
Expand All @@ -41,7 +52,10 @@ export default class CommonTemplate extends ActorDataModel.mixin(CurrencyTemplat
bonuses: new SchemaField({
check: new FormulaField({ required: true, label: "DND5E.AbilityCheckBonus" }),
save: new FormulaField({ required: true, label: "DND5E.SaveBonus" })
}, { label: "DND5E.AbilityBonuses" })
}, { label: "DND5E.AbilityBonuses" }),
save: new RollConfigField({
ability: false
})
}), {
initialKeys: CONFIG.DND5E.abilities, initialValue: this._initialAbilityValue.bind(this),
initialKeysOnly: true, label: "DND5E.Abilities"
Expand Down Expand Up @@ -147,14 +161,22 @@ export default class CommonTemplate extends ActorDataModel.mixin(CurrencyTemplat
const checkBonusAbl = simplifyBonus(abl.bonuses?.check, rollData);
abl.checkBonus = checkBonusAbl + checkBonus;

abl.save = abl.mod + abl.saveBonus;
if ( Number.isNumeric(abl.saveProf.term) ) abl.save += abl.saveProf.flat;
abl.save.value = abl.mod + abl.saveBonus;
if ( Number.isNumeric(abl.saveProf.term) ) abl.save.value += abl.saveProf.flat;
abl.dc = 8 + abl.mod + prof + dcBonus;

if ( !Number.isFinite(abl.max) ) abl.max = CONFIG.DND5E.maxAbilityScore;

// If we merged saves when transforming, take the highest bonus here.
if ( originalSaves && abl.proficient ) abl.save = Math.max(abl.save, originalSaves[id].save);
if ( originalSaves && abl.proficient ) abl.save.value = Math.max(abl.save, originalSaves[id].save.value);

// Deprecations.
abl.save.toString = function() {
foundry.utils.logCompatibilityWarning("The 'abilities.<ability>.save' property is now stored in 'abilities.<ability>.save.value'.", {
since: "4.2", until: "4.5"
});
return abl.save.value;
};
}
}

Expand Down
10 changes: 8 additions & 2 deletions module/data/shared/roll-config-field.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const { StringField, NumberField, SchemaField } = foundry.data.fields;

/**
* @typedef {object} RollConfigData
* @property {string} ability Default ability associated with this roll.
* @property {string} [ability] Default ability associated with this roll.
* @property {object} roll
* @property {number} roll.min Minimum number on the die rolled.
* @property {number} roll.max Maximum number on the die rolled.
Expand All @@ -18,7 +18,6 @@ export default class RollConfigField extends foundry.data.fields.SchemaField {
constructor({roll={}, ability="", ...fields}={}, options={}) {
const opts = { initial: null, nullable: true, min: 1, max: 20, integer: true };
fields = {
ability: new StringField({required: true, initial: ability, label: "DND5E.AbilityModifier"}),
roll: new SchemaField({
min: new NumberField({...opts, label: "DND5E.ROLL.Range.Minimum"}),
max: new NumberField({...opts, label: "DND5E.ROLL.Range.Maximum"}),
Expand All @@ -27,6 +26,13 @@ export default class RollConfigField extends foundry.data.fields.SchemaField {
}),
...fields
};
if ( ability !== false ) {
fields.ability = new StringField({
required: true,
initial: ability,
label: "DND5E.AbilityModifier"
});
}
super(fields, options);
}
}
2 changes: 1 addition & 1 deletion templates/actors/character-sheet.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@
{{{ability.icon}}}
</a>
<span class="ability-save" data-tooltip="DND5E.SavingThrow">
{{numberFormat ability.save decimals=0 sign=true}}
{{numberFormat ability.save.value decimals=0 sign=true}}
</span>
</div>
<a class="config-button" data-action="ability"
Expand Down
2 changes: 1 addition & 1 deletion templates/actors/npc-sheet-2.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@
data-tooltip="{{ hover }}" aria-label="{{ localize hover }}"
value="{{#if @root.editable}}{{ baseProf }}{{else}}{{ proficient }}{{/if}}"
{{ disabled (not @root.editable) }}></proficiency-cycle>
<span class="save">{{ dnd5e-formatModifier save }}</span>
<span class="save">{{ dnd5e-formatModifier save.value }}</span>
<i class="fas fa-shield-heart" inert></i>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion templates/actors/npc-sheet.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
{{{ability.icon}}}
</a>
<span class="ability-save" data-tooltip="DND5E.SavingThrow">
{{numberFormat ability.save decimals=0 sign=true}}
{{numberFormat ability.save.value decimals=0 sign=true}}
</span>
</div>
<a class="config-button" data-action="ability"
Expand Down
2 changes: 1 addition & 1 deletion templates/actors/tabs/character-details.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
{{/if}}
<a class="name saving-throw full {{ @root.rollableClass }}">{{ label }}</a>
<a class="name saving-throw abbr {{ @root.rollableClass }}">{{ abbr }}</a>
<div class="bonus">{{ dnd5e-formatModifier save }}</div>
<div class="bonus">{{ dnd5e-formatModifier save.value }}</div>
{{#if @root.editable}}
<a class="config-button" data-action="ability" data-tooltip="{{ config }}" aria-label="{{ localize config }}">
<i class="fas fa-cog"></i>
Expand Down

0 comments on commit 0744b69

Please sign in to comment.