Skip to content
4 changes: 2 additions & 2 deletions dnd5e.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ Hooks.once("init", function() {
if ( !game.settings.get("dnd5e", "sanityScore") ) delete DND5E.abilities.san;

// Legacy rules.
if ( game.settings.get("dnd5e", "rulesVersion") === "legacy" ) applyLegacyRules();
if ( dnd5e.settings.rulesVersion === "legacy" ) applyLegacyRules();

// Register system
DND5E.SPELL_LISTS.forEach(uuid => dnd5e.registry.spellLists.register(uuid));
Expand Down Expand Up @@ -483,7 +483,7 @@ Hooks.once("i18nInit", () => {
// Set up status effects. Explicitly performed after init and before prelocalization.
_configureStatusEffects();

if ( game.settings.get("dnd5e", "rulesVersion") === "legacy" ) {
if ( dnd5e.settings.rulesVersion === "legacy" ) {
const { translations, _fallback } = game.i18n;
foundry.utils.mergeObject(translations, {
"TYPES.Item": {
Expand Down
2 changes: 1 addition & 1 deletion module/applications/actor/api/base-actor-sheet.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export default class BaseActorSheet extends PrimarySheetMixin(
limited: this.actor.limited,
modernRules: this.actor.system.source?.rules
? this.actor.system.source.rules === "2024"
: game.settings.get("dnd5e", "rulesVersion") === "modern",
: dnd5e.settings.rulesVersion === "modern",
rollableClass: this.isEditable ? "rollable" : "",
sidebarCollapsed: !!game.user.getFlag("dnd5e", this._sidebarCollapsedKeyPath),
system: this.actor.system,
Expand Down
2 changes: 1 addition & 1 deletion module/applications/actor/npc-sheet.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ export default class NPCActorSheet extends BaseActorSheet {
// Visibility
if ( this._mode === this.constructor.MODES.PLAY ) {
context.showDeathSaves = context.important && !context.system.attributes.hp.value;
context.showInitiativeScore = game.settings.get("dnd5e", "rulesVersion") === "modern";
context.showInitiativeScore = dnd5e.settings.rulesVersion === "modern";
}
context.showLoyalty = context.important && game.settings.get("dnd5e", "loyaltyScore") && game.user.isGM;
context.showRests = game.user.isGM || (this.actor.isOwner && game.settings.get("dnd5e", "allowRests"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export default class AbilityScoreImprovementFlow extends AdvancementFlow {
};
}

const modernRules = game.settings.get("dnd5e", "rulesVersion") === "modern";
const modernRules = dnd5e.settings.rulesVersion === "modern";
const pluralRules = new Intl.PluralRules(game.i18n.lang);
return foundry.utils.mergeObject(super.getData(), {
abilities, lockImprovement, points, recommendation,
Expand Down
2 changes: 1 addition & 1 deletion module/applications/item/item-sheet.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ export default class ItemSheet5e extends PrimarySheetMixin(DocumentSheet5e) {

// If using modern rules, do not show redundant artificer progression unless it is already selected.
context.spellProgression = { ...CONFIG.DND5E.spellProgression };
if ( (game.settings.get("dnd5e", "rulesVersion") === "modern")
if ( (dnd5e.settings.rulesVersion === "modern")
&& (this.item.system.spellcasting?.progression !== "artificer") ) delete context.spellProgression.artificer;
context.spellProgression = Object.entries(context.spellProgression).map(([value, config]) => {
const group = CONFIG.DND5E.spellcasting[config.type]?.label ?? "";
Expand Down
2 changes: 1 addition & 1 deletion module/applications/settings/variant-rules-settings.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default class VariantRulesSettingsConfig extends BaseSettingsConfig {
switch ( partId ) {
case "general":
context.fields = [
game.settings.get("dnd5e", "rulesVersion") === "legacy" ? this.createSettingField("allowFeats") : null,
dnd5e.settings.rulesVersion === "legacy" ? this.createSettingField("allowFeats") : null,
this.createSettingField("restVariant"),
this.createSettingField("proficiencyModifier"),
this.createSettingField("levelingMode")
Expand Down
4 changes: 2 additions & 2 deletions module/canvas/layers/tokens.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default class TokenLayer5e extends foundry.canvas.layers.TokenLayer {
*/
isOccupiedGridSpaceBlocking(gridSpace, token, { preview=false }={}) {
const tokenSize = CONFIG.DND5E.actorSizes[token.actor?.system.traits.size]?.numerical ?? 2;
const modernRules = game.settings.get("dnd5e", "rulesVersion") === "modern";
const modernRules = dnd5e.settings.rulesVersion === "modern";
const halflingNimbleness = token.actor?.getFlag("dnd5e", "halflingNimbleness");
const found = this.#getRelevantOccupyingTokens(gridSpace, token, { preview }).filter(t => {
// Only creatures block movement.
Expand Down Expand Up @@ -60,7 +60,7 @@ export default class TokenLayer5e extends foundry.canvas.layers.TokenLayer {
* @returns {boolean} Whether the moving token should suffer difficult terrain
*/
isOccupiedGridSpaceDifficult(gridSpace, token, { preview=false }={}) {
const modernRules = game.settings.get("dnd5e", "rulesVersion") === "modern";
const modernRules = dnd5e.settings.rulesVersion === "modern";
const found = this.#getRelevantOccupyingTokens(gridSpace, token, { preview }).filter(t => {
// Only consider creatures as difficult terrain for now.
if ( !t.actor?.system.isCreature ) return false;
Expand Down
15 changes: 8 additions & 7 deletions module/data/abstract/system-data-model.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -136,22 +136,23 @@ export default class SystemDataModel extends foundry.abstract.TypeDataModel {
/* -------------------------------------------- */

/** @inheritDoc */
static cleanData(source, options) {
this._cleanData(source, options);
return super.cleanData(source, options);
static cleanData(source, options, _state) {
this._cleanData(source, options, _state);
return super.cleanData(source, options, _state);
}

/* -------------------------------------------- */

/**
* Performs cleaning without calling DataModel.cleanData.
* @param {object} [source] The source data
* @param {object} [options={}] Additional options (see DataModel.cleanData)
* @param {object} [source] The source data
* @param {object} [options={}] Additional options (see DataModel.cleanData)
* @param {Partial<DataFieldCleaningState>} [_state] Internal options used during cleaning recursion
* @protected
*/
static _cleanData(source, options) {
static _cleanData(source, options, _state) {
for ( const template of this._schemaTemplates ) {
template._cleanData(source, options);
template._cleanData(source, options, _state);
}
}

Expand Down
2 changes: 1 addition & 1 deletion module/data/activity/attack-data.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export default class BaseAttackActivityData extends BaseActivityData {
}
const actionType = this.getActionType(attackMode);
let actionTypeLabel = game.i18n.localize(`DND5E.Action${actionType.toUpperCase()}`);
const isLegacy = game.settings.get("dnd5e", "rulesVersion") === "legacy";
const isLegacy = dnd5e.settings.rulesVersion === "legacy";
const isUnarmed = this.attack.type.classification === "unarmed";
if ( isUnarmed ) attackModeLabel = game.i18n.localize("DND5E.ATTACK.Classification.Unarmed");
const isSpell = (actionType === "rsak") || (actionType === "msak");
Expand Down
4 changes: 2 additions & 2 deletions module/data/actor/npc.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ export default class NPCData extends CreatureTemplate {
if ( legres.max && legendaryResistanceItem ) {
const max = this._source.resources.legres.max;
const modernRules = (this.source?.rules
|| (game.settings.get("dnd5e", "rulesVersion") === "modern" ? "2024" : "2014")) === "2024";
|| (dnd5e.settings.rulesVersion === "modern" ? "2024" : "2014")) === "2024";
legendaryResistanceItem.system.uses.label = this.resources.lair.value && modernRules ? game.i18n.format(
"DND5E.LegendaryResistance.LairUses", { normal: formatNumber(max), lair: formatNumber(max + 1) }
) : `${formatNumber(max)}/${CONFIG.DND5E.limitedUsePeriods.day?.label ?? ""}`;
Expand Down Expand Up @@ -482,7 +482,7 @@ export default class NPCData extends CreatureTemplate {
if ( !max ) return "";
const pr = getPluralRules().select(max);
const rulesVersion = this.source?.rules
|| (game.settings.get("dnd5e", "rulesVersion") === "modern" ? "2024" : "2014");
|| (dnd5e.settings.rulesVersion === "modern" ? "2024" : "2014");
return game.i18n.format(`DND5E.LegendaryAction.Description${rulesVersion === "2014" ? "Legacy" : ""}`, {
name: name.toLowerCase(),
uses: this.resources.lair.value ? game.i18n.format("DND5E.LegendaryAction.LairUses", {
Expand Down
7 changes: 4 additions & 3 deletions module/data/actor/templates/attributes.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export default class AttributesFields {
bonuses: new SchemaField({
save: new FormulaField({ required: true, label: "DND5E.ConcentrationBonus" })
}),
limit: new NumberField({ integer: true, min: 0, initial: 1, label: "DND5E.ConcentrationLimit" })
limit: new FormulaField({ deterministic: true, initial: "1", label: "DND5E.ConcentrationLimit" })
}, { label: "DND5E.Concentration" }),
loyalty: new SchemaField({
value: new NumberField({ integer: true, min: 0, max: 20, label: "DND5E.Loyalty" })
Expand Down Expand Up @@ -246,6 +246,7 @@ export default class AttributesFields {
const ability = this.abilities?.[abilityId] || {};
const bonus = simplifyBonus(concentration.bonuses.save, rollData);
concentration.save = (ability.save?.value ?? 0) + bonus;
concentration.limit = Math.max(0, dnd5e.utils.simplifyBonus(concentration.limit || "1", rollData));
}

/* -------------------------------------------- */
Expand Down Expand Up @@ -379,7 +380,7 @@ export default class AttributesFields {
init.mod = ability.mod ?? 0;

// Initiative proficiency
const isLegacy = game.settings.get("dnd5e", "rulesVersion") === "legacy";
const isLegacy = dnd5e.settings.rulesVersion === "legacy";
const prof = this.attributes.prof ?? 0;
const joat = flags.jackOfAllTrades && isLegacy;
const ra = this.parent._isRemarkableAthlete(abilityId);
Expand Down Expand Up @@ -426,7 +427,7 @@ export default class AttributesFields {
const heavilyEncumbered = statuses.has("heavilyEncumbered");
const exceedingCarryingCapacity = statuses.has("exceedingCarryingCapacity");
const units = this.attributes.movement.units ??= defaultUnits("length");
let reduction = game.settings.get("dnd5e", "rulesVersion") === "modern"
let reduction = dnd5e.settings.rulesVersion === "modern"
? (this.attributes.exhaustion ?? 0) * (CONFIG.DND5E.conditionTypes.exhaustion?.reduction?.speed ?? 0) : 0;
reduction = convertLength(reduction, CONFIG.DND5E.defaultUnits.length.imperial, units);
const bonus = simplifyBonus(this.attributes.movement.bonus, rollData);
Expand Down
4 changes: 2 additions & 2 deletions module/data/advancement/scale-value.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ export class ScaleValueConfigurationData extends foundry.abstract.DataModel {
*/
export class ScaleValueEntryField extends foundry.data.fields.ObjectField {
/** @override */
_cleanType(value, options) {
_cleanType(value, options, _state) {
if ( !(typeof value === "object") ) value = {};

// Use a defined DataModel
const cls = TYPES[options.source?.type];
if ( cls ) return cls.cleanData(value, options);
if ( cls ) return cls.cleanData(value, options, _state);

return value;
}
Expand Down
4 changes: 2 additions & 2 deletions module/data/advancement/trait.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export class TraitConfigurationData extends foundry.abstract.DataModel {
/** @inheritDoc */
static migrateData(source) {
super.migrateData(source);
const version = game.settings.get("dnd5e", "rulesVersion");
const version = dnd5e.settings.rulesVersion;
const languageMap = LANGUAGE_MAP[version] ?? {};
if ( source.grants?.length ) source.grants = source.grants.map(t => languageMap[t] ?? t);
if ( source.choices?.length ) source.choices.forEach(c => c.pool = c.pool.map(t => languageMap[t] ?? t));
Expand All @@ -76,7 +76,7 @@ export class TraitValueData extends foundry.abstract.DataModel {
/** @inheritDoc */
static migrateData(source) {
super.migrateData(source);
const version = game.settings.get("dnd5e", "rulesVersion");
const version = dnd5e.settings.rulesVersion;
const languageMap = LANGUAGE_MAP[version] ?? {};
if ( source.chosen?.length ) source.chosen = source.chosen.map(t => languageMap[t] ?? t);
return source;
Expand Down
4 changes: 2 additions & 2 deletions module/data/fields/activities-field.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ export class ActivityField extends foundry.data.fields.ObjectField {
/* -------------------------------------------- */

/** @override */
_cleanType(value, options) {
_cleanType(value, options, _state) {
if ( !(typeof value === "object") ) value = {};

const cls = this.getModel(value);
if ( cls ) return cls.cleanData(value, options);
if ( cls ) return cls.cleanData(value, options, _state);
return value;
}

Expand Down
4 changes: 2 additions & 2 deletions module/data/fields/advancement-data-field.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ export default class AdvancementDataField extends foundry.data.fields.ObjectFiel
/* -------------------------------------------- */

/** @inheritDoc */
_cleanType(value, options) {
_cleanType(value, options, _state) {
if ( !(typeof value === "object") ) value = {};

// Use a defined DataModel
const cls = this.getModel();
if ( cls ) return cls.cleanData(value, options);
if ( cls ) return cls.cleanData(value, options, _state);
if ( options.partial ) return value;

// Use the defined defaults
Expand Down
4 changes: 2 additions & 2 deletions module/data/fields/advancement-field.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ export default class AdvancementField extends foundry.data.fields.ObjectField {
/* -------------------------------------------- */

/** @inheritDoc */
_cleanType(value, options) {
_cleanType(value, options, _state) {
if ( !(typeof value === "object") ) value = {};

const cls = this.getModelForType(value.type);
if ( cls ) return cls.cleanData(value, options);
if ( cls ) return cls.cleanData(value, options, _state);
return value;
}

Expand Down
15 changes: 7 additions & 8 deletions module/data/fields/mapping-field.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ export default class MappingField extends foundry.data.fields.ObjectField {
/* -------------------------------------------- */

/** @inheritDoc */
_cleanType(value, options) {
_cleanType(value, options, _state) {
Object.entries(value).forEach(([k, v]) => {
if ( k.startsWith("-=") ) return;
value[k] = this.model.clean(v, options);
value[k] = this.model.clean(v, options, _state);
});
return value;
}
Expand Down Expand Up @@ -100,9 +100,7 @@ export default class MappingField extends foundry.data.fields.ObjectField {
*/
_validateValues(value, { phase: _phase, ...options }={}) {
const isV13 = game.release.generation < 14;
const failure = isV13
? new foundry.data.validation.DataModelValidationFailure()
: new foundry.data.validation.DataModelValidationError();
const failure = new foundry.data.validation.DataModelValidationFailure();
for ( const [k, v] of Object.entries(value) ) {
if ( k.startsWith("-=") ) continue;
const error = this.model.validate(v, { ...options, strict: false, recursive: true });
Expand Down Expand Up @@ -133,11 +131,12 @@ export default class MappingField extends foundry.data.fields.ObjectField {
/* -------------------------------------------- */

/** @inheritDoc */
_getField(path) {
_getField(path, options) {
if ( path.length === 0 ) return this;
else if ( path.length === 1 ) return this.model;
path.shift();
return this.model._getField(path);
if ( game.release.generation < 14 ) path.shift();
else path.pop();
return this.model._getField(path, options);
}

/* -------------------------------------------- */
Expand Down
2 changes: 1 addition & 1 deletion module/data/item/background.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default class BackgroundData extends ItemDataModel.mixin(

/** @override */
_advancementToCreate(options) {
if ( game.settings.get("dnd5e", "rulesVersion") === "legacy" ) return [
if ( dnd5e.settings.rulesVersion === "legacy" ) return [
{ type: "Trait", title: game.i18n.localize("DND5E.ADVANCEMENT.Defaults.BackgroundProficiencies") },
{ type: "ItemGrant", title: game.i18n.localize("DND5E.ADVANCEMENT.Defaults.BackgroundFeature") }
];
Expand Down
2 changes: 1 addition & 1 deletion module/data/item/race.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export default class RaceData extends ItemDataModel.mixin(AdvancementTemplate, I

/** @override */
_advancementToCreate(options) {
if ( game.settings.get("dnd5e", "rulesVersion") === "legacy" ) return [
if ( dnd5e.settings.rulesVersion === "legacy" ) return [
{ type: "AbilityScoreImprovement" },
{ type: "Size" },
{ type: "Trait", configuration: { grants: ["languages:standard:common"] } }
Expand Down
4 changes: 2 additions & 2 deletions module/data/item/templates/starting-equipment.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export default class StartingEquipmentTemplate extends SystemDataModel {

// For modern classes, display as "Choose A or B"
modernStyle ??= (this.source.rules === "2024")
|| (!this.source.rules && (game.settings.get("dnd5e", "rulesVersion") === "modern"));
|| (!this.source.rules && (dnd5e.settings.rulesVersion === "modern"));
if ( modernStyle ) {
const entries = topLevel[0].type === "OR" ? topLevel[0].children : topLevel;
if ( this.wealth ) entries.push(new EquipmentEntryData({ type: "currency", key: "gp", count: this.wealth }));
Expand Down Expand Up @@ -249,7 +249,7 @@ export class EquipmentEntryData extends foundry.abstract.DataModel {
generateLabel({ depth=1, modernStyle }={}) {
let label;
modernStyle ??= (this.parent.source?.rules === "2024")
|| (!this.parent.source?.rules && (game.settings.get("dnd5e", "rulesVersion") === "modern"));
|| (!this.parent.source?.rules && (dnd5e.settings.rulesVersion === "modern"));

switch ( this.type ) {
// For AND/OR, use a simple conjunction/disjunction list (e.g. "first, second, and third")
Expand Down
2 changes: 1 addition & 1 deletion module/data/shared/source-field.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default class SourceField extends SchemaField {
license: new StringField(),
revision: new NumberField({ initial: 1 }),
rules: new StringField({
initial: () => game.settings.get("dnd5e", "rulesVersion") === "modern" ? "2024" : "2014"
initial: () => dnd5e.settings.rulesVersion === "modern" ? "2024" : "2014"
}),
...fields
};
Expand Down
Loading