Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add advantage modes to abilities objects #4922

Merged
merged 4 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
21 changes: 17 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,8 @@ 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 {RollConfigFieldData} check Properties related to ability checks.
* @property {RollConfigFieldData} save Properties related to ability saving throws.
*/

/**
Expand All @@ -41,7 +44,9 @@ 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" }),
check: new RollConfigField({ ability: false }),
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 +152,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
12 changes: 9 additions & 3 deletions module/data/shared/roll-config-field.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import AdvantageModeField from "../fields/advantage-mode-field.mjs";
const { StringField, NumberField, SchemaField } = foundry.data.fields;

/**
* @typedef {object} RollConfigData
* @property {string} ability Default ability associated with this roll.
* @typedef {object} RollConfigFieldData
arbron marked this conversation as resolved.
Show resolved Hide resolved
* @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"
});
}
arbron marked this conversation as resolved.
Show resolved Hide resolved
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