} 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);
+ }
+}
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 a35ea452..291403c5 100644
--- a/module/data/item/base.mjs
+++ b/module/data/item/base.mjs
@@ -9,6 +9,7 @@
*/
import { addLinkedItemsDiff, updateLinkedItemApps } from '../../helpers/utils.mjs';
+import { ActionsField } from '../fields/actionField.mjs';
const fields = foundry.data.fields;
@@ -23,7 +24,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
hasDescription: false,
hasResource: false,
isQuantifiable: false,
- isInventoryItem: false
+ isInventoryItem: false,
+ hasActions: false
};
}
@@ -71,6 +73,8 @@ 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/d20Roll.mjs b/module/dice/d20Roll.mjs
index 5f95b80d..dcf0143a 100644
--- a/module/dice/d20Roll.mjs
+++ b/module/dice/d20Roll.mjs
@@ -1,4 +1,5 @@
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
+import { getDiceSoNicePresets } from '../config/generalConfig.mjs';
import DHRoll from './dhRoll.mjs';
export default class D20Roll extends DHRoll {
@@ -156,6 +157,14 @@ export default class D20Roll extends DHRoll {
dice: roll.dAdvantage?.denomination,
value: roll.dAdvantage?.total
};
+ data.dice = data.dice.map(dice => ({
+ ...dice,
+ results: dice.results.filter(x => !x.rerolled),
+ rerolled: {
+ any: dice.results.some(x => x.rerolled),
+ rerolls: dice.results.filter(x => x.rerolled)
+ }
+ }));
data.isCritical = roll.isCritical;
data.extra = roll.dice
.filter(d => !roll.baseTerms.includes(d))
@@ -188,6 +197,26 @@ export default class D20Roll extends DHRoll {
await game.dice3d.showForRoll(parsedRoll, game.user, true);
}
- return { newRoll, parsedRoll };
+ const rerolled = {
+ any: true,
+ rerolls: [
+ ...(message.system.roll.dice[0].rerolled?.rerolls?.length > 0
+ ? [message.system.roll.dice[0].rerolled?.rerolls]
+ : []),
+ rollString.terms[0].results
+ ]
+ };
+ return {
+ newRoll: {
+ ...newRoll,
+ dice: [
+ {
+ ...newRoll.dice[0],
+ rerolled: rerolled
+ }
+ ]
+ },
+ parsedRoll
+ };
}
}
diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs
index 34fe77d7..49aa3551 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 c50c126a..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);
}
@@ -193,6 +193,11 @@ export const registerRollDiceHooks = () => {
if (config.roll.isCritical) updates.push({ key: 'stress', value: -1 });
if (config.roll.result.duality === -1) updates.push({ key: 'fear', value: 1 });
+ if (config.rerolledRoll.isCritical || config.rerolledRoll.result.duality === 1)
+ updates.push({ key: 'hope', value: -1 });
+ if (config.rerolledRoll.isCritical) updates.push({ key: 'stress', value: 1 });
+ if (config.rerolledRoll.result.duality === -1) updates.push({ key: 'fear', value: -1 });
+
if (updates.length) {
const target = actor.system.partner ?? actor;
if (!['dead', 'unconcious'].some(x => actor.statuses.has(x))) {
diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs
index 151d63df..1c414a63 100644
--- a/module/dice/dualityRoll.mjs
+++ b/module/dice/dualityRoll.mjs
@@ -171,11 +171,19 @@ export default class DualityRoll extends D20Roll {
data.hope = {
dice: roll.dHope.denomination,
- value: roll.dHope.total
+ value: roll.dHope.total,
+ rerolled: {
+ any: roll.dHope.results.some(x => x.rerolled),
+ rerolls: roll.dHope.results.filter(x => x.rerolled)
+ }
};
data.fear = {
dice: roll.dFear.denomination,
- value: roll.dFear.total
+ value: roll.dFear.total,
+ rerolled: {
+ any: roll.dFear.results.some(x => x.rerolled),
+ rerolls: roll.dFear.results.filter(x => x.rerolled)
+ }
};
data.rally = {
dice: roll.dRally?.denomination,
@@ -232,6 +240,13 @@ export default class DualityRoll extends D20Roll {
});
newRoll.extra = newRoll.extra.slice(2);
+ Hooks.call(`${CONFIG.DH.id}.postRollDuality`, {
+ source: { actor: message.system.source.actor ?? '' },
+ targets: message.system.targets,
+ roll: newRoll,
+ rerolledRoll:
+ newRoll.result.duality !== message.system.roll.result.duality ? message.system.roll : undefined
+ });
return { newRoll, parsedRoll };
}
}
diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs
index 490f53eb..2fa34b63 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 ab3d40ea..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 88189feb..25f3748a 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/src/packs/LICENSE b/src/packs/LICENSE
new file mode 100644
index 00000000..9a2a5c9f
--- /dev/null
+++ b/src/packs/LICENSE
@@ -0,0 +1,7 @@
+This product includes materials from the
+Daggerheart System Reference Document 1.0, ©
+Critical Role, LLC. under the terms of the
+Darrington Press Community Gaming (DPCGL)
+License. More information can be found at
+https://www.daggerheart.com/. There are no
+previous modifications by others.
\ No newline at end of file
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/styles/less/sheets/actors/character/effects.less b/styles/less/sheets/actors/character/effects.less
new file mode 100644
index 00000000..387b831b
--- /dev/null
+++ b/styles/less/sheets/actors/character/effects.less
@@ -0,0 +1,19 @@
+@import '../../../utils/colors.less';
+@import '../../../utils/fonts.less';
+
+.application.sheet.daggerheart.actor.dh-style.character {
+ .tab.effects {
+ .effects-sections {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ overflow-y: auto;
+ mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%);
+ padding: 20px 0;
+ padding-top: 10px;
+
+ scrollbar-width: thin;
+ scrollbar-color: light-dark(@dark-blue, @golden) transparent;
+ }
+ }
+}
diff --git a/styles/less/sheets/actors/character/features.less b/styles/less/sheets/actors/character/features.less
index c2feb6b1..af53e11d 100644
--- a/styles/less/sheets/actors/character/features.less
+++ b/styles/less/sheets/actors/character/features.less
@@ -11,7 +11,6 @@
mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%);
padding: 20px 0;
padding-top: 10px;
- height: 84%;
scrollbar-width: thin;
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
diff --git a/styles/less/sheets/actors/character/inventory.less b/styles/less/sheets/actors/character/inventory.less
index eac1065d..48bdd682 100644
--- a/styles/less/sheets/actors/character/inventory.less
+++ b/styles/less/sheets/actors/character/inventory.less
@@ -56,7 +56,6 @@
overflow-y: auto;
mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%);
padding: 20px 0;
- height: 73%;
scrollbar-width: thin;
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
diff --git a/styles/less/sheets/actors/character/loadout.less b/styles/less/sheets/actors/character/loadout.less
index b7863e77..98fe9eed 100644
--- a/styles/less/sheets/actors/character/loadout.less
+++ b/styles/less/sheets/actors/character/loadout.less
@@ -97,7 +97,6 @@
overflow-y: auto;
mask-image: linear-gradient(0deg, transparent 0%, black 10%, black 98%, transparent 100%);
padding: 20px 0;
- height: 84%;
scrollbar-width: thin;
scrollbar-color: light-dark(@dark-blue, @golden) transparent;
diff --git a/styles/less/sheets/actors/character/sheet.less b/styles/less/sheets/actors/character/sheet.less
index 3d19a3b2..f09ee541 100644
--- a/styles/less/sheets/actors/character/sheet.less
+++ b/styles/less/sheets/actors/character/sheet.less
@@ -11,6 +11,7 @@
width: 100%;
padding-bottom: 0;
overflow-x: auto;
+ margin-bottom: 0;
.character-sidebar-sheet {
grid-row: 1 / span 2;
@@ -25,6 +26,11 @@
.tab {
grid-row: 2;
grid-column: 2;
+ &.active {
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ }
}
}
}
diff --git a/styles/less/sheets/actors/character/sidebar.less b/styles/less/sheets/actors/character/sidebar.less
index 2c88d14f..7cfeffac 100644
--- a/styles/less/sheets/actors/character/sidebar.less
+++ b/styles/less/sheets/actors/character/sidebar.less
@@ -63,7 +63,7 @@
flex-direction: column;
top: -20px;
gap: 30px;
- margin-bottom: -10px;
+ margin-bottom: -16px;
.resources-section {
display: flex;
@@ -357,54 +357,39 @@
}
}
- .equipment-section {
- .title {
- display: flex;
- gap: 15px;
- align-items: center;
+ .shortcut-items-section {
+ overflow-y: hidden;
+ max-height: 56%;
+ padding-top: 16px;
+ padding-bottom: 20px;
+ mask-image: linear-gradient(0deg, transparent 0%, black 5%, black 95%, transparent 100%);
- h3 {
- font-size: 20px;
- }
- }
- .items-list {
- display: flex;
- flex-direction: column;
- gap: 10px;
- align-items: center;
+ &:hover {
+ overflow-y: auto;
+ scrollbar-width: thin;
+ scrollbar-color: light-dark(@dark-blue, @golden) transparent;
}
}
- .loadout-section {
+ .equipment-section,
+ .loadout-section,
+ .experience-section {
.title {
- display: flex;
- gap: 15px;
- align-items: center;
+ .section-title();
+ }
+ }
- h3 {
- font-size: 20px;
- }
+ .equipment-section {
+ .items-list {
+ .column-list(10px);
}
}
.experience-section {
- .title {
- display: flex;
- gap: 15px;
- align-items: center;
-
- h3 {
- font-size: 20px;
- }
- }
-
.experience-list {
- display: flex;
- flex-direction: column;
- gap: 5px;
+ .column-list(5px);
width: 100%;
margin-top: 10px;
- align-items: center;
.experience-row {
display: flex;
diff --git a/styles/less/sheets/index.less b/styles/less/sheets/index.less
index 3470de37..aa2f4356 100644
--- a/styles/less/sheets/index.less
+++ b/styles/less/sheets/index.less
@@ -4,6 +4,7 @@
@import './actors/adversary/sidebar.less';
@import './actors/character/biography.less';
+@import './actors/character/effects.less';
@import './actors/character/features.less';
@import './actors/character/header.less';
@import './actors/character/inventory.less';
diff --git a/styles/less/ui/chat/chat.less b/styles/less/ui/chat/chat.less
index c593a919..4558cf8e 100644
--- a/styles/less/ui/chat/chat.less
+++ b/styles/less/ui/chat/chat.less
@@ -62,6 +62,17 @@
}
&.rerollable {
+ position: relative;
+ flex: none;
+
+ .dice-rerolled {
+ z-index: 2;
+ position: absolute;
+ right: 0;
+ font-size: 12px;
+ cursor: help;
+ }
+
.reroll-button {
border: none;
background: initial;
@@ -85,12 +96,21 @@
display: flex;
flex-direction: column;
gap: 2px;
+ position: relative;
.dice-title {
color: var(--color-light-1);
text-shadow: 0 0 1px black;
}
+ .dice-rerolled {
+ z-index: 2;
+ position: absolute;
+ right: -2px;
+ font-size: 12px;
+ cursor: help;
+ }
+
.dice-inner-container {
display: flex;
align-items: center;
diff --git a/styles/less/utils/mixin.less b/styles/less/utils/mixin.less
index 39c2bee0..0e52fa82 100644
--- a/styles/less/utils/mixin.less
+++ b/styles/less/utils/mixin.less
@@ -20,3 +20,27 @@
@lightRules();
}
}
+
+/**
+ * Apply a style to sidebar titles.
+ */
+.section-title() {
+ display: flex;
+ gap: 15px;
+ align-items: center;
+
+ h3 {
+ font-size: 20px;
+ }
+}
+
+/**
+ * Apply default item list style.
+ * @param {Length} @gap - The vertical spacing between elements (e.g., 10px, 1rem)
+ */
+.column-list(@gap: 10px) {
+ display: flex;
+ flex-direction: column;
+ gap: @gap;
+ align-items: center;
+}
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}}
-
- {{localize "DAGGERHEART.ACTORS.Adversary.hordeDamage"}}
-
- {{formField ../fields.valueAlt.fields.flatMultiplier value=dmg.valueAlt.flatMultiplier name=(concat ../path "damage.parts." index ".valueAlt.flatMultiplier") label="DAGGERHEART.ACTIONS.Settings.multiplier" classes="inline-child" localize=true }}
- {{formField ../fields.valueAlt.fields.dice value=dmg.valueAlt.dice name=(concat ../path "damage.parts." index ".valueAlt.dice") classes="inline-child"}}
- {{formField ../fields.valueAlt.fields.bonus value=dmg.valueAlt.bonus name=(concat ../path "damage.parts." index ".valueAlt.bonus") localize=true classes="inline-child"}}
-
-
- {{/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/dialogs/ownershipSelection.hbs b/templates/dialogs/ownershipSelection.hbs
index 5c1def0e..43711c07 100644
--- a/templates/dialogs/ownershipSelection.hbs
+++ b/templates/dialogs/ownershipSelection.hbs
@@ -1,7 +1,7 @@
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/actors/character/effects.hbs b/templates/sheets/actors/character/effects.hbs
index 4c4fca27..30fb31f4 100644
--- a/templates/sheets/actors/character/effects.hbs
+++ b/templates/sheets/actors/character/effects.hbs
@@ -1,21 +1,23 @@
- {{> 'daggerheart.inventory-items'
- title='DAGGERHEART.GENERAL.activeEffects'
- type='effect'
- isGlassy=true
- collection=effects.actives
- canCreate=true
- hideResources=true
- }}
+
+ {{> 'daggerheart.inventory-items'
+ title='DAGGERHEART.GENERAL.activeEffects'
+ type='effect'
+ isGlassy=true
+ collection=effects.actives
+ canCreate=true
+ hideResources=true
+ }}
- {{> 'daggerheart.inventory-items'
- title='DAGGERHEART.GENERAL.inactiveEffects'
- type='effect'
- isGlassy=true
- collection=effects.inactives
- canCreate=true
- hideResources=true
- }}
+ {{> 'daggerheart.inventory-items'
+ title='DAGGERHEART.GENERAL.inactiveEffects'
+ type='effect'
+ isGlassy=true
+ collection=effects.inactives
+ canCreate=true
+ hideResources=true
+ }}
+
\ No newline at end of file
diff --git a/templates/sheets/actors/character/sidebar.hbs b/templates/sheets/actors/character/sidebar.hbs
index 6ed47fc7..860f50cc 100644
--- a/templates/sheets/actors/character/sidebar.hbs
+++ b/templates/sheets/actors/character/sidebar.hbs
@@ -88,71 +88,74 @@
-
-
-
-
{{localize "DAGGERHEART.GENERAL.equipment"}}
-
-
-
-
-
-
-
-
{{localize "DAGGERHEART.GENERAL.loadout"}}
-
-
-