} An object of value-specific errors by key.
+ */
+ _validateValues(value, options) {
+ const errors = {};
+ for ( const [k, v] of Object.entries(value) ) {
+ if ( k.startsWith("-=") ) continue;
+ const error = this.model.validate(v, options);
+ if ( error ) errors[k] = error;
+ }
+ return errors;
+ }
+
+ /* -------------------------------------------- */
+
+ /** @override */
+ initialize(value, model, options={}) {
+ if ( !value ) return value;
+ const obj = {};
+ const initialKeys = (this.initialKeys instanceof Array) ? this.initialKeys : Object.keys(this.initialKeys ?? {});
+ const keys = this.initialKeysOnly ? initialKeys : Object.keys(value);
+ for ( const key of keys ) {
+ const data = value[key] ?? this._getInitialValueForKey(key, value);
+ obj[key] = this.model.initialize(data, model, options);
+ }
+ return obj;
+ }
+
+ /* -------------------------------------------- */
+
+ /** @inheritDoc */
+ _getField(path) {
+ if ( path.length === 0 ) return this;
+ else if ( path.length === 1 ) return this.model;
+ path.shift();
+ return this.model._getField(path);
+ }
+}
\ No newline at end of file
diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs
index 8522c8fc..2639ae1e 100644
--- a/module/data/item/armor.mjs
+++ b/module/data/item/armor.mjs
@@ -1,7 +1,6 @@
import AttachableItem from './attachableItem.mjs';
-import ActionField from '../fields/actionField.mjs';
+import { ActionsField } from '../fields/actionField.mjs';
import { armorFeatures } from '../../config/itemConfig.mjs';
-import { actionsTypes } from '../action/_module.mjs';
export default class DHArmor extends AttachableItem {
/** @inheritDoc */
@@ -10,7 +9,8 @@ export default class DHArmor extends AttachableItem {
label: 'TYPES.Item.armor',
type: 'armor',
hasDescription: true,
- isInventoryItem: true
+ isInventoryItem: true,
+ hasActions: true
});
}
@@ -39,8 +39,7 @@ export default class DHArmor extends AttachableItem {
baseThresholds: new fields.SchemaField({
major: new fields.NumberField({ integer: true, initial: 0 }),
severe: new fields.NumberField({ integer: true, initial: 0 })
- }),
- actions: new fields.ArrayField(new ActionField())
+ })
};
}
@@ -65,7 +64,10 @@ export default class DHArmor extends AttachableItem {
actionIds.push(...feature.actionIds);
}
await this.parent.deleteEmbeddedDocuments('ActiveEffect', effectIds);
- changes.system.actions = this.actions.filter(x => !actionIds.includes(x._id));
+ changes.system.actions = actionIds.reduce((acc, id) => {
+ acc[`-=${id}`] = null;
+ return acc;
+ }, {});
for (var feature of added) {
const featureData = armorFeatures[feature.value];
@@ -79,17 +81,38 @@ export default class DHArmor extends AttachableItem {
]);
feature.effectIds = embeddedItems.map(x => x.id);
}
+
+ const newActions = {};
if (featureData.actions?.length > 0) {
- const newActions = featureData.actions.map(action => {
- const cls = actionsTypes[action.type];
- return new cls(
- { ...action, _id: foundry.utils.randomID(), name: game.i18n.localize(action.name) },
+ for (let action of featureData.actions) {
+ const embeddedEffects = await this.parent.createEmbeddedDocuments(
+ 'ActiveEffect',
+ (action.effects ?? []).map(effect => ({
+ ...effect,
+ transfer: false,
+ name: game.i18n.localize(effect.name),
+ description: game.i18n.localize(effect.description)
+ }))
+ );
+
+ const cls = game.system.api.models.actions.actionsTypes[action.type];
+ const actionId = foundry.utils.randomID();
+ newActions[actionId] = new cls(
+ {
+ ...cls.getSourceConfig(this),
+ ...action,
+ _id: actionId,
+ name: game.i18n.localize(action.name),
+ description: game.i18n.localize(action.description),
+ effects: embeddedEffects.map(x => ({ _id: x.id }))
+ },
{ parent: this }
);
- });
- changes.system.actions = [...this.actions, ...newActions];
- feature.actionIds = newActions.map(x => x._id);
+ }
}
+
+ changes.system.actions = newActions;
+ feature.actionIds = Object.keys(newActions);
}
}
}
diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs
index 24e5e0cc..0b2d8ddf 100644
--- a/module/data/item/base.mjs
+++ b/module/data/item/base.mjs
@@ -8,6 +8,8 @@
* @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item
*/
+import { ActionsField } from "../fields/actionField.mjs";
+
const fields = foundry.data.fields;
export default class BaseDataItem extends foundry.abstract.TypeDataModel {
@@ -21,7 +23,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
hasDescription: false,
hasResource: false,
isQuantifiable: false,
- isInventoryItem: false
+ isInventoryItem: false,
+ hasActions: false
};
}
@@ -69,6 +72,9 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
if (this.metadata.isQuantifiable)
schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true });
+ if (this.metadata.hasActions)
+ schema.actions = new ActionsField()
+
return schema;
}
diff --git a/module/data/item/consumable.mjs b/module/data/item/consumable.mjs
index 3e70f97a..cd192dfe 100644
--- a/module/data/item/consumable.mjs
+++ b/module/data/item/consumable.mjs
@@ -1,5 +1,5 @@
import BaseDataItem from './base.mjs';
-import ActionField from '../fields/actionField.mjs';
+import { ActionField } from '../fields/actionField.mjs';
export default class DHConsumable extends BaseDataItem {
/** @inheritDoc */
@@ -9,7 +9,8 @@ export default class DHConsumable extends BaseDataItem {
type: 'consumable',
hasDescription: true,
isQuantifiable: true,
- isInventoryItem: true
+ isInventoryItem: true,
+ hasActions: true
});
}
@@ -18,8 +19,7 @@ export default class DHConsumable extends BaseDataItem {
const fields = foundry.data.fields;
return {
...super.defineSchema(),
- consumeOnUse: new fields.BooleanField({ initial: false }),
- actions: new fields.ArrayField(new ActionField())
+ consumeOnUse: new fields.BooleanField({ initial: false })
};
}
}
diff --git a/module/data/item/domainCard.mjs b/module/data/item/domainCard.mjs
index df60b9d1..d366b7a0 100644
--- a/module/data/item/domainCard.mjs
+++ b/module/data/item/domainCard.mjs
@@ -1,5 +1,5 @@
import BaseDataItem from './base.mjs';
-import ActionField from '../fields/actionField.mjs';
+import { ActionField } from '../fields/actionField.mjs';
export default class DHDomainCard extends BaseDataItem {
/** @inheritDoc */
@@ -8,7 +8,8 @@ export default class DHDomainCard extends BaseDataItem {
label: 'TYPES.Item.domainCard',
type: 'domainCard',
hasDescription: true,
- hasResource: true
+ hasResource: true,
+ hasActions: true
});
}
@@ -29,8 +30,7 @@ export default class DHDomainCard extends BaseDataItem {
required: true,
initial: CONFIG.DH.DOMAIN.cardTypes.ability.id
}),
- inVault: new fields.BooleanField({ initial: false }),
- actions: new fields.ArrayField(new ActionField())
+ inVault: new fields.BooleanField({ initial: false })
};
}
diff --git a/module/data/item/feature.mjs b/module/data/item/feature.mjs
index 62b955e9..93a2c0bd 100644
--- a/module/data/item/feature.mjs
+++ b/module/data/item/feature.mjs
@@ -1,5 +1,5 @@
import BaseDataItem from './base.mjs';
-import ActionField from '../fields/actionField.mjs';
+import { ActionField, ActionsField } from '../fields/actionField.mjs';
export default class DHFeature extends BaseDataItem {
/** @inheritDoc */
@@ -8,7 +8,8 @@ export default class DHFeature extends BaseDataItem {
label: 'TYPES.Item.feature',
type: 'feature',
hasDescription: true,
- hasResource: true
+ hasResource: true,
+ hasActions: true
});
}
@@ -24,8 +25,7 @@ export default class DHFeature extends BaseDataItem {
}),
subType: new fields.StringField({ choices: CONFIG.DH.ITEM.featureSubTypes, nullable: true, initial: null }),
originId: new fields.StringField({ nullable: true, initial: null }),
- identifier: new fields.StringField(),
- actions: new fields.ArrayField(new ActionField())
+ identifier: new fields.StringField()
};
}
diff --git a/module/data/item/miscellaneous.mjs b/module/data/item/miscellaneous.mjs
index cad07f48..c40ac60b 100644
--- a/module/data/item/miscellaneous.mjs
+++ b/module/data/item/miscellaneous.mjs
@@ -1,5 +1,5 @@
import BaseDataItem from './base.mjs';
-import ActionField from '../fields/actionField.mjs';
+import { ActionField } from '../fields/actionField.mjs';
export default class DHMiscellaneous extends BaseDataItem {
/** @inheritDoc */
@@ -9,16 +9,15 @@ export default class DHMiscellaneous extends BaseDataItem {
type: 'miscellaneous',
hasDescription: true,
isQuantifiable: true,
- isInventoryItem: true
+ isInventoryItem: true,
+ hasActions: true
});
}
/** @inheritDoc */
static defineSchema() {
- const fields = foundry.data.fields;
return {
- ...super.defineSchema(),
- actions: new fields.ArrayField(new ActionField())
+ ...super.defineSchema()
};
}
}
diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs
index 0d0c7f76..71d1e08d 100644
--- a/module/data/item/weapon.mjs
+++ b/module/data/item/weapon.mjs
@@ -1,6 +1,5 @@
import AttachableItem from './attachableItem.mjs';
-import { actionsTypes } from '../action/_module.mjs';
-import ActionField from '../fields/actionField.mjs';
+import { ActionsField, ActionField } from '../fields/actionField.mjs';
export default class DHWeapon extends AttachableItem {
/** @inheritDoc */
@@ -9,8 +8,8 @@ export default class DHWeapon extends AttachableItem {
label: 'TYPES.Item.weapon',
type: 'weapon',
hasDescription: true,
- isInventoryItem: true
- // hasInitialAction: true
+ isInventoryItem: true,
+ hasActions: true
});
}
@@ -64,8 +63,7 @@ export default class DHWeapon extends AttachableItem {
]
}
}
- }),
- actions: new fields.ArrayField(new ActionField())
+ })
};
}
@@ -95,7 +93,10 @@ export default class DHWeapon extends AttachableItem {
}
await this.parent.deleteEmbeddedDocuments('ActiveEffect', removedEffectsUpdate);
- changes.system.actions = this.actions.filter(x => !removedActionsUpdate.includes(x._id));
+ changes.system.actions = removedActionsUpdate.reduce((acc, id) => {
+ acc[`-=${id}`] = null;
+ return acc;
+ }, {});
for (let weaponFeature of added) {
const featureData = CONFIG.DH.ITEM.weaponFeatures[weaponFeature.value];
@@ -110,7 +111,7 @@ export default class DHWeapon extends AttachableItem {
weaponFeature.effectIds = embeddedItems.map(x => x.id);
}
- const newActions = [];
+ const newActions = {};
if (featureData.actions?.length > 0) {
for (let action of featureData.actions) {
const embeddedEffects = await this.parent.createEmbeddedDocuments(
@@ -122,24 +123,25 @@ export default class DHWeapon extends AttachableItem {
description: game.i18n.localize(effect.description)
}))
);
- const cls = actionsTypes[action.type];
- newActions.push(
- new cls(
- {
- ...action,
- _id: foundry.utils.randomID(),
- name: game.i18n.localize(action.name),
- description: game.i18n.localize(action.description),
- effects: embeddedEffects.map(x => ({ _id: x.id }))
- },
- { parent: this }
- )
+
+ const cls = game.system.api.models.actions.actionsTypes[action.type];
+ const actionId = foundry.utils.randomID();
+ newActions[actionId] = new cls(
+ {
+ ...cls.getSourceConfig(this),
+ ...action,
+ _id: actionId,
+ name: game.i18n.localize(action.name),
+ description: game.i18n.localize(action.description),
+ effects: embeddedEffects.map(x => ({ _id: x.id }))
+ },
+ { parent: this }
);
}
}
- changes.system.actions = [...this.actions, ...newActions];
- weaponFeature.actionIds = newActions.map(x => x._id);
+ changes.system.actions = newActions;
+ weaponFeature.actionIds = Object.keys(newActions);
}
}
}
diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs
index 34fe77d7..4f5d9172 100644
--- a/module/dice/damageRoll.mjs
+++ b/module/dice/damageRoll.mjs
@@ -12,6 +12,7 @@ export default class DamageRoll extends DHRoll {
static async buildEvaluate(roll, config = {}, message = {}) {
if (config.evaluate !== false) {
+ if(config.dialog.configure === false) roll.constructFormula(config);
for (const roll of config.roll) await roll.roll.evaluate();
}
roll._evaluated = true;
diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs
index 52759316..4bc1a5bd 100644
--- a/module/dice/dhRoll.mjs
+++ b/module/dice/dhRoll.mjs
@@ -56,13 +56,13 @@ export default class DHRoll extends Roll {
}
// Create Chat Message
+ if (roll instanceof CONFIG.Dice.daggerheart.DamageRoll && Object.values(config.roll)?.length) {
+ const pool = foundry.dice.terms.PoolTerm.fromRolls(
+ Object.values(config.roll).flatMap(r => r.parts.map(p => p.roll))
+ );
+ roll = Roll.fromTerms([pool]);
+ }
if (config.source?.message) {
- if (Object.values(config.roll)?.length) {
- const pool = foundry.dice.terms.PoolTerm.fromRolls(
- Object.values(config.roll).flatMap(r => r.parts.map(p => p.roll))
- );
- roll = Roll.fromTerms([pool]);
- }
if (game.modules.get('dice-so-nice')?.active) await game.dice3d.showForRoll(roll, game.user, true);
} else config.message = await this.toMessage(roll, config);
}
diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs
index 490f53eb..f0b374f7 100644
--- a/module/documents/actor.mjs
+++ b/module/documents/actor.mjs
@@ -23,6 +23,23 @@ export default class DhpActor extends Actor {
return this.system.metadata.isNPC;
}
+ /** @inheritDoc */
+ getEmbeddedDocument(embeddedName, id, options) {
+ let doc;
+ switch ( embeddedName ) {
+ case "Action":
+ doc = this.system.actions?.get(id);
+ if(!doc && this.system.attack?.id === id) doc = this.system.attack;
+ break;
+ default:
+ return super.getEmbeddedDocument(embeddedName, id, options);
+ }
+ if ( options?.strict && !doc ) {
+ throw new Error(`The key ${id} does not exist in the ${embeddedName} Collection`);
+ }
+ return doc;
+ }
+
async _preCreate(data, options, user) {
if ((await super._preCreate(data, options, user)) === false) return false;
diff --git a/module/documents/item.mjs b/module/documents/item.mjs
index 6c3732db..4bad4fdc 100644
--- a/module/documents/item.mjs
+++ b/module/documents/item.mjs
@@ -9,6 +9,23 @@ export default class DHItem extends foundry.documents.Item {
for (const action of this.system.actions ?? []) action.prepareData();
}
+ /** @inheritDoc */
+ getEmbeddedDocument(embeddedName, id, options) {
+ let doc;
+ switch (embeddedName) {
+ case 'Action':
+ doc = this.system.actions?.get(id);
+ if (!doc && this.system.attack?.id === id) doc = this.system.attack;
+ break;
+ default:
+ return super.getEmbeddedDocument(embeddedName, id, options);
+ }
+ if (options?.strict && !doc) {
+ throw new Error(`The key ${id} does not exist in the ${embeddedName} Collection`);
+ }
+ return doc;
+ }
+
/**
* @inheritdoc
* @param {object} options - Options which modify the getRollData method.
@@ -106,10 +123,10 @@ export default class DHItem extends foundry.documents.Item {
}
async use(event) {
- const actions = this.system.actionsList;
- if (actions?.length) {
- let action = actions[0];
- if (actions.length > 1 && !event?.shiftKey) {
+ const actions = new Set(this.system.actionsList);
+ if (actions?.size) {
+ let action = actions.first();
+ if (actions.size > 1 && !event?.shiftKey) {
// Actions Choice Dialog
action = await this.selectActionDialog(event);
}
diff --git a/module/documents/tooltipManager.mjs b/module/documents/tooltipManager.mjs
index 8e1c729e..71dd71d2 100644
--- a/module/documents/tooltipManager.mjs
+++ b/module/documents/tooltipManager.mjs
@@ -4,19 +4,18 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
let html = options.html;
if (element.dataset.tooltip?.startsWith('#item#')) {
- const splitValues = element.dataset.tooltip.slice(6).split('#action#');
- const itemUuid = splitValues[0];
- const actionId = splitValues.length > 1 ? splitValues[1] : null;
-
- const baseItem = await foundry.utils.fromUuid(itemUuid);
- const item = actionId ? baseItem.system.actions.find(x => x.id === actionId) : baseItem;
+ const itemUuid = element.dataset.tooltip.slice(6);
+ const item = await foundry.utils.fromUuid(itemUuid);
if (item) {
- const type = actionId ? 'action' : item.type;
- const description = await TextEditor.enrichHTML(item.system.description);
- for (let feature of item.system.features) {
- feature.system.enrichedDescription = await TextEditor.enrichHTML(feature.system.description);
+ const isAction = item instanceof game.system.api.models.actions.actionsTypes.base;
+ const description = await TextEditor.enrichHTML(isAction ? item.description : item.system.description);
+ if (item.system?.features) {
+ for (let feature of item.system.features) {
+ feature.system.enrichedDescription = await TextEditor.enrichHTML(feature.system.description);
+ }
}
+ const type = isAction ? 'action' : item.type;
html = await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/ui/tooltip/${type}.hbs`,
{
diff --git a/module/helpers/handlebarsHelper.mjs b/module/helpers/handlebarsHelper.mjs
index 0c919191..9e769d2e 100644
--- a/module/helpers/handlebarsHelper.mjs
+++ b/module/helpers/handlebarsHelper.mjs
@@ -9,7 +9,8 @@ export default class RegisterHandlebarsHelpers {
damageFormula: this.damageFormula,
damageSymbols: this.damageSymbols,
rollParsed: this.rollParsed,
- hasProperty: foundry.utils.hasProperty
+ hasProperty: foundry.utils.hasProperty,
+ setVar: this.setVar
});
}
static add(a, b) {
@@ -50,4 +51,8 @@ export default class RegisterHandlebarsHelpers {
const result = itemAbleRollParse(value, actor, item);
return isNumerical && !result ? 0 : result;
}
+
+ static setVar(name, value, context) {
+ this[name] = value;
+ }
}
diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs
index 7e73695e..5ee52018 100644
--- a/module/helpers/utils.mjs
+++ b/module/helpers/utils.mjs
@@ -96,7 +96,7 @@ export const tagifyElement = (element, options, onChange, tagifyOptions = {}) =>
mapValueTo: 'name',
searchKeys: ['name'],
enabled: 0,
- maxItems: 20,
+ maxItems: 100,
closeOnSelect: true,
highlightFirst: false
},
diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less
index 29a2c1dd..4c319ed5 100755
--- a/styles/less/global/elements.less
+++ b/styles/less/global/elements.less
@@ -346,6 +346,12 @@
&:has(.list-w-img) {
gap: 0;
}
+
+ &.no-style {
+ border-width: 0;
+ margin: 0;
+ padding: 0;
+ }
}
.two-columns {
diff --git a/templates/actionTypes/damage.hbs b/templates/actionTypes/damage.hbs
index ad45fc33..9ae25e0e 100644
--- a/templates/actionTypes/damage.hbs
+++ b/templates/actionTypes/damage.hbs
@@ -4,90 +4,78 @@
{{localize "DAGGERHEART.GENERAL.damage"}}
{{#unless (eq path 'system.attack.')}}{{/unless}}
- {{#unless (or @root.isNPC path)}}
- {{#if @root.hasBaseDamage}}
- {{formField @root.fields.damage.fields.includeBase value=@root.source.damage.includeBase name="damage.includeBase" classes="checkbox" localize=true }}
- {{/if}}
- {{/unless}}
+ {{#if @root.hasBaseDamage}}
+ {{formField @root.fields.damage.fields.includeBase value=@root.source.damage.includeBase name="damage.includeBase" classes="checkbox" localize=true }}
+ {{/if}}
{{#each source.parts as |dmg index|}}
- {{#if (or @root.isNPC ../path)}}
- {{formField ../fields.value.fields.custom.fields.enabled value=dmg.value.custom.enabled name=(concat ../path "damage.parts." index ".value.custom.enabled") classes="checkbox"}}
-
- {{#if dmg.value.custom.enabled}}
- {{formField ../fields.value.fields.custom.fields.formula value=dmg.value.custom.formula name=(concat ../path "damage.parts." index ".value.custom.formula") localize=true}}
- {{else}}
-
- {{#if @root.isNPC}}{{formField ../fields.value.fields.flatMultiplier value=dmg.value.flatMultiplier name=(concat ../path "damage.parts." index ".value.flatMultiplier") label="DAGGERHEART.ACTIONS.Settings.multiplier" classes="inline-child" localize=true }}{{/if}}
- {{formField ../fields.value.fields.dice value=dmg.value.dice name=(concat ../path "damage.parts." index ".value.dice") classes="inline-child"}}
- {{formField ../fields.value.fields.bonus value=dmg.value.bonus name=(concat ../path "damage.parts." index ".value.bonus") localize=true classes="inline-child"}}
-
- {{/if}}
-
- {{formField ../fields.applyTo value=dmg.applyTo name=(concat ../path "damage.parts." realIndex ".applyTo") localize=true}}
- {{#if (eq dmg.applyTo 'hitPoints')}}
- {{formField ../fields.type value=dmg.type name=(concat ../path "damage.parts." index ".type") localize=true}}
- {{/if}}
-
- {{#if ../horde}}
-
- {{/if}}
+ {{#if (and @root.hasBaseDamage @root.source.damage.includeBase)}}
+ {{setVar 'realIndex' (add index -1)}}
{{else}}
- {{#with (@root.getRealIndex index) as | realIndex |}}
-
- {{/with}}
+ {{setVar 'realIndex' index}}
{{/if}}
+
{{/each}}
{{#*inline "formula"}}
{{#unless dmg.base}}
- {{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat "damage.parts." realIndex "." target ".custom.enabled") classes="checkbox"}}
+ {{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat path "damage.parts." realIndex "." target ".custom.enabled") classes="checkbox"}}
{{/unless}}
{{#if source.custom.enabled}}
- {{formField fields.custom.fields.formula value=source.custom.formula name=(concat "damage.parts." realIndex "." target ".custom.formula") localize=true}}
+ {{formField fields.custom.fields.formula value=source.custom.formula name=(concat path "damage.parts." realIndex "." target ".custom.formula") localize=true}}
{{else}}
- {{formField fields.multiplier value=source.multiplier name=(concat "damage.parts." realIndex "." target ".multiplier") localize=true}}
- {{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat "damage.parts." realIndex ".flatMultiplier") }}{{/if}}
- {{formField fields.dice value=source.dice name=(concat "damage.parts." realIndex "." target ".dice")}}
- {{formField fields.bonus value=source.bonus name=(concat "damage.parts." realIndex "." target ".bonus") localize=true}}
+ {{#unless @root.isNPC}}
+ {{formField fields.multiplier value=source.multiplier name=(concat path "damage.parts." realIndex "." target ".multiplier") localize=true}}
+ {{/unless}}
+ {{#if (eq source.multiplier 'flat')}}{{formField fields.flatMultiplier value=source.flatMultiplier name=(concat ../path "damage.parts." realIndex "." target ".flatMultiplier") }}{{/if}}
+ {{formField fields.dice value=source.dice name=(concat path "damage.parts." realIndex "." target ".dice")}}
+ {{formField fields.bonus value=source.bonus name=(concat path "damage.parts." realIndex "." target ".bonus") localize=true}}
{{/if}}
+ {{#if @root.isNPC}}
+
+ {{/if}}
{{/inline}}
\ No newline at end of file
diff --git a/templates/sheets-settings/adversary-settings/attack.hbs b/templates/sheets-settings/adversary-settings/attack.hbs
index bdb6da5b..a51128a1 100644
--- a/templates/sheets-settings/adversary-settings/attack.hbs
+++ b/templates/sheets-settings/adversary-settings/attack.hbs
@@ -19,7 +19,5 @@
{{/if}}
{{/if}}
- {{#if (eq document.system.type 'horde')}}
- {{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields.parts.element.fields source=document.system.attack.damage path="system.attack." horde=true}}
- {{/if}}
+ {{> 'systems/daggerheart/templates/actionTypes/damage.hbs' fields=systemFields.attack.fields.damage.fields.parts.element.fields source=document.system.attack.damage path="system.attack." horde=(eq document.system.type 'horde')}}
\ No newline at end of file
diff --git a/templates/sheets/actors/adversary/sidebar.hbs b/templates/sheets/actors/adversary/sidebar.hbs
index 131fb33b..0bb1f129 100644
--- a/templates/sheets/actors/adversary/sidebar.hbs
+++ b/templates/sheets/actors/adversary/sidebar.hbs
@@ -54,7 +54,7 @@
{{/if}}
-
{{localize DAGGERHEART.GENERAL.difficulty}}
+ {{localize "DAGGERHEART.GENERAL.difficulty"}}
diff --git a/templates/sheets/global/partials/inventory-item-V2.hbs b/templates/sheets/global/partials/inventory-item-V2.hbs
index 73ae5ae7..71b06202 100644
--- a/templates/sheets/global/partials/inventory-item-V2.hbs
+++ b/templates/sheets/global/partials/inventory-item-V2.hbs
@@ -249,7 +249,7 @@ Parameters:
{{#if (and showActions (eq item.type 'feature'))}}
{{#each item.system.actions as | action |}}
-