Set up DhLevelTier datamodel

This commit is contained in:
WBHarry 2025-05-27 13:43:45 +02:00
parent 63274d67ce
commit 0e0507fe6f
8 changed files with 462 additions and 110 deletions

View file

@ -301,39 +301,43 @@
}
},
"LevelUp": {
"Tier1": {
"Label": "Level 2-4",
"InfoLabel": "At Level 2, take an additional Experience.",
"Pretext": "When you level up, record it on your character sheet, then choose two available options from the list below and mark them.",
"Posttext": "Then increase your Severe Damage Threshold by +2 and choose a new Domain Deck card at your Level or lower."
"Options": {
"trait": "Gain a +1 bonus to two unmarked character traits and mark them.",
"hitPoint": "Permanently gain one Hit Point slot.",
"stress": "Permanently gain one Stress slot.",
"experience": "Permanently gain a +1 bonus to two experiences.",
"domainCard": "Choose an additional domain card of your level or lower from a domain you have access to (up to level {maxLevel})",
"evasion": "Permanently gain a +1 bonus to your Evasion.",
"subclass": "Take an upgraded subclass card. Then cross out the multiclass option for this tier.",
"proficiency": "Increase your Proficiency by +1.",
"multiclass": "Multiclass: Choose an additional class for your character, then cross out an unused “Take an upgraded subclass card” and the other multiclass option on this sheet."
},
"Tier2": {
"Label": "Level 5-7",
"InfoLabel": "At Level 5, take an additional Experience and clear all marks on Character Traits.",
"Pretext": "When you level up, record it on your character sheet, then choose two from the list below or any unmarked from the previous tier.",
"Posttext": "Then, increase your Damage Thresholds: Major by +1 and Severe by +3. Then choose a new Domain Deck card at your Level or lower. If your loadout is full, you may choose a card to swap."
"Label": "Levels 2-4",
"InfoLabel": "At Level 2, gain an additional Experience at +2 and gain a +1 bonus to your Proficiency.",
"Pretext": "Choose two options from the list below",
"Posttext": "Take an additional domain card of your level or lower from a domain you have access to."
},
"Tier3": {
"Label": "Level 8-10",
"Label": "Levels 5-7",
"InfoLabel": "At Level 5, take an additional Experience and clear all marks on Character Traits.",
"Pretext": "When you level up, record it on your character sheet, then choose two from the list below or any unmarked from the previous tier.",
"Posttext": "Take an additional domain card of your level or lower from a domain you have access to."
},
"Tier4": {
"Label": "Levels 8-10",
"InfoLabel": "At Level 8, take an additional Experience and clear all marks on Character Traits.",
"Pretext": "When you level up, record it on your character sheet, then choose two from the list below or any unmarked from the previous tier.",
"Posttext": "Then, increase your Damage Thresholds: Minor by +1, Major by +2, and Severe by +4. Then choose a new Domain Deck card at your Level or lower. If your loadout is full, you may choose a card to swap."
"Posttext": "Take an additional domain card of your level or lower from a domain you have access to."
},
"ChoiceDescriptions": {
"Attributes": "Increase two unmarked Character Traits by +1 and mark them.",
"HitPointSlots": "Permanently add one Hit Point Slot.",
"StressSlots": "Permanently add one Stress Slot.",
"Experiences": "Increase two Experiences by +1.",
"Proficiency": "Increase your Proficiency by +1",
"ArmorOrEvasionSlot": "Permanently add one Armor Slot or take +1 to your Evasion.",
"MajorDamageThreshold2": "Increase your Major Damage Threshold by +2.",
"SevereDamageThreshold2": "Increase your Severe Damage Threshold by +2.",
"MinorDamageThreshold2": "Increase your Minor Damage Threshold by +2.",
"SevereDamageThreshold3": "Increase your Severe Damage Threshold by +3.",
"Major2OrSevere4DamageThreshold": "Increase your Major Damage Threshold by +2 or Severe Damage Threshold by +4",
"Minor1OrMajor1DamageThreshold": "Increase your Minor or Major Damage Threshold by +1.",
"SevereDamageThreshold4": "Increase your Severe Damage Threshold by +4.",
"MajorDamageThreshold1": "Increase your Major Damage Threshold by +1.",
"Attributes": "Gain a +1 bonus to two unmarked character traits and mark them.",
"HitPointSlots": "Permanently gain one Hit Point slot.",
"StressSlots": "Permanently gain one Stress slot.",
"Experiences": "Permanently gain a +1 bonus to two experiences.",
"DomainCard": "Choose an additional domain card of your level or lower from a domain you have access to (up to level {maxLevel})",
"Evasion": "Permanently gain a +1 bonus to your Evasion.",
"Proficiency": "Increase your Proficiency by +1.",
"Subclass": "Take an upgraded subclass card. Then cross out the multiclass option for this tier.",
"Multiclass": "Multiclass: Choose an additional class for your character, then cross out an unused “Take an upgraded subclass card” and the other multiclass option on this sheet."
}

View file

@ -1,4 +1,5 @@
import { DualityRollColor } from '../config/settingsConfig.mjs';
import { defaultLevelTiers, DhLevelTiers } from '../data/levelTier.mjs';
class DhpAutomationSettings extends FormApplication {
constructor(object = {}, options = {}) {
@ -225,6 +226,23 @@ export const registerDHPSettings = () => {
default: DualityRollColor.colorful.value
});
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.DualityRollColor, {
name: game.i18n.localize('DAGGERHEART.Settings.DualityRollColor.Name'),
hint: game.i18n.localize('DAGGERHEART.Settings.DualityRollColor.Hint'),
scope: 'world',
config: true,
type: Number,
choices: Object.values(DualityRollColor),
default: DualityRollColor.colorful.value
});
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.LevelTiers, {
scope: 'world',
config: false,
type: DhLevelTiers,
default: defaultLevelTiers
});
game.settings.registerMenu(SYSTEM.id, SYSTEM.SETTINGS.menu.Automation.Name, {
name: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Name'),
label: game.i18n.localize('DAGGERHEART.Settings.Menu.Automation.Label'),

View file

@ -214,9 +214,9 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
}
: {};
context.attributes = Object.keys(this.document.system.attributes).reduce((acc, key) => {
context.attributes = Object.keys(this.document.system.traits).reduce((acc, key) => {
acc[key] = {
...this.document.system.attributes[key],
...this.document.system.traits[key],
name: game.i18n.localize(SYSTEM.ACTOR.abilities[key].name),
verbs: SYSTEM.ACTOR.abilities[key].verbs.map(x => game.i18n.localize(x))
};
@ -478,7 +478,7 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
}
async attributeChange(event) {
const path = `system.attributes.${event.currentTarget.dataset.attribute}.data.base`;
const path = `system.traits.${event.currentTarget.dataset.attribute}.data.base`;
await this.document.update({ [path]: event.currentTarget.value });
}
@ -524,14 +524,14 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
}
static async toggleAttributeMark(_, button) {
const attribute = this.document.system.attributes[button.dataset.attribute];
const attribute = this.document.system.traits[button.dataset.attribute];
const newMark = this.document.system.availableAttributeMarks
.filter(x => x > Math.max.apply(null, this.document.system.attributes[button.dataset.attribute].levelMarks))
.filter(x => x > Math.max.apply(null, this.document.system.traits[button.dataset.attribute].levelMarks))
.sort((a, b) => (a > b ? 1 : -1))[0];
if (attribute.levelMark || !newMark) return;
const path = `system.attributes.${button.dataset.attribute}.levelMarks`;
const path = `system.traits.${button.dataset.attribute}.levelMarks`;
await this.document.update({ [path]: [...attribute.levelMarks, newMark] });
}
@ -569,7 +569,7 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) {
type: weapon.system.damage.type,
bonusDamage: this.document.system.bonuses.damage
};
const modifier = this.document.system.attributes[weapon.system.trait].data.value;
const modifier = this.document.system.traits[weapon.system.trait].data.value;
const { roll, hope, fear, advantage, disadvantage, modifiers, bonusDamageString } =
await this.document.dualityRoll(

View file

@ -52,31 +52,31 @@ export const abilities = {
export const featureProperties = {
agility: {
name: 'DAGGERHEART.Abilities.Agility.Name',
path: actor => actor.system.attributes.agility.data.value
path: actor => actor.system.traits.agility.data.value
},
strength: {
name: 'DAGGERHEART.Abilities.Strength.Name',
path: actor => actor.system.attributes.strength.data.value
path: actor => actor.system.traits.strength.data.value
},
finesse: {
name: 'DAGGERHEART.Abilities.Finesse.Name',
path: actor => actor.system.attributes.finesse.data.value
path: actor => actor.system.traits.finesse.data.value
},
instinct: {
name: 'DAGGERHEART.Abilities.Instinct.Name',
path: actor => actor.system.attributes.instinct.data.value
path: actor => actor.system.traits.instinct.data.value
},
presence: {
name: 'DAGGERHEART.Abilities.Presence.Name',
path: actor => actor.system.attributes.presence.data.value
path: actor => actor.system.traits.presence.data.value
},
knowledge: {
name: 'DAGGERHEART.Abilities.Knowledge.Name',
path: actor => actor.system.attributes.knowledge.data.value
path: actor => actor.system.traits.knowledge.data.value
},
spellcastingTrait: {
name: 'DAGGERHEART.FeatureProperty.SpellcastingTrait',
path: actor => actor.system.attributes[actor.system.subclass.system.spellcastingTrait].data.value
path: actor => actor.system.traits[actor.system.subclass.system.spellcastingTrait].data.value
}
};

View file

@ -25,7 +25,8 @@ export const gameSettings = {
AbilityArray: 'AbilityArray',
RangeMeasurement: 'RangeMeasurement'
},
DualityRollColor: 'DualityRollColor'
DualityRollColor: 'DualityRollColor',
LevelTiers: 'LevelTiers'
};
export const DualityRollColor = {

371
module/data/levelTier.mjs Normal file
View file

@ -0,0 +1,371 @@
export class DhLevelTiers extends foundry.abstract.DataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
tiers: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelTier))
};
}
}
class DhLevelTier extends foundry.abstract.DataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
tier: new fields.NumberField({ required: true, integer: true }),
name: new fields.StringField({ required: true }),
levels: new fields.SchemaField({
start: new fields.NumberField({ required: true, integer: true }),
end: new fields.NumberField({ required: true, integer: true })
}),
initialAchievements: new fields.SchemaField({
experience: new fields.SchemaField({
nr: new fields.NumberField({ required: true, initial: 1 }),
modifier: new fields.NumberField({ required: true, initial: 2 })
}),
proficiency: new fields.NumberField({ integer: true, initial: 1 })
}),
availableOptions: new fields.NumberField({ required: true, initial: 2 }),
domainCardByLevel: new fields.NumberField({ initial: 1 }),
options: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelOption))
};
}
}
class DhLevelOption extends foundry.abstract.DataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
label: new fields.StringField({ required: true }),
checkboxQuantity: new fields.NumberField({ required: true, integer: true, initial: 1 }),
minCost: new fields.NumberField({ required: true, integer: true, initial: 1 }),
type: new fields.StringField({ required: true, choices: LevelOptionType }),
choice: new fields.StringField(),
value: new fields.NumberField({ integer: true }),
amount: new fields.NumberField({ integer: true })
};
}
}
// class DhLevelOptionType extends foundry.abstract.DataModel {
// static defineSchema(){
// return new fields.SchemaField({
// trait: new fields.SchemaField({
// id: new fields.StringField({ required: true }),
// label: new fields.StringField({ required: true }),
// }),
// attribute: new fields.SchemaField({
// id: new fields.StringField({ required: true }),
// label: new fields.StringField({ required: true }),
// choice: new fields.StringField({ required: true, choices: attributeChoices })
// }),
// experience: new fields.SchemaField({
// id: new fields.StringField({ required: true }),
// label: new fields.StringField({ required: true }),
// }),
// domainCard: new fields.SchemaField({
// id: new fields.StringField({ required: true }),
// label: new fields.StringField({ required: true }),
// }),
// subclass: new fields.SchemaField({
// id: new fields.StringField({ required: true }),
// label: new fields.StringField({ required: true }),
// }),
// });
// }
// }
const LevelOptionType = {
trait: {
id: 'trait',
label: 'Character Trait'
},
// attribute: {
// id: 'attribute',
// label: 'Attribute',
// choices: attributeChoices,
// },
hitPoint: {
id: 'hitPoint',
label: 'Hit Points'
},
stress: {
id: 'stress',
label: 'Stress'
},
evasion: {
id: 'evasion',
label: 'Evasion'
},
proficiency: {
id: 'proficiency',
label: 'Proficiency'
},
experience: {
id: 'experience',
label: 'Experience'
},
domainCard: {
id: 'domainCard',
label: 'Domain Card'
},
subclass: {
id: 'subclass',
label: 'Subclass'
},
multiclass: {
id: 'multiclass',
label: 'Multiclass'
}
};
// const attributeChoices = {
// hitPoint: {
// id: 'hitPoint',
// label: 'Hit Points',
// },
// stress: {
// id: 'stress',
// label: 'Stress',
// },
// evasion: {
// id: 'evasion',
// label: 'Evasion',
// },
// proficiency: {
// id: 'proficiency',
// label: 'Proficiency',
// },
// };
export const defaultLevelTiers = {
tiers: {
2: {
tier: 2,
name: 'Tier 2',
levels: {
start: 2,
end: 4
},
initialAchievements: {
experience: {
nr: 2,
modifier: 1
},
proficiency: 1
},
availableOptions: 2,
domainCardByLevel: 1,
options: {
trait: {
label: 'DAGGERHEART.LevelUp.Options.trait',
checkboxQuantity: 3,
minCost: 1,
type: LevelOptionType.trait.id,
amount: 2
},
hitPoint: {
label: 'DAGGERHEART.LevelUp.Options.hitPoint',
checkboxQuantity: 2,
minCost: 1,
type: LevelOptionType.hitPoint.id,
value: 1,
value: 1
},
stress: {
label: 'DAGGERHEART.LevelUp.Options.stress',
checkboxQuantity: 2,
minCost: 1,
type: LevelOptionType.stress.id,
value: 1
},
experience: {
label: 'DAGGERHEART.LevelUp.Options.experience',
checkboxQuantity: 1,
minCost: 1,
type: LevelOptionType.experience.id,
value: 1,
amount: 2
},
domainCard: {
label: 'DAGGERHEART.LevelUp.Options.domainCard',
checkboxQuantity: 1,
minCost: 1,
type: LevelOptionType.domainCard.id,
amount: 1
},
evasion: {
label: 'DAGGERHEART.LevelUp.Options.evasion',
checkboxQuantity: 1,
minCost: 1,
type: LevelOptionType.evasion.id,
value: 1
}
}
},
3: {
tier: 3,
name: 'Tier 3',
levels: {
start: 5,
end: 7
},
initialAchievements: {
experience: {
nr: 2,
modifier: 1
},
proficiency: 1
},
availableOptions: 2,
domainCardByLevel: 1,
options: {
trait: {
label: 'DAGGERHEART.LevelUp.Options.trait',
checkboxQuantity: 3,
minCost: 1,
type: LevelOptionType.trait.id,
amount: 2
},
hitPoint: {
label: 'DAGGERHEART.LevelUp.Options.hitPoint',
checkboxQuantity: 2,
minCost: 1,
type: LevelOptionType.hitPoint.id,
value: 1
},
stress: {
label: 'DAGGERHEART.LevelUp.Options.stress',
checkboxQuantity: 2,
minCost: 1,
type: LevelOptionType.stress.id,
value: 1
},
experience: {
label: 'DAGGERHEART.LevelUp.Options.experience',
checkboxQuantity: 1,
minCost: 1,
type: LevelOptionType.experience.id,
value: 1,
amount: 2
},
domainCard: {
label: 'DAGGERHEART.LevelUp.Options.domainCard',
checkboxQuantity: 1,
minCost: 1,
type: LevelOptionType.domainCard.id,
amount: 1
},
evasion: {
label: 'DAGGERHEART.LevelUp.Options.evasion',
checkboxQuantity: 1,
minCost: 1,
type: LevelOptionType.evasion.id,
value: 1
},
subclass: {
label: 'DAGGERHEART.LevelUp.Options.subclass',
checkboxQuantity: 1,
minCost: 1,
type: LevelOptionType.subclass.id
},
proficiency: {
label: 'DAGGERHEART.LevelUp.Options.proficiency',
checkboxQuantity: 2,
minCost: 2,
type: LevelOptionType.proficiency.id,
value: 1
},
multiclass: {
label: 'DAGGERHEART.LevelUp.Options.multiclass',
checkboxQuantity: 2,
minCost: 2,
type: LevelOptionType.multiclass.id
}
}
},
4: {
tier: 4,
name: 'Tier 4',
levels: {
start: 8,
end: 10
},
initialAchievements: {
experience: {
nr: 2,
modifier: 1
},
proficiency: 1
},
availableOptions: 2,
domainCardByLevel: 1,
options: {
trait: {
label: 'DAGGERHEART.LevelUp.Options.trait',
checkboxQuantity: 3,
minCost: 1,
type: LevelOptionType.trait.id,
amount: 2
},
hitPoint: {
label: 'DAGGERHEART.LevelUp.Options.hitPoint',
checkboxQuantity: 2,
minCost: 1,
type: LevelOptionType.hitPoint.id,
value: 1
},
stress: {
label: 'DAGGERHEART.LevelUp.Options.stress',
checkboxQuantity: 2,
minCost: 1,
type: LevelOptionType.stress.id,
value: 1
},
experience: {
label: 'DAGGERHEART.LevelUp.Options.experience',
checkboxQuantity: 1,
minCost: 1,
type: LevelOptionType.experience.id,
value: 1,
amount: 2
},
domainCard: {
label: 'DAGGERHEART.LevelUp.Options.domainCard',
checkboxQuantity: 1,
minCost: 1,
type: LevelOptionType.domainCard.id,
amount: 1
},
evasion: {
label: 'DAGGERHEART.LevelUp.Options.evasion',
checkboxQuantity: 1,
minCost: 1,
type: LevelOptionType.evasion.id,
value: 1
},
subclass: {
label: 'DAGGERHEART.LevelUp.Options.subclass',
checkboxQuantity: 1,
minCost: 1,
type: LevelOptionType.subclass.id
},
proficiency: {
label: 'DAGGERHEART.LevelUp.Options.proficiency',
checkboxQuantity: 2,
minCost: 2,
type: LevelOptionType.proficiency.id,
value: 1
},
multiclass: {
label: 'DAGGERHEART.LevelUp.Options.multiclass',
checkboxQuantity: 2,
minCost: 2,
type: LevelOptionType.multiclass.id
}
}
}
}
};

View file

@ -10,26 +10,8 @@ const attributeField = () =>
bonus: new fields.NumberField({ initial: 0, integer: true }),
actualValue: new fields.NumberField({ initial: 0, integer: true }),
overrideValue: new fields.NumberField({ initial: 0, integer: true })
}),
levelMarks: new fields.ArrayField(new fields.NumberField({ nullable: true, initial: null, integer: true })),
levelMark: new fields.NumberField({ nullable: true, initial: null, integer: true })
});
const levelUpTier = () => ({
attributes: new fields.TypedObjectField(new fields.BooleanField()),
hitPointSlots: new fields.TypedObjectField(new fields.BooleanField()),
stressSlots: new fields.TypedObjectField(new fields.BooleanField()),
experiences: new fields.TypedObjectField(new fields.ArrayField(new fields.StringField({}))),
proficiency: new fields.TypedObjectField(new fields.BooleanField()),
armorOrEvasionSlot: new fields.TypedObjectField(new fields.StringField({})),
subclass: new fields.TypedObjectField(
new fields.SchemaField({
multiclass: new fields.BooleanField(),
feature: new fields.StringField({})
})
),
multiclass: new fields.TypedObjectField(new fields.BooleanField())
});
});
export default class DhpPC extends foundry.abstract.TypeDataModel {
static defineSchema() {
@ -61,7 +43,7 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
})
)
}),
attributes: new fields.SchemaField({
traits: new fields.SchemaField({
agility: attributeField(),
strength: attributeField(),
finesse: attributeField(),
@ -78,14 +60,13 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
experiences: new fields.ArrayField(
new fields.SchemaField({
id: new fields.StringField({ required: true }),
level: new fields.NumberField({ required: true, integer: true }),
description: new fields.StringField({}),
value: new fields.NumberField({ integer: true, nullable: true, initial: null })
}),
{
initial: [
{ id: foundry.utils.randomID(), level: 1, description: '', value: 2 },
{ id: foundry.utils.randomID(), level: 1, description: '', value: 2 }
{ id: foundry.utils.randomID(), description: '', value: 2 },
{ id: foundry.utils.randomID(), description: '', value: 2 }
]
}
),
@ -100,30 +81,6 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
maxLoadout: new fields.NumberField({ initial: 2, integer: true }),
maxCards: new fields.NumberField({ initial: 2, integer: true })
}),
levelData: new fields.SchemaField({
currentLevel: new fields.NumberField({ initial: 1, integer: true }),
changedLevel: new fields.NumberField({ initial: 1, integer: true }),
levelups: new fields.TypedObjectField(
new fields.SchemaField({
level: new fields.NumberField({ required: true, integer: true }),
tier1: new fields.SchemaField({
...levelUpTier()
}),
tier2: new fields.SchemaField(
{
...levelUpTier()
},
{ nullable: true, initial: null }
),
tier3: new fields.SchemaField(
{
...levelUpTier()
},
{ nullable: true, initial: null }
)
})
)
}),
story: new fields.SchemaField({
background: new fields.HTMLField(),
appearance: new fields.HTMLField(),
@ -141,6 +98,7 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
max: new fields.NumberField({ initial: 6, integer: true }),
value: new fields.NumberField({ initial: 0, integer: true })
})
// levelUpData: new fields.TypeDataModel(DhpLevelUpData),
};
}
@ -292,30 +250,30 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
};
}
get totalAttributeMarks() {
return Object.keys(this.levelData.levelups).reduce((nr, level) => {
const nrAttributeMarks = Object.keys(this.levelData.levelups[level]).reduce((nr, tier) => {
nr += Object.keys(this.levelData.levelups[level][tier]?.attributes ?? {}).length * 2;
// get totalAttributeMarks() {
// return Object.keys(this.levelData.levelups).reduce((nr, level) => {
// const nrAttributeMarks = Object.keys(this.levelData.levelups[level]).reduce((nr, tier) => {
// nr += Object.keys(this.levelData.levelups[level][tier]?.attributes ?? {}).length * 2;
return nr;
}, 0);
// return nr;
// }, 0);
nr.push(...Array(nrAttributeMarks).fill(Number.parseInt(level)));
// nr.push(...Array(nrAttributeMarks).fill(Number.parseInt(level)));
return nr;
}, []);
}
// return nr;
// }, []);
// }
get availableAttributeMarks() {
const attributeMarks = Object.keys(this.attributes).flatMap(y => this.attributes[y].levelMarks);
return this.totalAttributeMarks.reduce((acc, attribute) => {
if (!attributeMarks.findSplice(x => x === attribute)) {
acc.push(attribute);
}
// get availableAttributeMarks() {
// const attributeMarks = Object.keys(this.attributes).flatMap(y => this.attributes[y].levelMarks);
// return this.totalAttributeMarks.reduce((acc, attribute) => {
// if (!attributeMarks.findSplice(x => x === attribute)) {
// acc.push(attribute);
// }
return acc;
}, []);
}
// return acc;
// }, []);
// }
get effects() {
return this.parent.items.reduce((acc, item) => {
@ -373,8 +331,8 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
this.resources.hope.value = Math.max(this.resources.hope.max - 1, 0);
}
for (var attributeKey in this.attributes) {
const attribute = this.attributes[attributeKey];
for (var attributeKey in this.traits) {
const attribute = this.traits[attributeKey];
attribute.levelMark = attribute.levelMarks.find(x => this.isSameTier(x)) ?? null;

View file

@ -31,9 +31,9 @@ export default class DhpActor extends Actor {
return acc;
}, {});
changed.system.attributes = Object.keys(this.system.attributes).reduce((acc, key) => {
changed.system.traits = Object.keys(this.system.traits).reduce((acc, key) => {
acc[key] = {
levelMarks: this.system.attributes[key].levelMarks.filter(
levelMarks: this.system.traits[key].levelMarks.filter(
x => x <= changed.system.levelData.currentLevel
)
};