[Feature] ActiveEffect Autocomplete (#338)

* Fixed translation of TrackedAttributeChoices

* Styling improvements

* Added hints

* fix autocomplete style, fix tagify style, fix magical and physical tag style and fix lang in details adversary settings

* Removed commented out code

* Some companion fixes

---------

Co-authored-by: moliloo <dev.murilobrito@gmail.com>
This commit is contained in:
WBHarry 2025-07-15 15:55:45 +02:00 committed by GitHub
parent 37c1d7ad88
commit 0dd5b53313
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 501 additions and 171 deletions

View file

@ -3,3 +3,5 @@ export { default as AdversarySettings } from './adversary-settings.mjs';
export { default as CompanionSettings } from './companion-settings.mjs';
export { default as EnvironmentSettings } from './environment-settings.mjs';
export { default as ActiveEffectConfig } from './activeEffectConfig.mjs';
export { default as DhTokenConfig } from './token-config.mjs';
export { default as DhPrototypeTokenConfig } from './prototype-token-config.mjs';

View file

@ -1,4 +1,24 @@
import autocomplete from 'autocompleter';
export default class DhActiveEffectConfig extends foundry.applications.sheets.ActiveEffectConfig {
constructor(options) {
super(options);
const ignoredActorKeys = ['config', 'DhEnvironment'];
this.changeChoices = Object.keys(game.system.api.models.actors).reduce((acc, key) => {
if (!ignoredActorKeys.includes(key)) {
const model = game.system.api.models.actors[key];
const attributes = CONFIG.Token.documentClass.getTrackedAttributes(model);
const group = game.i18n.localize(model.metadata.label);
const choices = CONFIG.Token.documentClass
.getTrackedAttributeChoices(attributes, model)
.map(x => ({ ...x, group: group }));
acc.push(...choices);
}
return acc;
}, []);
}
static DEFAULT_OPTIONS = {
classes: ['daggerheart', 'sheet', 'dh-style']
};
@ -27,36 +47,59 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
}
};
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
const changeChoices = this.changeChoices;
htmlElement.querySelectorAll('.effect-change-input').forEach(element => {
autocomplete({
input: element,
fetch: function (text, update) {
if (!text) {
update(changeChoices);
} else {
text = text.toLowerCase();
var suggestions = changeChoices.filter(n => n.label.toLowerCase().includes(text));
update(suggestions);
}
},
render: function (item, search) {
const label = game.i18n.localize(item.label);
const matchIndex = label.toLowerCase().indexOf(search);
const beforeText = label.slice(0, matchIndex);
const matchText = label.slice(matchIndex, matchIndex + search.length);
const after = label.slice(matchIndex + search.length, label.length);
const element = document.createElement('li');
element.innerHTML = `${beforeText}${matchText ? `<strong>${matchText}</strong>` : ''}${after}`;
if (item.hint) {
element.dataset.tooltip = game.i18n.localize(item.hint);
}
return element;
},
renderGroup: function (label) {
const itemElement = document.createElement('div');
itemElement.textContent = game.i18n.localize(label);
return itemElement;
},
onSelect: function (item) {
element.value = `system.${item.value}`;
},
click: e => e.fetch(),
minLength: 0
});
});
}
async _preparePartContext(partId, context) {
const partContext = await super._preparePartContext(partId, context);
switch (partId) {
case 'changes':
const fieldPaths = [];
const validFieldPath = fieldPath => this.validFieldPath(fieldPath, this.#unapplicablePaths);
context.document.parent.system.schema.apply(function () {
if (!(this instanceof foundry.data.fields.SchemaField)) {
if (validFieldPath(this.fieldPath)) {
fieldPaths.push(this.fieldPath);
}
}
});
context.fieldPaths = fieldPaths;
break;
}
return partContext;
}
#unapplicablePaths = ['story', 'pronouns', 'description'];
validFieldPath(fieldPath, unapplicablePaths) {
const splitPath = fieldPath.split('.');
if (splitPath.length > 1 && unapplicablePaths.includes(splitPath[1])) return false;
/* The current value of a resource should not be modified */
if (new RegExp(/resources.*\.value/).exec(fieldPath)) return false;
return true;
}
}

View file

@ -0,0 +1,20 @@
export default class DhPrototypeTokenConfig extends foundry.applications.sheets.PrototypeTokenConfig {
/** @inheritDoc */
async _prepareResourcesTab() {
const token = this.token;
const usesTrackableAttributes = !foundry.utils.isEmpty(CONFIG.Actor.trackableAttributes);
const attributeSource =
this.actor?.system instanceof foundry.abstract.DataModel && usesTrackableAttributes
? this.actor?.type
: this.actor?.system;
const TokenDocument = foundry.utils.getDocumentClass('Token');
const attributes = TokenDocument.getTrackedAttributes(attributeSource);
return {
barAttributes: TokenDocument.getTrackedAttributeChoices(attributes, attributeSource),
bar1: token.getBarAttribute?.('bar1'),
bar2: token.getBarAttribute?.('bar2'),
turnMarkerModes: DhPrototypeTokenConfig.TURN_MARKER_MODES,
turnMarkerAnimations: CONFIG.Combat.settings.turnMarkerAnimations
};
}
}

View file

@ -0,0 +1,20 @@
export default class DhTokenConfig extends foundry.applications.sheets.TokenConfig {
/** @inheritDoc */
async _prepareResourcesTab() {
const token = this.token;
const usesTrackableAttributes = !foundry.utils.isEmpty(CONFIG.Actor.trackableAttributes);
const attributeSource =
this.actor?.system instanceof foundry.abstract.DataModel && usesTrackableAttributes
? this.actor?.type
: this.actor?.system;
const TokenDocument = foundry.utils.getDocumentClass('Token');
const attributes = TokenDocument.getTrackedAttributes(attributeSource);
return {
barAttributes: TokenDocument.getTrackedAttributeChoices(attributes, attributeSource),
bar1: token.getBarAttribute?.('bar1'),
bar2: token.getBarAttribute?.('bar2'),
turnMarkerModes: DhTokenConfig.TURN_MARKER_MODES,
turnMarkerAnimations: CONFIG.Combat.settings.turnMarkerAnimations
};
}
}

View file

@ -103,7 +103,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
htmlElement.querySelectorAll('.inventory-item-quantity').forEach(element => {
element.addEventListener('change', this.updateItemQuantity.bind(this));
});
// Add listener for armor marks input
htmlElement.querySelectorAll('.armor-marks-input').forEach(element => {
element.addEventListener('change', this.updateArmorMarks.bind(this));

View file

@ -3,55 +3,55 @@ export const domains = {
id: 'arcana',
label: 'DAGGERHEART.GENERAL.Domain.arcana.label',
src: 'systems/daggerheart/assets/icons/domains/arcana.svg',
description: 'DAGGERHEART.GENERAL.Domain.Arcana'
description: 'DAGGERHEART.GENERAL.Domain.arcana.description'
},
blade: {
id: 'blade',
label: 'DAGGERHEART.GENERAL.Domain.blade.label',
src: 'systems/daggerheart/assets/icons/domains/blade.svg',
description: 'DAGGERHEART.GENERAL.Domain.Blade'
description: 'DAGGERHEART.GENERAL.Domain.blade.description'
},
bone: {
id: 'bone',
label: 'DAGGERHEART.GENERAL.Domain.bone.label',
src: 'systems/daggerheart/assets/icons/domains/bone.svg',
description: 'DAGGERHEART.GENERAL.Domain.Bone'
description: 'DAGGERHEART.GENERAL.Domain.bone.description'
},
codex: {
id: 'codex',
label: 'DAGGERHEART.GENERAL.Domain.codex.label',
src: 'systems/daggerheart/assets/icons/domains/codex.svg',
description: 'DAGGERHEART.GENERAL.Domain.Codex'
description: 'DAGGERHEART.GENERAL.Domain.codex.description'
},
grace: {
id: 'grace',
label: 'DAGGERHEART.GENERAL.Domain.grace.label',
src: 'systems/daggerheart/assets/icons/domains/grace.svg',
description: 'DAGGERHEART.GENERAL.Domain.Grace'
description: 'DAGGERHEART.GENERAL.Domain.grace.description'
},
midnight: {
id: 'midnight',
label: 'DAGGERHEART.GENERAL.Domain.midnight.label',
src: 'systems/daggerheart/assets/icons/domains/midnight.svg',
description: 'DAGGERHEART.GENERAL.Domain.Midnight'
description: 'DAGGERHEART.GENERAL.Domain.midnight.description'
},
sage: {
id: 'sage',
label: 'DAGGERHEART.GENERAL.Domain.sage.label',
src: 'systems/daggerheart/assets/icons/domains/sage.svg',
description: 'DAGGERHEART.GENERAL.Domain.Sage'
description: 'DAGGERHEART.GENERAL.Domain.sage.description'
},
splendor: {
id: 'splendor',
label: 'DAGGERHEART.GENERAL.Domain.splendor.label',
src: 'systems/daggerheart/assets/icons/domains/splendor.svg',
description: 'DAGGERHEART.GENERAL.Domain.Splendor'
description: 'DAGGERHEART.GENERAL.Domain.splendor.description'
},
valor: {
id: 'valor',
label: 'DAGGERHEART.GENERAL.Domain.valor.label',
src: 'systems/daggerheart/assets/icons/domains/valor.svg',
description: 'DAGGERHEART.GENERAL.Domain.Valor'
description: 'DAGGERHEART.GENERAL.Domain.valor.description'
}
};

View file

@ -364,7 +364,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
}
get modifiers() {
if(!this.actor) return [];
if (!this.actor) return [];
const modifiers = [];
/** Placeholder for specific bonuses **/
return modifiers;

View file

@ -31,14 +31,29 @@ export default class DhpAdversary extends BaseDataActor {
motivesAndTactics: new fields.StringField(),
notes: new fields.HTMLField(),
difficulty: new fields.NumberField({ required: true, initial: 1, integer: true }),
hordeHp: new fields.NumberField({ required: true, initial: 1, integer: true }),
hordeHp: new fields.NumberField({
required: true,
initial: 1,
integer: true,
label: 'DAGGERHEART.GENERAL.hordeHp'
}),
damageThresholds: new fields.SchemaField({
major: new fields.NumberField({ required: true, initial: 0, integer: true }),
severe: new fields.NumberField({ required: true, initial: 0, integer: true })
major: new fields.NumberField({
required: true,
initial: 0,
integer: true,
label: 'DAGGERHEART.GENERAL.DamageThresholds.majorThreshold'
}),
severe: new fields.NumberField({
required: true,
initial: 0,
integer: true,
label: 'DAGGERHEART.GENERAL.DamageThresholds.severeThreshold'
})
}),
resources: new fields.SchemaField({
hitPoints: resourceField(0, true),
stress: resourceField(0, true)
hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.hitPoints', true),
stress: resourceField(0, 'DAGGERHEART.GENERAL.stress', true)
}),
attack: new ActionField({
initial: {
@ -75,13 +90,13 @@ export default class DhpAdversary extends BaseDataActor {
),
bonuses: new fields.SchemaField({
roll: new fields.SchemaField({
attack: bonusField(),
action: bonusField(),
reaction: bonusField()
attack: bonusField('DAGGERHEART.GENERAL.Roll.attack'),
action: bonusField('DAGGERHEART.GENERAL.Roll.action'),
reaction: bonusField('DAGGERHEART.GENERAL.Roll.reaction')
}),
damage: new fields.SchemaField({
physical: bonusField(),
magical: bonusField()
physical: bonusField('DAGGERHEART.GENERAL.Damage.physicalDamage'),
magical: bonusField('DAGGERHEART.GENERAL.Damage.magicalDamage')
})
})
};

View file

@ -1,10 +1,10 @@
import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs';
const resistanceField = () =>
const resistanceField = reductionLabel =>
new foundry.data.fields.SchemaField({
resistance: new foundry.data.fields.BooleanField({ initial: false }),
immunity: new foundry.data.fields.BooleanField({ initial: false }),
reduction: new foundry.data.fields.NumberField({ integer: true, initial: 0 })
reduction: new foundry.data.fields.NumberField({ integer: true, initial: 0, label: reductionLabel })
});
/**
@ -40,8 +40,8 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
if (this.metadata.isNPC) schema.description = new fields.HTMLField({ required: true, nullable: true });
if (this.metadata.hasResistances)
schema.resistance = new fields.SchemaField({
physical: resistanceField(),
magical: resistanceField()
physical: resistanceField('DAGGERHEART.GENERAL.DamageResistance.physicalReduction'),
magical: resistanceField('DAGGERHEART.GENERAL.DamageResistance.magicalReduction')
});
return schema;
}

View file

@ -5,6 +5,8 @@ import BaseDataActor from './base.mjs';
import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
export default class DhCharacter extends BaseDataActor {
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Character'];
static get metadata() {
return foundry.utils.mergeObject(super.metadata, {
label: 'TYPES.Actor.character',
@ -19,24 +21,36 @@ export default class DhCharacter extends BaseDataActor {
return {
...super.defineSchema(),
resources: new fields.SchemaField({
hitPoints: resourceField(0, true),
stress: resourceField(6, true),
hope: resourceField(6)
hitPoints: resourceField(0, 'DAGGERHEART.GENERAL.hitPoints', true),
stress: resourceField(6, 'DAGGERHEART.GENERAL.stress', true),
hope: resourceField(6, 'DAGGERHEART.GENERAL.hope')
}),
traits: new fields.SchemaField({
agility: attributeField(),
strength: attributeField(),
finesse: attributeField(),
instinct: attributeField(),
presence: attributeField(),
knowledge: attributeField()
agility: attributeField('DAGGERHEART.CONFIG.Traits.agility.name'),
strength: attributeField('DAGGERHEART.CONFIG.Traits.strength.name'),
finesse: attributeField('DAGGERHEART.CONFIG.Traits.finesse.name'),
instinct: attributeField('DAGGERHEART.CONFIG.Traits.instinct.name'),
presence: attributeField('DAGGERHEART.CONFIG.Traits.presence.name'),
knowledge: attributeField('DAGGERHEART.CONFIG.Traits.knowledge.name')
}),
proficiency: new fields.NumberField({ initial: 1, integer: true }),
evasion: new fields.NumberField({ initial: 0, integer: true }),
armorScore: new fields.NumberField({ integer: true, initial: 0 }),
proficiency: new fields.NumberField({
initial: 1,
integer: true,
label: 'DAGGERHEART.GENERAL.proficiency'
}),
evasion: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.evasion' }),
armorScore: new fields.NumberField({ integer: true, initial: 0, label: 'DAGGERHEART.GENERAL.armorScore' }),
damageThresholds: new fields.SchemaField({
severe: new fields.NumberField({ integer: true, initial: 0 }),
major: new fields.NumberField({ integer: true, initial: 0 })
severe: new fields.NumberField({
integer: true,
initial: 0,
label: 'DAGGERHEART.GENERAL.DamageThresholds.majorThreshold'
}),
major: new fields.NumberField({
integer: true,
initial: 0,
label: 'DAGGERHEART.GENERAL.DamageThresholds.severeThreshold'
})
}),
experiences: new fields.TypedObjectField(
new fields.SchemaField({
@ -76,25 +90,37 @@ export default class DhCharacter extends BaseDataActor {
levelData: new fields.EmbeddedDataField(DhLevelData),
bonuses: new fields.SchemaField({
roll: new fields.SchemaField({
attack: bonusField(),
spellcast: bonusField(),
trait: bonusField(),
action: bonusField(),
reaction: bonusField(),
primaryWeapon: bonusField(),
secondaryWeapon: bonusField()
attack: bonusField('DAGGERHEART.GENERAL.Roll.attack'),
spellcast: bonusField('DAGGERHEART.GENERAL.Roll.spellcast'),
trait: bonusField('DAGGERHEART.GENERAL.Roll.trait'),
action: bonusField('DAGGERHEART.GENERAL.Roll.action'),
reaction: bonusField('DAGGERHEART.GENERAL.Roll.reaction'),
primaryWeapon: bonusField('DAGGERHEART.GENERAL.Roll.primaryWeaponAttack'),
secondaryWeapon: bonusField('DAGGERHEART.GENERAL.Roll.secondaryWeaponAttack')
}),
damage: new fields.SchemaField({
physical: bonusField(),
magical: bonusField(),
primaryWeapon: bonusField(),
secondaryWeapon: bonusField()
physical: bonusField('DAGGERHEART.GENERAL.Damage.physicalDamage'),
magical: bonusField('DAGGERHEART.GENERAL.Damage.magicalDamage'),
primaryWeapon: bonusField('DAGGERHEART.GENERAL.Damage.primaryWeapon'),
secondaryWeapon: bonusField('DAGGERHEART.GENERAL.Damage.primaryWeapon')
}),
healing: bonusField(),
healing: bonusField('DAGGERHEART.GENERAL.Healing.healingAmount'),
range: new fields.SchemaField({
weapon: new fields.NumberField({ integer: true, initial: 0 }),
spell: new fields.NumberField({ integer: true, initial: 0 }),
other: new fields.NumberField({ integer: true, initial: 0 })
weapon: new fields.NumberField({
integer: true,
initial: 0,
label: 'DAGGERHEART.GENERAL.Range.weapon'
}),
spell: new fields.NumberField({
integer: true,
initial: 0,
label: 'DAGGERHEART.GENERAL.Range.spell'
}),
other: new fields.NumberField({
integer: true,
initial: 0,
label: 'DAGGERHEART.GENERAL.Range.other'
})
})
}),
companion: new ForeignDocumentUUIDField({ type: 'Actor', nullable: true, initial: null }),
@ -102,25 +128,34 @@ export default class DhCharacter extends BaseDataActor {
damageReduction: new fields.SchemaField({
maxArmorMarked: new fields.SchemaField({
value: new fields.NumberField({ required: true, integer: true, initial: 1 }),
bonus: new fields.NumberField({ required: true, integer: true, initial: 0 }),
stressExtra: new fields.NumberField({ required: true, integer: true, initial: 0 })
bonus: new fields.NumberField({
required: true,
integer: true,
initial: 0,
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedBonus'
}),
stressExtra: new fields.NumberField({
required: true,
integer: true,
initial: 0,
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedStress.label',
hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedStress.hint'
})
}),
stressDamageReduction: new fields.SchemaField({
severe: stressDamageReductionRule(),
major: stressDamageReductionRule(),
minor: stressDamageReductionRule()
severe: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.severe'),
major: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.major'),
minor: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.minor')
}),
increasePerArmorMark: new fields.NumberField({
integer: true,
initial: 1,
label: 'DAGGERHEART.GENERAL.Rules.damageReduction.increasePerArmorMark.label',
hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.increasePerArmorMark.hint'
}),
increasePerArmorMark: new fields.NumberField({ integer: true, initial: 1 }),
magical: new fields.BooleanField({ initial: false }),
physical: new fields.BooleanField({ initial: false })
}),
strangePatterns: new fields.NumberField({
integer: true,
min: 1,
max: 12,
nullable: true,
initial: null
}),
weapon: new fields.SchemaField({
/* Unimplemented
-> Should remove the lowest damage dice from weapon damage

View file

@ -24,10 +24,16 @@ export default class DhCompanion extends BaseDataActor {
...super.defineSchema(),
partner: new ForeignDocumentUUIDField({ type: 'Actor' }),
resources: new fields.SchemaField({
stress: resourceField(3, true),
hope: new fields.NumberField({ initial: 0, integer: true })
stress: resourceField(3, 'DAGGERHEART.GENERAL.stress', true),
hope: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.hope' })
}),
evasion: new fields.NumberField({
required: true,
min: 1,
initial: 10,
integer: true,
label: 'DAGGERHEART.GENERAL.evasion'
}),
evasion: new fields.NumberField({ required: true, min: 1, initial: 10, integer: true }),
experiences: new fields.TypedObjectField(
new fields.SchemaField({
name: new fields.StringField({}),
@ -74,8 +80,8 @@ export default class DhCompanion extends BaseDataActor {
levelData: new fields.EmbeddedDataField(DhLevelData),
bonuses: new fields.SchemaField({
damage: new fields.SchemaField({
physical: bonusField(),
magical: bonusField()
physical: bonusField('DAGGERHEART.GENERAL.Damage.physicalDamage'),
magical: bonusField('DAGGERHEART.GENERAL.Damage.magicalDamage')
})
})
};

View file

@ -1,28 +1,32 @@
const fields = foundry.data.fields;
const attributeField = () =>
const attributeField = label =>
new fields.SchemaField({
value: new fields.NumberField({ initial: 0, integer: true }),
value: new fields.NumberField({ initial: 0, integer: true, label }),
tierMarked: new fields.BooleanField({ initial: false })
});
const resourceField = (max = 0, reverse = false) =>
const resourceField = (max = 0, label, reverse = false) =>
new fields.SchemaField({
value: new fields.NumberField({ initial: 0, integer: true }),
value: new fields.NumberField({ initial: 0, integer: true, label }),
max: new fields.NumberField({ initial: max, integer: true }),
isReversed: new fields.BooleanField({ initial: reverse })
});
const stressDamageReductionRule = () =>
const stressDamageReductionRule = localizationPath =>
new fields.SchemaField({
enabled: new fields.BooleanField({ required: true, initial: false }),
cost: new fields.NumberField({ integer: true })
cost: new fields.NumberField({
integer: true,
label: `${localizationPath}.label`,
hint: `${localizationPath}.hint`
})
});
const bonusField = () =>
const bonusField = label =>
new fields.SchemaField({
bonus: new fields.NumberField({ integer: true, initial: 0 }),
bonus: new fields.NumberField({ integer: true, initial: 0, label }),
dice: new fields.ArrayField(new fields.StringField())
})
});
export { attributeField, resourceField, stressDamageReductionRule, bonusField };
export { attributeField, resourceField, stressDamageReductionRule, bonusField };

View file

@ -102,7 +102,7 @@ export default class D20Roll extends DHRoll {
value: this.options.data.experiences[m].value
});
});
this.addModifiers();
if (this.options.extraFormula) {
this.terms.push(
@ -123,15 +123,17 @@ export default class D20Roll extends DHRoll {
applyBaseBonus() {
const modifiers = [];
if(this.options.roll.bonus)
if (this.options.roll.bonus)
modifiers.push({
label: 'Bonus to Hit',
value: this.options.roll.bonus
});
modifiers.push(...this.getBonus(`roll.${this.options.type}`, `${this.options.type.capitalize()} Bonus`));
modifiers.push(...this.getBonus(`roll.${this.options.roll.type}`, `${this.options.roll.type.capitalize()} Bonus`));
modifiers.push(
...this.getBonus(`roll.${this.options.roll.type}`, `${this.options.roll.type.capitalize()} Bonus`)
);
return modifiers;
}

View file

@ -34,16 +34,16 @@ export default class DamageRoll extends DHRoll {
});
const weapons = ['primaryWeapon', 'secondaryWeapon'];
weapons.forEach(w => {
if(this.options.source.item && this.options.source.item === this.data[w]?.id)
if (this.options.source.item && this.options.source.item === this.data[w]?.id)
modifiers.push(...this.getBonus(`${type}.${w}`, 'Weapon Bonus'));
});
return modifiers;
}
constructFormula(config) {
super.constructFormula(config);
if (config.isCritical) {
const tmpRoll = new Roll(this._formula)._evaluateSync({ maximize: true }),
criticalBonus = tmpRoll.total - this.constructor.calculateTotalModifiers(tmpRoll);

View file

@ -4,7 +4,7 @@ export default class DHRoll extends Roll {
baseTerms = [];
constructor(formula, data, options) {
super(formula, data, options);
if(!this.data || !Object.keys(this.data).length) this.data = options.data;
if (!this.data || !Object.keys(this.data).length) this.data = options.data;
}
static messageType = 'adversaryRoll';
@ -100,7 +100,7 @@ export default class DHRoll extends Roll {
}
formatModifier(modifier) {
if(Array.isArray(modifier)) {
if (Array.isArray(modifier)) {
return [
new foundry.dice.terms.OperatorTerm({ operator: '+' }),
...this.constructor.parse(modifier.join(' + '), this.options.data)
@ -127,12 +127,12 @@ export default class DHRoll extends Roll {
getBonus(path, label) {
const bonus = foundry.utils.getProperty(this.data.bonuses, path),
modifiers = [];
if(bonus?.bonus)
if (bonus?.bonus)
modifiers.push({
label: label,
value: bonus?.bonus
});
if(bonus?.dice?.length)
if (bonus?.dice?.length)
modifiers.push({
label: label,
value: bonus?.dice

View file

@ -121,7 +121,7 @@ export default class DualityRoll extends D20Roll {
applyBaseBonus() {
const modifiers = super.applyBaseBonus();
if(this.options.roll.trait && this.data.traits[this.options.roll.trait])
if (this.options.roll.trait && this.data.traits[this.options.roll.trait])
modifiers.unshift({
label: `DAGGERHEART.CONFIG.Traits.${this.options.roll.trait}.name`,
value: this.data.traits[this.options.roll.trait].value
@ -129,7 +129,7 @@ export default class DualityRoll extends D20Roll {
const weapons = ['primaryWeapon', 'secondaryWeapon'];
weapons.forEach(w => {
if(this.options.source.item && this.options.source.item === this.data[w]?.id)
if (this.options.source.item && this.options.source.item === this.data[w]?.id)
modifiers.push(...this.getBonus(`roll.${w}`, 'Weapon Bonus'));
});

View file

@ -3,4 +3,5 @@ export { default as DHItem } from './item.mjs';
export { default as DhpCombat } from './combat.mjs';
export { default as DhActiveEffect } from './activeEffect.mjs';
export { default as DhChatMessage } from './chatMessage.mjs';
export { default as DhToken } from './token.mjs';
export { default as DhTooltipManager } from './tooltipManager.mjs';

View file

@ -0,0 +1,35 @@
export default class DHToken extends TokenDocument {
/**
* Inspect the Actor data model and identify the set of attributes which could be used for a Token Bar.
* @param {object} attributes The tracked attributes which can be chosen from
* @returns {object} A nested object of attribute choices to display
*/
static getTrackedAttributeChoices(attributes, model) {
attributes = attributes || this.getTrackedAttributes();
const barGroup = game.i18n.localize('TOKEN.BarAttributes');
const valueGroup = game.i18n.localize('TOKEN.BarValues');
const bars = attributes.bar.map(v => {
const a = v.join('.');
const modelLabel = model ? game.i18n.localize(model.schema.getField(`${a}.value`).label) : null;
return { group: barGroup, value: a, label: modelLabel ? modelLabel : a };
});
bars.sort((a, b) => a.label.compare(b.label));
const invalidAttributes = ['gold', 'levelData', 'rules.damageReduction.maxArmorMarked.value'];
const values = attributes.value.reduce((acc, v) => {
const a = v.join('.');
if (invalidAttributes.some(x => a.startsWith(x))) return acc;
const field = model ? model.schema.getField(a) : null;
const modelLabel = field ? game.i18n.localize(field.label) : null;
const hint = field ? game.i18n.localize(field.hint) : null;
acc.push({ group: valueGroup, value: a, label: modelLabel ? modelLabel : a, hint: hint });
return acc;
}, []);
values.sort((a, b) => a.label.compare(b.label));
return bars.concat(values);
}
}