From 07b7c8209440dfc350c1b668ab95487d7cfcf799 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Tue, 23 Jun 2026 17:26:53 -0400 Subject: [PATCH 01/25] Fetch origin fetch when in compendium (#2042) --- module/documents/activeEffect.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index 083b3950..cdfc9a52 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -225,7 +225,7 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { */ _getTags() { const tags = []; - const originActor = DhActiveEffect.#resolveParentDocument(fromUuidSync(this.origin), Actor); + const originActor = DhActiveEffect.#resolveParentDocument(fromUuidSync(this.origin, { strict: false }), Actor); if (originActor && originActor !== this.actor) { tags.push(_loc('DAGGERHEART.EFFECTS.OriginTag', { name: originActor.name })); } else if (!(this.parent instanceof Actor)) { From ca82cbcf669240ba6034d0200f84781df5e185de Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Tue, 23 Jun 2026 23:28:04 +0200 Subject: [PATCH 02/25] . (#2041) --- module/documents/combat.mjs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/module/documents/combat.mjs b/module/documents/combat.mjs index 20996b77..e74127e9 100644 --- a/module/documents/combat.mjs +++ b/module/documents/combat.mjs @@ -46,7 +46,9 @@ export default class DhpCombat extends Combat { for (let actor of actors) { await actor.createEmbeddedDocuments( 'ActiveEffect', - effects.filter(x => x.effectTargetTypes.includes(actor.type)) + effects + .filter(x => x.effectTargetTypes.includes(actor.type)) + .map(x => foundry.utils.deepClone(x)) ); } } else { From 2c1f52413d60004c8d4933caf586258ff4baf1cd Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 23 Jun 2026 23:33:29 +0200 Subject: [PATCH 03/25] Raised verison --- system.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system.json b/system.json index 08693074..4660a196 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "2.4.0", + "version": "2.4.1", "compatibility": { "minimum": "14.364", "verified": "14.364", @@ -10,7 +10,7 @@ }, "url": "https://github.com/Foundryborne/daggerheart", "manifest": "https://raw.githubusercontent.com/Foundryborne/daggerheart/v14/system.json", - "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.4.0/system.zip", + "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.4.1/system.zip", "authors": [ { "name": "WBHarry" From 1ebbad47973b631bb6779340e5cab85dd4532a7a Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Wed, 24 Jun 2026 17:37:00 -0400 Subject: [PATCH 04/25] [Fix] quirks involving expanding items and hovering over them (#2033) --- .../sheets/api/application-mixin.mjs | 2 +- module/data/action/baseAction.mjs | 4 + module/data/item/armor.mjs | 8 +- module/data/item/weapon.mjs | 8 +- module/documents/activeEffect.mjs | 4 + module/documents/item.mjs | 4 + styles/less/global/inventory-item.less | 28 +- .../global/partials/inventory-item-V2.hbs | 266 +++++++++--------- 8 files changed, 163 insertions(+), 161 deletions(-) diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 98f38f03..0168f46d 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -603,7 +603,7 @@ export default function DHApplicationMixin(Base) { const doc = await fromUuid(itemUuid); //get inventory-item description element - const descriptionElement = el.querySelector('.invetory-description'); + const descriptionElement = el.querySelector('.inventory-description'); if (!doc || !descriptionElement) continue; // localize the description (idk if it's still necessary) diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 27383b7a..f3008704 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -54,6 +54,10 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return {}; } + get hasDescription() { + return Boolean(this.description); + } + /** * Create a Map containing each Action step based on fields define in schema. Ordered by Fields order property. * diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index 21c56f9a..15bb620d 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -52,6 +52,10 @@ export default class DHArmor extends AttachableItem { ); } + get itemFeatures() { + return this.armorFeatures; + } + /**@inheritdoc */ async getDescriptionData() { const baseDescription = this.description; @@ -169,8 +173,4 @@ export default class DHArmor extends AttachableItem { const labels = [`${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.armor.max}`]; return labels; } - - get itemFeatures() { - return this.armorFeatures; - } } diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index 84e4de7f..39c0fc8e 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -113,6 +113,10 @@ export default class DHWeapon extends AttachableItem { ); } + get itemFeatures() { + return this.weaponFeatures; + } + /**@inheritdoc */ async getDescriptionData() { const baseDescription = this.description; @@ -269,8 +273,4 @@ export default class DHWeapon extends AttachableItem { return labels; } - - get itemFeatures() { - return this.weaponFeatures; - } } diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index cdfc9a52..4a9f3cc4 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -65,6 +65,10 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { ); } + get hasDescription() { + return Boolean(this.description); + } + /* -------------------------------------------- */ /* Event Handlers */ /* -------------------------------------------- */ diff --git a/module/documents/item.mjs b/module/documents/item.mjs index 32543ebd..14717538 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -89,6 +89,10 @@ export default class DHItem extends foundry.documents.Item { return !pack?.locked && this.isOwner && isValidType && hasActions; } + get hasDescription() { + return Boolean(this.system.description) || Boolean(this.system.itemFeatures?.length); + } + /** @inheritdoc */ static async createDialog(data = {}, createOptions = {}, options = {}) { const { folders, types, template, context = {}, ...dialogOptions } = options; diff --git a/styles/less/global/inventory-item.less b/styles/less/global/inventory-item.less index 3a5a9321..fc73ba95 100644 --- a/styles/less/global/inventory-item.less +++ b/styles/less/global/inventory-item.less @@ -43,16 +43,19 @@ } } + .item-main { + border-radius: 5px; + padding: 2px; + margin: -2px; + } + &:hover { .inventory-item-header .item-label .item-name .expanded-icon { margin-left: 10px; display: inline-block; } - &:has(.inventory-item-content.extensible) { - .inventory-item-header, - .inventory-item-content { - background: light-dark(@dark-blue-40, @golden-40); - } + .item-main { + background: light-dark(@dark-blue-40, @golden-40); } &:has(.inventory-item-content.extended) { .inventory-item-header .item-label .item-name .expanded-icon { @@ -60,19 +63,6 @@ } } } - - &:has(.inventory-item-content.extensible) { - .inventory-item-header { - border-radius: 5px 5px 0 0; - } - .inventory-item-content { - border-radius: 0 0 5px 5px; - } - } - - &:not(:has(.inventory-item-content.extensible)) .inventory-item-header { - border-radius: 5px; - } } .inventory-item-header, @@ -171,7 +161,7 @@ grid-template-rows: 1fr; padding-top: 4px; } - .invetory-description { + .inventory-description { overflow: hidden; h1 { diff --git a/templates/sheets/global/partials/inventory-item-V2.hbs b/templates/sheets/global/partials/inventory-item-V2.hbs index f7d22a30..775690d4 100644 --- a/templates/sheets/global/partials/inventory-item-V2.hbs +++ b/templates/sheets/global/partials/inventory-item-V2.hbs @@ -25,146 +25,146 @@ Parameters: data-type="{{type}}" data-item-type="{{item.type}}" data-item-uuid="{{item.uuid}}" data-no-compendium-edit="{{noCompendiumEdit}}" > -
- {{!-- Image --}} -
- - {{#if (and item.usable (ne showActions false))}} - {{#if @root.isNPC}} - d20 - {{else}} - 2d12 - {{/if}} - {{/if}} -
- - {{!-- Name & Tags --}} -
- {{!-- Item Name --}} - {{localize item.name}} {{#unless (or noExtensible (not item.system.description))}}{{/unless}} - - {{!-- Tags Start --}} - {{#if (not hideTags)}} - {{#> "systems/daggerheart/templates/sheets/global/partials/item-tags.hbs" item}} - {{#if (eq ../type 'feature')}} - {{#if (and system.featureForm (ne @root.document.type "character"))}} -
- {{localize (concat "DAGGERHEART.CONFIG.FeatureForm." system.featureForm)}} -
+
+
+ {{!-- Image --}} +
+ + {{#if (and item.usable (ne showActions false))}} + {{#if @root.isNPC}} + d20 + {{else}} + 2d12 {{/if}} {{/if}} - {{/ "systems/daggerheart/templates/sheets/global/partials/item-tags.hbs"}} - {{/if}} +
- {{!--Tags End --}} -
+ {{!-- Name & Tags --}} +
+ {{!-- Item Name --}} + {{localize item.name}} {{#unless (or noExtensible (not item.hasDescription))}}{{/unless}} - {{!-- Simple Resource --}} - {{#if (and (not hideResources) (not (eq item.system.resource.type 'diceValue')))}} - {{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}} - {{/if}} - {{#if (or isQuantifiable (or (eq item.system.quantity 0) (gt item.system.quantity 1)))}} -
- + {{!-- Tags Start --}} + {{#if (not hideTags)}} + {{#> "systems/daggerheart/templates/sheets/global/partials/item-tags.hbs" item}} + {{#if (and (eq ../type 'feature') system.featureForm (ne @root.document.type "character"))}} +
+ {{localize (concat "DAGGERHEART.CONFIG.FeatureForm." system.featureForm)}} +
+ {{/if}} + {{/ "systems/daggerheart/templates/sheets/global/partials/item-tags.hbs"}} + {{/if}} + + {{!--Tags End --}} +
+ + {{!-- Simple Resource --}} + {{#if (and (not hideResources) (not (eq item.system.resource.type 'diceValue')))}} + {{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}} + {{/if}} + {{#if (or isQuantifiable (or (eq item.system.quantity 0) (gt item.system.quantity 1)))}} +
+ +
+ {{/if}} + + {{!-- Controls --}} + {{#unless hideControls}} +
+ {{!-- Toggle/Equip buttons --}} + {{#if @root.editable}} + {{#if (and (eq actorType 'character') (eq type 'weapon'))}} + + + + {{/if}} + {{#if (and (eq actorType 'character') (eq type 'armor'))}} + + + + {{/if}} + {{#if (and (eq type 'domainCard'))}} + + + + {{/if}} + {{#if (and (and (eq type 'effect') (not (eq item.type 'beastform'))))}} + + + + {{/if}} + {{/if}} + + {{!-- Send to Chat --}} + {{#if (hasProperty item "toChat")}} + + + + {{/if}} + + {{!-- Document management buttons or context menu --}} + {{#if (and (not isActor) (not hideContextMenu))}} + + + + {{else if (and @root.editable (not hideModifyControls))}} + + + + {{#if (not isActor)}} + + + + {{else if (eq type 'adversary')}} + + + + {{/if}} + {{/if}} +
+ {{/unless}}
+ {{#unless hideDescription}} +
+ {{!-- Description --}} +
+
+ {{/unless}} +
+ {{!-- Dice Resource --}} + {{#if (and (not hideResources) (eq item.system.resource.type 'diceValue'))}} + {{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}} {{/if}} - - {{!-- Controls --}} - {{#unless hideControls}} -
- {{!-- Toggle/Equip buttons --}} - {{#if @root.editable}} - {{#if (and (eq actorType 'character') (eq type 'weapon'))}} - - - - {{/if}} - {{#if (and (eq actorType 'character') (eq type 'armor'))}} - - - - {{/if}} - {{#if (and (eq type 'domainCard'))}} - - - - {{/if}} - {{#if (and (and (eq type 'effect') (not (eq item.type 'beastform'))))}} - - - - {{/if}} + {{!-- Actions Buttons --}} + {{#if (and showActions item.system.actions.size)}} +
+ {{#each item.system.actions as | action |}} +
+ {{#if (and (eq action.type 'beastform') @root.beastformActive)}} + + {{else}} + {{/if}} - - {{!-- Send to Chat --}} - {{#if (hasProperty item "toChat")}} - - - - {{/if}} - - {{!-- Document management buttons or context menu --}} - {{#if (and (not isActor) (not hideContextMenu))}} - - - - {{else if (and @root.editable (not hideModifyControls))}} - - - - {{#if (not isActor)}} - - - - {{else if (eq type 'adversary')}} - - - - {{/if}} + {{#if action.uses.max}} +
+ {{/if}}
- {{/unless}} -
-
- {{!-- Description --}} - {{#unless hideDescription}} -
- {{/unless}} -
- {{!-- Dice Resource --}} - {{#if (and (not hideResources) (eq item.system.resource.type 'diceValue'))}} - {{> "systems/daggerheart/templates/sheets/global/partials/item-resource.hbs"}} - {{/if}} - {{!-- Actions Buttons --}} - {{#if (and showActions item.system.actions.size)}} -
- {{#each item.system.actions as | action |}} -
- {{#if (and (eq action.type 'beastform') @root.beastformActive)}} - - {{else}} - - {{/if}} - {{#if action.uses.max}} -
- - {{/if}} + {{/each}}
- {{/each}} -
- {{/if}} - \ No newline at end of file + {{/if}} + From 9c58f7058e3ba99af32bb6ebfbc4af26b59d91d6 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Mon, 29 Jun 2026 14:22:12 +0200 Subject: [PATCH 05/25] [Fix] V13 Migration Fixes (#2044) * Fixed so that damageParts without an applyTo field is assumed to be hitPoints * Added the applyTo field to the basic attack for Adversaries and Companions * Tentative blindfix to issues of entities being null. Can't replicate it * Added some safety for missing things if someone was on a REALLY old system version and then updated all the way * Moved v13 countdown migration over to a migrateData * . --- module/data/action/baseAction.mjs | 8 +++ module/data/actor/adversary.mjs | 1 + module/data/actor/character.mjs | 1 + module/data/actor/companion.mjs | 1 + module/data/countdowns.mjs | 33 ++++++++++++ module/systemRegistration/migrations.mjs | 68 ++++++------------------ 6 files changed, 61 insertions(+), 51 deletions(-) diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index f3008704..58be672b 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -451,7 +451,15 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel static migrateData(source) { if (source.damage?.parts && Array.isArray(source.damage.parts)) { + let hitPointsExists = source.damage.parts.some(x => x.applyTo === 'hitPoints'); source.damage.parts = source.damage.parts.reduce((acc, part) => { + if (!part.applyTo && hitPointsExists) return acc; + + if (!part.applyTo) { + hitPointsExists = true; + part.applyTo = 'hitPoints'; + } + acc[part.applyTo] = part; return acc; }, {}); diff --git a/module/data/actor/adversary.mjs b/module/data/actor/adversary.mjs index d6d0dcdf..ae17c128 100644 --- a/module/data/actor/adversary.mjs +++ b/module/data/actor/adversary.mjs @@ -87,6 +87,7 @@ export default class DhpAdversary extends DhCreature { parts: { hitPoints: { type: ['physical'], + applyTo: 'hitPoints', value: { multiplier: 'flat' } diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index 3b12da6f..b39c64aa 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -107,6 +107,7 @@ export default class DhCharacter extends DhCreature { parts: { hitPoints: { type: ['physical'], + applyTo: 'hitPoints', value: { custom: { enabled: true, diff --git a/module/data/actor/companion.mjs b/module/data/actor/companion.mjs index 300bd698..2ca7fd5b 100644 --- a/module/data/actor/companion.mjs +++ b/module/data/actor/companion.mjs @@ -102,6 +102,7 @@ export default class DhCompanion extends DhCreature { parts: { hitPoints: { type: ['physical'], + applyTo: 'hitPoints', value: { dice: 'd6', multiplier: 'prof' diff --git a/module/data/countdowns.mjs b/module/data/countdowns.mjs index 8e55ed31..ffe4d26b 100644 --- a/module/data/countdowns.mjs +++ b/module/data/countdowns.mjs @@ -28,6 +28,39 @@ export default class DhCountdowns extends foundry.abstract.DataModel { for (const countdownKey of changedCountdowns) foundry.ui.countdowns.changedCountdownsForAnimation.add(countdownKey); } + + static migrateData(source) { + const migrateOldCountdowns = (data, type) => { + for (const key of Object.keys(data.countdowns)) { + const countdown = data.countdowns[key]; + source.countdowns[key] = { + ...countdown, + type: type, + ownership: Object.keys(countdown.ownership.players).reduce((acc, key) => { + acc[key] = + countdown.ownership.players[key].type === 1 ? 2 : countdown.ownership.players[key].type; + return acc; + }, {}), + progress: { + ...countdown.progress, + type: countdown.progress.type.value + } + }; + } + + source[type] = null; + }; + + if (source.narrative) { + migrateOldCountdowns(source.narrative, 'narrative'); + } + + if (source.encounter) { + migrateOldCountdowns(source.encounter, 'encounter'); + } + + return super.migrateData(source); + } } export class DhCountdown extends foundry.abstract.DataModel { diff --git a/module/systemRegistration/migrations.mjs b/module/systemRegistration/migrations.mjs index ec546c92..6971c34c 100644 --- a/module/systemRegistration/migrations.mjs +++ b/module/systemRegistration/migrations.mjs @@ -1,5 +1,4 @@ import { defaultRestOptions } from '../config/generalConfig.mjs'; -import { RefreshType, socketEvent } from './socket.mjs'; export async function runMigrations() { let lastMigrationVersion = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LastMigrationVersion); @@ -153,61 +152,26 @@ export async function runMigrations() { await pack.configure({ locked: true }); } - /* Migrate old countdown structure */ - const countdownSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns); - const getCountdowns = (data, type) => { - return Object.keys(data.countdowns).reduce((acc, key) => { - const countdown = data.countdowns[key]; - acc[key] = { - ...countdown, - type: type, - ownership: Object.keys(countdown.ownership.players).reduce((acc, key) => { - acc[key] = - countdown.ownership.players[key].type === 1 ? 2 : countdown.ownership.players[key].type; - return acc; - }, {}), - progress: { - ...countdown.progress, - type: countdown.progress.type.value - } - }; - - return acc; - }, {}); - }; - - await countdownSettings.updateSource({ - countdowns: { - ...getCountdowns(countdownSettings.narrative, 'narrative'), - ...getCountdowns(countdownSettings.encounter, 'encounter') - } - }); - await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Countdowns, countdownSettings); - - game.socket.emit(`system.${CONFIG.DH.id}`, { - action: socketEvent.Refresh, - data: { refreshType: RefreshType.Countdown } - }); - Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Countdown }); - lastMigrationVersion = '1.2.0'; } if (foundry.utils.isNewerVersion('1.2.7', lastMigrationVersion)) { - const tagTeam = game.settings.get(CONFIG.DH.id, 'TagTeamRoll'); - const initatorMissing = tagTeam.initiator && !game.actors.some(actor => actor.id === tagTeam.initiator); - const missingMembers = Object.keys(tagTeam.members).reduce((acc, id) => { - if (!game.actors.some(actor => actor.id === id)) { - acc[id] = _del; - } - return acc; - }, {}); + try { + const tagTeam = game.settings.get(CONFIG.DH.id, 'TagTeamRoll'); + const initatorMissing = tagTeam.initiator && !game.actors.some(actor => actor.id === tagTeam.initiator); + const missingMembers = Object.keys(tagTeam.members).reduce((acc, id) => { + if (!game.actors.some(actor => actor.id === id)) { + acc[id] = _del; + } + return acc; + }, {}); - await tagTeam.updateSource({ - initiator: initatorMissing ? null : tagTeam.initiator, - members: missingMembers - }); - await game.settings.set(CONFIG.DH.id, 'TagTeamRoll', tagTeam); + await tagTeam.updateSource({ + initiator: initatorMissing ? null : tagTeam.initiator, + members: missingMembers + }); + await game.settings.set(CONFIG.DH.id, 'TagTeamRoll', tagTeam); + } catch { } lastMigrationVersion = '1.2.7'; } @@ -303,6 +267,8 @@ export async function runMigrations() { /* Migrate existing effects modifying armor, creating new Armor Effects instead */ const migrateEffects = async entity => { + if (!entity?.effects) return; + for (const effect of entity.effects) { if (effect.system.changes.every(x => x.key !== 'system.armorScore')) continue; From 8c6a470d84d80ac70966e7b0c985daf396637c70 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 29 Jun 2026 14:25:16 +0200 Subject: [PATCH 06/25] Raised version --- system.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system.json b/system.json index 4660a196..0ecc2e75 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "2.4.1", + "version": "2.4.2", "compatibility": { "minimum": "14.364", "verified": "14.364", @@ -10,7 +10,7 @@ }, "url": "https://github.com/Foundryborne/daggerheart", "manifest": "https://raw.githubusercontent.com/Foundryborne/daggerheart/v14/system.json", - "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.4.1/system.zip", + "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.4.2/system.zip", "authors": [ { "name": "WBHarry" From 2cc52fae1f0d4358fb4267310c1f44a3af15d210 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Tue, 30 Jun 2026 05:18:13 -0400 Subject: [PATCH 07/25] Remove fieldset from top level notes (#2048) --- styles/less/global/global.less | 3 ++ styles/less/global/prose-mirror.less | 4 ++- .../sheets/actors/actor-sheet-shared.less | 31 +++++++++++++++++++ templates/sheets/actors/adversary/notes.hbs | 12 +++---- templates/sheets/actors/environment/notes.hbs | 5 +-- templates/sheets/actors/npc/notes.hbs | 6 ++-- templates/sheets/actors/party/notes.hbs | 11 +++---- 7 files changed, 49 insertions(+), 23 deletions(-) diff --git a/styles/less/global/global.less b/styles/less/global/global.less index c0e7f3fc..19a9e519 100644 --- a/styles/less/global/global.less +++ b/styles/less/global/global.less @@ -12,6 +12,9 @@ } .daggerheart.dh-style { + /** Not an actual scrollbar width (it can't be configured on all browsers) but actually a compensation value for scrollbar gutter purposes */ + --scrollbar-width: 10px; + * { scrollbar-width: thin; scrollbar-color: light-dark(@dark-blue, @golden) transparent; diff --git a/styles/less/global/prose-mirror.less b/styles/less/global/prose-mirror.less index fc8e49f9..27048ddf 100644 --- a/styles/less/global/prose-mirror.less +++ b/styles/less/global/prose-mirror.less @@ -1,5 +1,6 @@ @import '../utils/colors.less'; @import '../utils/fonts.less'; +@import '../utils/mixin.less'; .application.daggerheart { prose-mirror { @@ -12,6 +13,7 @@ background-color: transparent; } .editor-content { + .with-scroll-shadows(); h1 { font-size: var(--font-size-32); } @@ -42,7 +44,7 @@ ul { list-style: disc; } - } + } // Fixes centering and makes it not render over scrollbar &:hover button.toggle:enabled { display: flex; diff --git a/styles/less/sheets/actors/actor-sheet-shared.less b/styles/less/sheets/actors/actor-sheet-shared.less index 89617103..5eb5b43c 100644 --- a/styles/less/sheets/actors/actor-sheet-shared.less +++ b/styles/less/sheets/actors/actor-sheet-shared.less @@ -54,6 +54,37 @@ } } + .tab.notes.active { + padding: 0; + margin: 0; + margin-top: -10px; // will be removed once tab-navigation bottom margin is removed on all actor sheets + scrollbar-gutter: unset; + + // Add padding around top level level prosemirrors used for note tabs + > prose-mirror { + @right-padding: calc(16px - var(--scrollbar-width)); + .editor-content { + scrollbar-gutter: stable; + padding-right: @right-padding; + } + &.inactive { + button.toggle { + top: 16px; + } + .editor-content { + padding: 16px @right-padding 4px 16px; + } + } + &.active { + padding: 8px 0 4px 16px; + } + } + + .artist-attribution { + padding-left: 16px; + } + } + .search-section { display: flex; gap: 10px; diff --git a/templates/sheets/actors/adversary/notes.hbs b/templates/sheets/actors/adversary/notes.hbs index a5c3f706..d329d318 100644 --- a/templates/sheets/actors/adversary/notes.hbs +++ b/templates/sheets/actors/adversary/notes.hbs @@ -1,13 +1,9 @@
-
- {{localize tabs.notes.label}} - {{formInput notes.field value=notes.value enriched=notes.enriched toggled=true}} -
- + {{formInput notes.field value=notes.value enriched=notes.enriched toggled=true}} {{#if (and showAttribution document.system.attribution.artist)}} {{/if}} diff --git a/templates/sheets/actors/environment/notes.hbs b/templates/sheets/actors/environment/notes.hbs index 4f6b131e..1acf0e93 100644 --- a/templates/sheets/actors/environment/notes.hbs +++ b/templates/sheets/actors/environment/notes.hbs @@ -3,10 +3,7 @@ data-tab='{{tabs.notes.id}}' data-group='{{tabs.notes.group}}' > -
- {{localize tabs.notes.label}} - {{formInput notes.field value=notes.value enriched=notes.value toggled=true}} -
+ {{formInput notes.field value=notes.value enriched=notes.value toggled=true}} {{#if (and showAttribution document.system.attribution.artist)}} diff --git a/templates/sheets/actors/npc/notes.hbs b/templates/sheets/actors/npc/notes.hbs index bc9ac3cf..7dd5432d 100644 --- a/templates/sheets/actors/npc/notes.hbs +++ b/templates/sheets/actors/npc/notes.hbs @@ -1,7 +1,7 @@
{{formInput notes.field value=notes.value enriched=notes.enriched toggled=true}} diff --git a/templates/sheets/actors/party/notes.hbs b/templates/sheets/actors/party/notes.hbs index 663a484a..af21b29f 100644 --- a/templates/sheets/actors/party/notes.hbs +++ b/templates/sheets/actors/party/notes.hbs @@ -1,10 +1,7 @@
-
- {{localize tabs.notes.label}} - {{formInput notes.field value=notes.value enriched=notes.value toggled=true}} -
+ {{formInput notes.field value=notes.value enriched=notes.value toggled=true}}
\ No newline at end of file From 70388dbd736631679fc8c22d17d43f0b8912f7d9 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Tue, 30 Jun 2026 13:05:35 +0200 Subject: [PATCH 08/25] [Fix] SummonAction Actor Choice (#2045) * Fixed logic for picking which actor to summon * Moved getWorldActor function to utils and updated logic * . * Improved logic --- module/data/fields/action/summonField.mjs | 17 ++--------------- module/helpers/utils.mjs | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/module/data/fields/action/summonField.mjs b/module/data/fields/action/summonField.mjs index a2275fa5..6845d2ba 100644 --- a/module/data/fields/action/summonField.mjs +++ b/module/data/fields/action/summonField.mjs @@ -1,4 +1,4 @@ -import { itemAbleRollParse, triggerChatRollFx } from '../../../helpers/utils.mjs'; +import { getWorldActor, itemAbleRollParse, triggerChatRollFx } from '../../../helpers/utils.mjs'; import FormulaField from '../formulaField.mjs'; const fields = foundry.data.fields; @@ -42,7 +42,7 @@ export default class DHSummonField extends fields.ArrayField { const count = roll.total; if (!roll.isDeterministic) rolls.push(roll); - const actor = await DHSummonField.getWorldActor(await foundry.utils.fromUuid(summon.actorUUID)); + const actor = await getWorldActor(await foundry.utils.fromUuid(summon.actorUUID)); /* Extending summon data in memory so it's available in actionField.toChat. Think it's harmless, but ugly. Could maybe find a better way. */ summon.actor = actor.toObject(); @@ -62,19 +62,6 @@ export default class DHSummonField extends fields.ArrayField { DHSummonField.handleSummon(summonData, this.actor); } - /* Check for any available instances of the actor present in the world if we're missing artwork in the compendium. If none exists, create one. */ - static async getWorldActor(baseActor) { - const dataType = game.system.api.data.actors[`Dh${baseActor.type.capitalize()}`]; - if (baseActor.inCompendium && dataType && baseActor.img === dataType.DEFAULT_ICON) { - const worldActorCopy = game.actors.find(x => x.name === baseActor.name); - if (worldActorCopy) return worldActorCopy; - - return await game.system.api.documents.DhpActor.create(baseActor.toObject()); - } - - return baseActor; - } - static async handleSummon(summonData, actionActor) { await CONFIG.ux.TokenManager.createTokensWithPreview(summonData, { elevation: actionActor.token?.elevation }); diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 6467edd7..cb79a76b 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -889,4 +889,27 @@ export async function triggerChatRollFx(rolls, options = { whisper: false, blind export function shouldUseHopeFearAutomation(options = { gmAsPlayer: true }) { const { hopeFear } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation); return (!game.user.isGM || options.gmAsPlayer) ? hopeFear.players : hopeFear.gm; +} + +export async function getWorldActor(baseActor) { + if (baseActor.inCompendium) { + const worldActorCopy = game.actors.find(x => + x._stats.compendiumSource === baseActor.uuid && + (!x.prototypeToken.actorLink || x.name === baseActor.name) + ); + + if (worldActorCopy) + return worldActorCopy; + + const baseActorData = baseActor; + return await game.system.api.documents.DhpActor.create({ + ...baseActorData, + _stats: { + ...baseActorData._stats, + compendiumSource: baseActor.uuid + } + }); + } + + return baseActor; } \ No newline at end of file From 1af86d87bb5a522308981e9cbb883434ef546c4d Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Wed, 1 Jul 2026 07:39:16 -0400 Subject: [PATCH 09/25] Refactor styling of sidebar resources (#2050) --- .../applications/sheets/actors/adversary.mjs | 7 +- .../applications/sheets/actors/character.mjs | 6 +- styles/less/global/resource-bar.less | 209 ++++--- .../less/sheets/actors/adversary/sidebar.less | 103 +--- .../less/sheets/actors/character/sidebar.less | 181 +----- .../sheets/actors/party/party-members.less | 560 +++++++++--------- templates/sheets/actors/character/sidebar.hbs | 302 +++++----- .../sheets/actors/party/party-members.hbs | 382 ++++++------ .../sheets/global/partials/resource-bar.hbs | 52 +- templates/ui/tooltip/armorManagement.hbs | 4 +- 10 files changed, 767 insertions(+), 1039 deletions(-) diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs index 85380392..35612e69 100644 --- a/module/applications/sheets/actors/adversary.mjs +++ b/module/applications/sheets/actors/adversary.mjs @@ -58,12 +58,13 @@ export default class AdversarySheet extends DHBaseActorSheet { template: 'systems/daggerheart/templates/sheets/actors/adversary/features.hbs', scrollable: ['.feature-section'] }, - notes: { - template: 'systems/daggerheart/templates/sheets/actors/adversary/notes.hbs' - }, effects: { template: 'systems/daggerheart/templates/sheets/actors/adversary/effects.hbs', scrollable: ['.effects-sections'] + }, + notes: { + template: 'systems/daggerheart/templates/sheets/actors/adversary/notes.hbs', + scrollable: ['.editor-content'] } }; diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 3a60e7ca..5006e9d6 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -1057,7 +1057,7 @@ export default class CharacterSheet extends DHBaseActorSheet { direction: 'DOWN' }); - html.querySelectorAll('.armor-slot').forEach(element => { + html.querySelectorAll('.armor .slot').forEach(element => { element.addEventListener('click', CharacterSheet.armorSourcePipUpdate); }); } @@ -1072,7 +1072,7 @@ export default class CharacterSheet extends DHBaseActorSheet { /** Update specific armor source */ static async armorSourcePipUpdate(event) { - const target = event.target.closest('.armor-slot'); + const target = event.target.closest('.slot'); const { uuid, value } = target.dataset; const document = await foundry.utils.fromUuid(uuid); @@ -1100,7 +1100,7 @@ export default class CharacterSheet extends DHBaseActorSheet { } const container = target.closest('.slot-bar'); - for (const armorSlot of container.querySelectorAll('.armor-slot i')) { + for (const armorSlot of container.querySelectorAll('.armor .slot i')) { const index = Number.parseInt(armorSlot.dataset.index); if (decreasing && index >= newCurrent) { armorSlot.classList.remove('fa-shield'); diff --git a/styles/less/global/resource-bar.less b/styles/less/global/resource-bar.less index d06b43a8..fb7f7d4e 100644 --- a/styles/less/global/resource-bar.less +++ b/styles/less/global/resource-bar.less @@ -9,111 +9,109 @@ } }); +/** Pips styling, can exist standalone even without a slot-value */ +.slot-bar { + display: flex; + gap: 4px; + padding: 5px; + border: 1px solid @color-border; + border-radius: 6px; + z-index: 1; + color: @color-text-emphatic; + width: fit-content; + min-height: 22px; + flex-wrap: wrap; + + .slot { + transition: all 0.3s ease; + cursor: pointer; + } + + .slot:not(:has(i)) { + width: 15px; + height: 10px; + border: 1px solid @color-border; + background: light-dark(@dark-blue-10, @golden-10); + border-radius: 3px; + + &.large { + width: 20px; + } + + &.filled { + background: light-dark(@dark-blue, @golden); + } + } + + &.armor .slot { + font-size: var(--font-size-12); + .fa-shield-halved { + color: light-dark(@dark-blue-40, @golden-40); + } + } + + .empty-slot { + width: 15px; + height: 10px; + } +} + +.slot-value { + display: flex; + flex-direction: column; + padding: 0 5px; + font-size: 1.5rem; + align-items: center; + justify-content: center; + text-align: center; + z-index: 2; + color: @beige; + + .slot-label { + display: flex; + align-items: center; + color: light-dark(@beige, @dark-blue); + background: light-dark(@dark-blue, @golden); + padding: 0 5px; + width: fit-content; + font-weight: bold; + border-radius: 0px 0px 5px 5px; + font-size: var(--font-size-12); + + .label { + padding-right: 5px; + } + + .value { + padding-left: 6px; + border-left: 1px solid light-dark(@beige, @dark-golden); + } + } +} + .status-bar { display: flex; - justify-content: center; + flex-direction: column; + align-items: center; position: relative; - width: 120px; - height: 40px; - - .status-label { - position: relative; - top: 40px; - height: 22px; - width: 79px; - clip-path: path('M0 0H79L74 16.5L39 22L4 16.5L0 0Z'); - background: light-dark(@dark-blue, @golden); - - h4 { - font-weight: bold; - text-align: center; - line-height: 18px; - color: light-dark(@beige, @dark-blue); - } - } - .slot-value { - position: absolute; - display: flex; - flex-direction: column; - padding: 0 5px; - font-size: 1.5rem; - align-items: center; - width: 140px; - height: 40px; - justify-content: center; - text-align: center; - z-index: 2; - color: @beige; - - .slot-bar { - display: flex; - flex-wrap: wrap; - gap: 5px; - padding: 5px; - border: 1px solid @color-border; - border-radius: 6px; - z-index: 1; - color: @color-text-emphatic; - width: fit-content; - - .slot { - width: 15px; - height: 10px; - border: 1px solid @color-border; - background: light-dark(@dark-blue-10, @golden-10); - border-radius: 3px; - transition: all 0.3s ease; - cursor: pointer; - - &.large { - width: 20px; - } - - &.filled { - background: light-dark(@dark-blue, @golden); - } - } - - .empty-slot { - width: 15px; - height: 10px; - } - } - .slot-label { - display: flex; - align-items: center; - color: light-dark(@beige, @dark-blue); - background: light-dark(@dark-blue, @golden); - padding: 0 5px; - width: fit-content; - font-weight: bold; - border-radius: 0px 0px 5px 5px; - font-size: var(--font-size-12); - - .label { - padding-right: 5px; - } - - .value { - padding-left: 6px; - border-left: 1px solid light-dark(@beige, @dark-golden); - } - } - } .status-value { - position: absolute; + position: relative; display: flex; padding: 0 5px; font-size: 1.5rem; align-items: center; - width: 140px; + width: 100px; height: 40px; justify-content: center; text-align: center; z-index: 2; color: @beige; + > * { + z-index: 1; + } + input[type='number'] { background: transparent; font-size: 1.5rem; @@ -146,11 +144,11 @@ .progress-bar { position: absolute; appearance: none; - width: 100px; - height: 40px; + width: 100%; + height: 100%; border: 1px solid @color-border; border-radius: 6px; - z-index: 1; + z-index: 0; background: @dark-blue; &::-webkit-progress-bar { @@ -175,4 +173,29 @@ border-radius: 6px; } } + .status-label { + position: relative; + height: 22px; + width: 79px; + background: light-dark(@dark-blue, @golden); + + &.pointy { + clip-path: path('M0 0H79L74 16.5L39 22L4 16.5L0 0Z'); + margin-bottom: -2px; // compensate for pointy bottom so spacing feels more "right" + } + + h4 { + font-weight: bold; + text-align: center; + line-height: 18px; + color: light-dark(@beige, @dark-blue); + } + } } + +// Overrides for sidebar usage. +aside[data-application-part="sidebar"] .resources-section .slot-bar { + display: grid; + grid-template-columns: repeat(6, min-content); + grid-auto-flow: row; +} \ No newline at end of file diff --git a/styles/less/sheets/actors/adversary/sidebar.less b/styles/less/sheets/actors/adversary/sidebar.less index 5db9f5e9..a2a3240a 100644 --- a/styles/less/sheets/actors/adversary/sidebar.less +++ b/styles/less/sheets/actors/adversary/sidebar.less @@ -106,7 +106,7 @@ display: flex; flex-direction: column; top: -20px; - gap: 16px; + gap: 10px; margin-bottom: -10px; &.pip-display { @@ -120,105 +120,6 @@ .resources-section { display: flex; justify-content: space-evenly; - margin-bottom: 16px; - - .status-bar { - display: flex; - justify-content: center; - position: relative; - width: 100px; - height: 40px; - - .status-label { - position: relative; - top: 40px; - height: 22px; - width: 79px; - clip-path: path('M0 0H79L74 16.5L39 22L4 16.5L0 0Z'); - background: light-dark(@dark-blue, @golden); - - h4 { - font-weight: bold; - text-align: center; - line-height: 18px; - color: light-dark(@beige, @dark-blue); - } - } - .status-value { - position: absolute; - display: flex; - padding: 0 6px; - font-size: 1.5rem; - align-items: center; - width: 100px; - height: 40px; - justify-content: center; - text-align: center; - z-index: 2; - color: @beige; - - input[type='number'] { - background: transparent; - font-size: 1.5rem; - width: 40px; - height: 30px; - text-align: center; - border: none; - outline: 2px solid transparent; - color: @beige; - - &.bar-input { - padding: 0; - color: @beige; - backdrop-filter: none; - background: transparent; - transition: all 0.3s ease; - - &:hover, - &:focus { - background: @semi-transparent-dark-blue; - backdrop-filter: blur(9.5px); - } - } - } - - .bar-label { - width: 40px; - } - } - .progress-bar { - position: absolute; - appearance: none; - width: 100px; - height: 40px; - border: 1px solid @color-border; - border-radius: 6px; - z-index: 1; - background: @dark-blue; - - &::-webkit-progress-bar { - border: none; - background: @dark-blue; - border-radius: 6px; - } - &::-webkit-progress-value { - background: @gradient-hp; - border-radius: 6px; - } - &.stress-color::-webkit-progress-value { - background: @gradient-stress; - border-radius: 6px; - } - &::-moz-progress-bar { - background: @gradient-hp; - border-radius: 6px; - } - &.stress-color::-moz-progress-bar { - background: @gradient-stress; - border-radius: 6px; - } - } - } } .status-section { @@ -256,7 +157,7 @@ .status-label { padding: 2px 10px; width: 100%; - border-radius: 3px; + border-radius: 0 0 3px 3px; background: light-dark(@dark-blue, @golden); h4 { diff --git a/styles/less/sheets/actors/character/sidebar.less b/styles/less/sheets/actors/character/sidebar.less index c76ee9ff..66f09cfc 100644 --- a/styles/less/sheets/actors/character/sidebar.less +++ b/styles/less/sheets/actors/character/sidebar.less @@ -89,113 +89,13 @@ .resources-section { justify-content: space-around; - margin: 8px 2px 8px 2px; + margin: 8px 2px 0 2px; } } .resources-section { display: flex; justify-content: space-evenly; - margin-bottom: 20px; - - .status-bar { - display: flex; - justify-content: center; - position: relative; - width: 120px; - height: 40px; - - .status-label { - position: relative; - top: 40px; - height: 22px; - width: 79px; - clip-path: path('M0 0H79L74 16.5L39 22L4 16.5L0 0Z'); - background: light-dark(@dark-blue, @golden); - - h4 { - font-weight: bold; - text-align: center; - line-height: 18px; - color: light-dark(@beige, @dark-blue); - } - } - - .status-value { - position: absolute; - display: flex; - padding: 0 5px; - font-size: 1.5rem; - align-items: center; - width: 140px; - height: 40px; - justify-content: center; - text-align: center; - z-index: 2; - color: @beige; - - input[type='number'] { - background: transparent; - font-size: 1.5rem; - width: 40px; - height: 30px; - text-align: center; - border: none; - outline: 2px solid transparent; - color: @beige; - - &.bar-input { - padding: 0; - color: @beige; - backdrop-filter: none; - background: transparent; - transition: all 0.3s ease; - - &:hover, - &:focus { - background: @semi-transparent-dark-blue; - backdrop-filter: blur(9.5px); - } - } - } - - .bar-label { - width: 40px; - } - } - .progress-bar { - position: absolute; - appearance: none; - width: 100px; - height: 40px; - border: 1px solid @color-border; - border-radius: 6px; - z-index: 1; - background: @dark-blue; - - &::-webkit-progress-bar { - border: none; - background: @dark-blue; - border-radius: 6px; - } - &::-webkit-progress-value { - background: @gradient-hp; - border-radius: 6px; - } - &.stress-color::-webkit-progress-value { - background: @gradient-stress; - border-radius: 6px; - } - &::-moz-progress-bar { - background: @gradient-hp; - border-radius: 6px; - } - &.stress-color::-moz-progress-bar { - background: @gradient-stress; - border-radius: 6px; - } - } - } } .status-section { @@ -245,21 +145,21 @@ .status-bar.armor-slots { display: flex; - justify-content: center; - position: relative; width: 95px; - height: 30px; white-space: nowrap; + .status-label { + height: 30px; + } + .status-label { padding: 2px 2px; position: relative; - top: 30px; height: 22px; width: 95px; border-radius: 3px; background: light-dark(@dark-blue, @golden); - clip-path: none; + display: flex; align-items: center; justify-content: center; @@ -291,40 +191,16 @@ } } .slot-value { - position: absolute; - display: flex; - padding: 0 5px; font-size: 1.2rem; - align-items: center; width: 80px; height: 30px; - justify-content: center; - text-align: center; - z-index: 2; color: light-dark(@dark-blue, @beige); flex-direction: column; .slot-bar { - display: flex; - flex-wrap: wrap; - gap: 4px; - padding: 5px; - border: 1px solid @color-border; - border-radius: 6px; - z-index: 1; background: @dark-blue; justify-content: center; - color: @color-text-emphatic; - - .armor-slot { - cursor: pointer; - transition: all 0.3s ease; - font-size: var(--font-size-12); - - .fa-shield-halved { - color: light-dark(@dark-blue-40, @golden-40); - } - } + border-bottom: none; } .slot-label { display: flex; @@ -364,41 +240,21 @@ } } .status-value { - position: absolute; - display: flex; padding: 0 6px; font-size: 1.2rem; - align-items: center; width: 80px; height: 30px; justify-content: center; - text-align: center; - z-index: 2; color: light-dark(@dark-blue, @beige); - border: 1px solid @color-border; - border-bottom: none; - border-radius: 6px 6px 0 0; input[type='number'] { - background: transparent; font-size: 1.2rem; width: 30px; height: 20px; - text-align: center; - border: none; - outline: 2px solid transparent; color: light-dark(@dark-blue, @beige); &.bar-input { - padding: 0; color: light-dark(@dark-blue, @beige); - backdrop-filter: none; - background: transparent; - &:hover, - &:focus { - background: @semi-transparent-dark-blue; - backdrop-filter: blur(9.5px); - } } } @@ -407,32 +263,9 @@ } } .progress-bar { - position: absolute; - appearance: none; - width: 80px; - height: 30px; - border: 1px solid @color-border; - border-radius: 6px; - z-index: 1; background: light-dark(transparent, @dark-blue); border-bottom: none; border-radius: 6px 6px 0 0; - &::-webkit-progress-bar { - border: none; - background: light-dark(transparent, @dark-blue); - } - &::-webkit-progress-value { - background: @gradient-stress; - } - &.stress-color::-webkit-progress-value { - background: @gradient-stress; - } - &::-moz-progress-bar { - background: @gradient-stress; - } - &.stress-color::-moz-progress-bar { - background: @gradient-stress; - } } } diff --git a/styles/less/sheets/actors/party/party-members.less b/styles/less/sheets/actors/party/party-members.less index dc464291..2490757f 100644 --- a/styles/less/sheets/actors/party/party-members.less +++ b/styles/less/sheets/actors/party/party-members.less @@ -1,295 +1,265 @@ -@import '../../../utils/colors.less'; -@import '../../../utils/fonts.less'; -@import '../../../utils/mixin.less'; - -.application.sheet.daggerheart.actor.dh-style.party .tab.partyMembers { - overflow: auto; - - .actors-list { - display: flex; - flex-direction: column; - gap: 8px; - align-items: stretch; - width: 100%; - - .actor-resources { - display: grid; - grid-template: - "img header" min-content - "img body" 1fr - / 7.5rem 1fr; - gap: 6px; - column-gap: 12px; - padding: 6px; - background-color: light-dark(@dark-blue-10, @golden-10); - - .actor-img-frame { - grid-area: img; - width: 7.375rem; - height: 7.375rem; - position: relative; - - .actor-img { - object-fit: cover; - object-position: top center; - border-radius: 6px; - width: 100%; - height: 100%; - } - - .equipped-weapons { - position: absolute; - top: -2px; - left: -3px; - display: flex; - flex-direction: column; - gap: 1px; - img { - border-radius: 50%; - width: 24px; - height: 24px; - border: 1px solid @color-border; - object-fit: cover; - } - } - - .evasion { - position: absolute; - top: 1px; - right: 1px; - width: 1.75rem; - height: 1.75rem; - background: url('../assets/svg/trait-shield.svg') no-repeat; - background-size: 100%; - color: var(--color-light-1); - font-size: var(--font-size-14); - font-weight: 700; - display: flex; - align-items: center; - justify-content: center; - } - - .threshold-section { - position: absolute; - left: 0; - right: 0; - bottom: -2px; - margin: auto; - - display: flex; - gap: 4px; - background-color: light-dark(var(--color-light-1), @dark-blue); - color: @color-text-emphatic; - padding: 4px 6px; - border: 1px solid @color-border; - border-radius: 3px; - align-items: baseline; - width: fit-content; - - h4 { - font-weight: bold; - text-transform: uppercase; - white-space: nowrap; - - &.threshold-label { - font-size: var(--font-size-10); - color: @color-text-emphatic; - } - - &.threshold-value { - font-size: var(--font-size-11); - color: light-dark(@dark, @beige); - } - } - } - } - - header { - grid-area: header; - display: grid; - grid-template: - "name hope" min-content - "subtitle subtitle" min-content - / 1fr min-content; - - .actor-name { - width: 100%; - z-index: 1; - font-size: var(--font-size-20); - color: @color-text-emphatic; - font-weight: bold; - } - - .delete-icon { - font-size: 0.75em; - } - - .subtitle { - grid-area: subtitle; - font-size: var(--font-size-14); - } - - .hope-section { - display: flex; - background-color: light-dark(transparent, @dark-blue); - color: @color-text-emphatic; - padding: 3px 6px; - border: 1px solid @color-border; - border-radius: 3px; - align-items: center; - width: fit-content; - margin-left: auto; - - h4 { - font-size: var(--font-size-12); - font-weight: bold; - text-transform: uppercase; - color: @color-text-emphatic; - margin-right: 3px; - } - - .hope-value { - display: flex; - cursor: pointer; - font-size: var(--font-size-12); - margin-left: 1px; - } - } - } - - .body { - grid-area: body; - display: flex; - align-items: start; - justify-content: space-between; - } - - .resources { - display: flex; - flex-direction: column; - gap: 4px; - - .slot-section { - display: flex; - flex-direction: row; - align-items: stretch; - - .slot-label { - display: flex; - align-items: center; - color: light-dark(@beige, @dark-blue); - background: light-dark(@dark-blue, @golden); - padding: 0 4px; - width: fit-content; - font-weight: bold; - border-radius: 6px 0px 0px 6px; - font-size: var(--font-size-12); - white-space: nowrap; - - .label { - padding-right: 2px; - } - - .value { - font-variant-numeric: tabular-nums; - .current { - display: inline-block; - text-align: end; - width: 2ch; - } - .max { - display: inline-block; - text-align: start; - width: 2ch; - } - } - } - - .slot-bar { - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 4px; - - background-color: light-dark(@dark-blue-10, @dark-blue); - color: @color-text-emphatic; - padding: 2px 5px; - border: 1px solid @color-border; - border-radius: 0 6px 6px 0; - width: fit-content; - min-height: 22px; - - .armor-slot { - cursor: pointer; - transition: all 0.3s ease; - font-size: var(--font-size-12); - - .fa-shield-halved { - color: light-dark(@dark-blue-40, @golden-40); - } - } - - .slot { - width: 16px; - height: 10px; - border: 1px solid @color-border; - background: light-dark(@dark-blue-10, @golden-10); - border-radius: 3px; - transition: all 0.3s ease; - cursor: pointer; - - &.filled { - background: light-dark(@dark-blue, @golden); - } - } - } - } - } - - .traits { - background-color: light-dark(@dark-blue-10, @dark-blue); - border: 1px solid @color-border; - border-radius: 6px; - display: grid; - grid-template-columns: 1fr 1fr; - font-size: var(--font-size-12); - padding: 3px 4px; - gap: 3px 7px; - .trait { - display: flex; - justify-content: space-between; - gap: 3px; - .label { - color: @color-text-emphatic; - } - .value { - font-weight: 600; - } - } - } - } - } - - .actors-list.limited { - .actor-resources { - display: flex; - align-items: center; - } - .actor-img-frame { - width: 3rem; - height: 3rem; - } - } - - .actors-dragger { - display: flex; - align-items: center; - justify-content: center; - box-sizing: border-box; - width: 100%; - height: 40px; - border: 1px dashed light-dark(@dark-blue-50, @beige-50); - border-radius: 3px; - color: light-dark(@dark-blue-50, @beige-50); - } -} +@import '../../../utils/colors.less'; +@import '../../../utils/fonts.less'; +@import '../../../utils/mixin.less'; + +.application.sheet.daggerheart.actor.dh-style.party .tab.partyMembers { + overflow: auto; + + .actors-list { + display: flex; + flex-direction: column; + gap: 8px; + align-items: stretch; + width: 100%; + + .actor-resources { + display: grid; + grid-template: + "img header" min-content + "img body" 1fr + / 7.5rem 1fr; + gap: 6px; + column-gap: 12px; + padding: 6px; + background-color: light-dark(@dark-blue-10, @golden-10); + + .actor-img-frame { + grid-area: img; + width: 7.375rem; + height: 7.375rem; + position: relative; + + .actor-img { + object-fit: cover; + object-position: top center; + border-radius: 6px; + width: 100%; + height: 100%; + } + + .equipped-weapons { + position: absolute; + top: -2px; + left: -3px; + display: flex; + flex-direction: column; + gap: 1px; + img { + border-radius: 50%; + width: 24px; + height: 24px; + border: 1px solid @color-border; + object-fit: cover; + } + } + + .evasion { + position: absolute; + top: 1px; + right: 1px; + width: 1.75rem; + height: 1.75rem; + background: url('../assets/svg/trait-shield.svg') no-repeat; + background-size: 100%; + color: var(--color-light-1); + font-size: var(--font-size-14); + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; + } + + .threshold-section { + position: absolute; + left: 0; + right: 0; + bottom: -2px; + margin: auto; + + display: flex; + gap: 4px; + background-color: light-dark(var(--color-light-1), @dark-blue); + color: @color-text-emphatic; + padding: 4px 6px; + border: 1px solid @color-border; + border-radius: 3px; + align-items: baseline; + width: fit-content; + + h4 { + font-weight: bold; + text-transform: uppercase; + white-space: nowrap; + + &.threshold-label { + font-size: var(--font-size-10); + color: @color-text-emphatic; + } + + &.threshold-value { + font-size: var(--font-size-11); + color: light-dark(@dark, @beige); + } + } + } + } + + header { + grid-area: header; + display: grid; + grid-template: + "name hope" min-content + "subtitle subtitle" min-content + / 1fr min-content; + + .actor-name { + width: 100%; + z-index: 1; + font-size: var(--font-size-20); + color: @color-text-emphatic; + font-weight: bold; + } + + .delete-icon { + font-size: 0.75em; + } + + .subtitle { + grid-area: subtitle; + font-size: var(--font-size-14); + } + + .hope-section { + display: flex; + background-color: light-dark(transparent, @dark-blue); + color: @color-text-emphatic; + padding: 3px 6px; + border: 1px solid @color-border; + border-radius: 3px; + align-items: center; + width: fit-content; + margin-left: auto; + + h4 { + font-size: var(--font-size-12); + font-weight: bold; + text-transform: uppercase; + color: @color-text-emphatic; + margin-right: 3px; + } + + .hope-value { + display: flex; + cursor: pointer; + font-size: var(--font-size-12); + margin-left: 1px; + } + } + } + + .body { + grid-area: body; + display: flex; + align-items: start; + justify-content: space-between; + } + + .resources { + display: flex; + flex-direction: column; + gap: 4px; + + .slot-section { + display: flex; + flex-direction: row; + align-items: stretch; + + .slot-label { + display: flex; + align-items: center; + color: light-dark(@beige, @dark-blue); + background: light-dark(@dark-blue, @golden); + padding: 0 4px; + width: fit-content; + font-weight: bold; + border-radius: 6px 0px 0px 6px; + font-size: var(--font-size-12); + white-space: nowrap; + + .label { + padding-right: 2px; + } + + .value { + font-variant-numeric: tabular-nums; + .current { + display: inline-block; + text-align: end; + width: 2ch; + } + .max { + display: inline-block; + text-align: start; + width: 2ch; + } + } + } + + .slot-bar { + align-items: center; + flex-wrap: wrap; + background-color: light-dark(@dark-blue-10, @dark-blue); + padding: 2px 5px; + border-radius: 0 6px 6px 0; + width: fit-content; + } + } + } + + .traits { + background-color: light-dark(@dark-blue-10, @dark-blue); + border: 1px solid @color-border; + border-radius: 6px; + display: grid; + grid-template-columns: 1fr 1fr; + font-size: var(--font-size-12); + padding: 3px 4px; + gap: 3px 7px; + .trait { + display: flex; + justify-content: space-between; + gap: 3px; + .label { + color: @color-text-emphatic; + } + .value { + font-weight: 600; + } + } + } + } + } + + .actors-list.limited { + .actor-resources { + display: flex; + align-items: center; + } + .actor-img-frame { + width: 3rem; + height: 3rem; + } + } + + .actors-dragger { + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + width: 100%; + height: 40px; + border: 1px dashed light-dark(@dark-blue-50, @beige-50); + border-radius: 3px; + color: light-dark(@dark-blue-50, @beige-50); + } +} diff --git a/templates/sheets/actors/character/sidebar.hbs b/templates/sheets/actors/character/sidebar.hbs index 313c81f9..6ca858bf 100644 --- a/templates/sheets/actors/character/sidebar.hbs +++ b/templates/sheets/actors/character/sidebar.hbs @@ -1,152 +1,152 @@ -
\ No newline at end of file From 98fedf399079b566aee8a389aa735a920f42837c Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Fri, 3 Jul 2026 11:14:25 +0200 Subject: [PATCH 18/25] [Feature] Transform Linked Actors (#2046) * Added ability to transform linked actors * Updated to use the utils getWorldActor function * Animate change and fix error when converting to unlinked --------- Co-authored-by: Carlos Fernandez --- daggerheart.d.ts | 23 ++++++++ lang/en.json | 1 + module/applications/ui/effectsDisplay.mjs | 6 ++ module/data/action/baseAction.mjs | 7 ++- module/data/fields/action/transformField.mjs | 61 ++++++++++++-------- module/documents/_types.d.ts | 13 ++++- module/documents/actor.mjs | 8 ++- module/documents/token.mjs | 3 +- module/helpers/utils.mjs | 11 ++++ 9 files changed, 101 insertions(+), 32 deletions(-) diff --git a/daggerheart.d.ts b/daggerheart.d.ts index 1641faa8..891a3a2a 100644 --- a/daggerheart.d.ts +++ b/daggerheart.d.ts @@ -4,6 +4,15 @@ import '@common/primitives/global.mjs'; import Canvas from '@client/canvas/board.mjs'; import { ResourceUpdateMap } from './module/data/action/baseAction.mjs'; +import * as applications from './module/applications/_module.mjs'; +import * as data from './module/data/_module.mjs'; +import * as models from './module/data/_module.mjs'; +import * as documents from './module/documents/_module.mjs'; +import { macros } from './module/_module.mjs'; +import * as dice from './module/dice/_module.mjs'; +import * as fields from './module/data/fields/_module.mjs'; + + // Foundry's use of `Object.assign(globalThis) means many globally available objects are not read as such // This declare global hopefully fixes that // Note: eslint is not aware of these, whatever is added here should go in the eslint's globals list @@ -80,3 +89,17 @@ declare global { damageOptions: object; } } + +declare module '@client/packages/system.mjs' { + export default interface System { + api: { + applications: typeof applications, + data: typeof data, + models: typeof models, + documents: typeof documents, + macros: typeof macros, + dice: typeof dice, + fields: typeof fields + }; + } +} diff --git a/lang/en.json b/lang/en.json index 0180b085..b9a81b29 100755 --- a/lang/en.json +++ b/lang/en.json @@ -84,6 +84,7 @@ "transformActorMissing": "The assigned actor to transform into does not exist. It was probably deleted or moved in/out of a compendium", "canvasError": "There is no active scene.", "prototypeError": "You can only use a transform action from a Token", + "linkedSelectedError": "To transform a linked actor there either needs to be only a single token of it on the canvas, or you need to left-click select only one of them.", "actorLinkError": "You cannot transform a token with Actor Link set to true" } }, diff --git a/module/applications/ui/effectsDisplay.mjs b/module/applications/ui/effectsDisplay.mjs index a64b1b22..c9739f56 100644 --- a/module/applications/ui/effectsDisplay.mjs +++ b/module/applications/ui/effectsDisplay.mjs @@ -39,6 +39,12 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica } }; + /** + * Debounce and slightly delayed request to re-render this panel. Necessary for situations where it is not possible + * to properly wait for promises to resolve before refreshing the UI. + */ + refresh = foundry.utils.debounce(this.render.bind(this), 50); + get element() { return document.body.querySelector('.daggerheart.dh-style.effects-display'); } diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 58be672b..be7224cd 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -114,7 +114,10 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return this._id; } - /** Returns true if the current user is the owner of the containing item */ + /** + * Returns true if the current user is the owner of the containing item. + * @returns {boolean} + */ get isOwner() { return this.item?.isOwner ?? true; } @@ -143,6 +146,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel /** * Return the first Actor parent found. + * @returns {DhpActor | null} */ get actor() { return this.item instanceof DhpActor @@ -155,6 +159,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel /** * Returns true if the action is usable. * An action is usable on any actor type. For example, an adversary might have a base attack action. + * @returns {boolean} */ get usable() { const actor = this.actor; diff --git a/module/data/fields/action/transformField.mjs b/module/data/fields/action/transformField.mjs index 07b32166..0fc35742 100644 --- a/module/data/fields/action/transformField.mjs +++ b/module/data/fields/action/transformField.mjs @@ -1,5 +1,11 @@ +import { getWorldActor } from '../../../helpers/utils.mjs'; + const fields = foundry.data.fields; +/** + * @import DHSummonAction from '../../action/summonAction.mjs' + */ + export default class DHSummonField extends fields.SchemaField { /** * Action Workflow order @@ -20,6 +26,11 @@ export default class DHSummonField extends fields.SchemaField { super(transformFields, options, context); } + /** + * Runs the execute. This is run on behalf of DHSummonAction. + * @todo move this function to be on the summon action. + * @this DHSummonAction + */ static async execute() { if (!this.transform.actorUUID) { ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.noTransformActor')); @@ -37,26 +48,37 @@ export default class DHSummonField extends fields.SchemaField { return false; } - if (this.actor.prototypeToken.actorLink) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.actorLinkError')); + const activeTokens = this.actor.getActiveTokens(false, true); + const controlledMatchingTokens = canvas.tokens.controlled + .filter(x => x.actor && x.actor.uuid === this.actor.uuid) + .map(x => x.document); + /** @type {typeof game.system.api.documents.DhToken | null} */ + const token = this.actor.token ?? ( + activeTokens.length === 1 ? activeTokens[0] : + (controlledMatchingTokens.length === 1 ? controlledMatchingTokens[0] : null) + ); + + if (!this.actor.token && !token) { + ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.linkedSelectedError')); return false; } - if (!this.actor.token) { + if (!token) { ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.transform.prototypeError')); return false; } - const actor = await DHSummonField.getWorldActor(baseActor); + const actor = await getWorldActor(baseActor); const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes; const tokenSize = actor?.system.metadata.usesSize ? tokenSizes[actor.system.size] : actor.prototypeToken.width; - await this.actor.token.update( - { ...actor.prototypeToken.toJSON(), actorId: actor.id, width: tokenSize, height: tokenSize }, - { diff: false, recursive: false, noHook: true } + // Update token. Avoid using recursive: false, since that prevents animations + await token.update( + { ...actor.prototypeToken.toObject(), actorId: actor.id, width: tokenSize, height: tokenSize }, + { diff: false, noHook: true } ); - if (this.actor.token.combatant) { + if (token.combatant) { this.actor.token.combatant.update({ actorId: actor.id, img: actor.prototypeToken.texture.src }); } @@ -64,17 +86,17 @@ export default class DHSummonField extends fields.SchemaField { if (!this.transform.resourceRefresh.hitPoints) { marks.hitPoints = Math.min( this.actor.system.resources.hitPoints.value, - this.actor.token.actor.system.resources.hitPoints.max - 1 + token.actor.system.resources.hitPoints.max - 1 ); } if (!this.transform.resourceRefresh.stress) { marks.stress = Math.min( this.actor.system.resources.stress.value, - this.actor.token.actor.system.resources.stress.max - 1 + token.actor.system.resources.stress.max - 1 ); } if (marks.hitPoints || marks.stress) { - this.actor.token.actor.update({ + token.actor.update({ 'system.resources': { hitPoints: { value: marks.hitPoints }, stress: { value: marks.stress } @@ -84,20 +106,9 @@ export default class DHSummonField extends fields.SchemaField { const prevPosition = { ...this.actor.sheet.position }; this.actor.sheet.close(); - this.actor.token.actor.sheet.render({ force: true, position: prevPosition }); - } - - /* Check for any available instances of the actor present in the world, or create a world actor based on compendium */ - static async getWorldActor(baseActor) { - if (!baseActor.inCompendium) return baseActor; - - const dataType = game.system.api.data.actors[`Dh${baseActor.type.capitalize()}`]; - if (dataType && baseActor.img === dataType.DEFAULT_ICON) { - const worldActorCopy = game.actors.find(x => x.name === baseActor.name); - if (worldActorCopy) return worldActorCopy; + token.actor.sheet.render({ force: true, position: prevPosition }); + if (token.object.controlled) { + ui.effectsDisplay.refresh(); } - - const worldActor = await game.system.api.documents.DhpActor.create(baseActor.toObject()); - return worldActor; } } diff --git a/module/documents/_types.d.ts b/module/documents/_types.d.ts index a94d6395..a3af1b33 100644 --- a/module/documents/_types.d.ts +++ b/module/documents/_types.d.ts @@ -3,17 +3,26 @@ import DHItem from './item.mjs'; import BaseDataItem from '../data/item/base.mjs'; import DhActiveEffect from './activeEffect.mjs'; import EmbeddedCollection from '@common/abstract/embedded-collection.mjs'; +import DHToken from './token.mjs'; +import Actor from '@client/documents/actor.mjs'; +import Item from '@client/documents/item.mjs'; declare module './actor.mjs' { - export default interface DhpActor { + export default interface DhpActor extends Actor { system: T; items: EmbeddedCollection; effects: EmbeddedCollection; + get token(): DHToken | null; + + /** @inheritdoc */ + getActiveTokens(linked?: boolean, document?: boolean): (DHToken | foundry.canvas.placeables.Token)[]; + getActiveTokens(linked?: boolean, document: true): DHToken[]; + getActiveTokens(linked?: boolean, document: false): foundry.canvas.placeables.Token[]; } } declare module './item.mjs' { - export default interface DHItem { + export default interface DHItem extends Item { parent: DhpActor; actor: DhpActor; system: T; diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 8ef64f65..30901374 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -34,12 +34,14 @@ export default class DhpActor extends Actor { super.prepareData(); // Update effects if it is the user's character or is controlled - if (canvas.ready) { + // A timeout avoids an infinite loop when accessing token actors before the delta is finished constructing + window.setTimeout(() => { + if (!canvas.ready) return; const controlled = canvas.tokens.controlled.some(t => t.actor === this); if (game.user.character === this || controlled) { - ui.effectsDisplay.render(); + ui.effectsDisplay.refresh(); } - } + }, 0); } /* -------------------------------------------- */ diff --git a/module/documents/token.mjs b/module/documents/token.mjs index 8e91d4f0..d70480db 100644 --- a/module/documents/token.mjs +++ b/module/documents/token.mjs @@ -38,7 +38,8 @@ export default class DHToken extends CONFIG.Token.documentClass { tokens.filter(x => x.actor).map(x => x.actor) ); } - super.createCombatants(tokens, combat ?? {}); + + await super.createCombatants(tokens, combat ?? {}); } /**@inheritdoc */ diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 71f43997..84bcacf2 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -1,6 +1,10 @@ import { diceTypes, getDiceSoNicePresets, getDiceSoNicePreset, range } from '../config/generalConfig.mjs'; import Tagify from '@yaireo/tagify'; +/** + * @import DhpActor from '../documents/actor.mjs'; + */ + export const capitalize = string => { return string.charAt(0).toUpperCase() + string.slice(1); }; @@ -891,6 +895,13 @@ export function shouldUseHopeFearAutomation(options = { gmAsPlayer: true }) { return (!game.user.isGM || options.gmAsPlayer) ? hopeFear.players : hopeFear.gm; } +/** + * Returns the given actor if its a world actor, + * finds a world actor equivalent, + * or imports the actor and returns the imported actor. + * @param {DhpActor} baseActor + * @returns {Promise} a world actor + */ export async function getWorldActor(baseActor) { if (baseActor.inCompendium) { const worldActorCandidates = game.actors.filter(x => From 1dcfc92a0347b20c9b07563beb939e3d6b3e4654 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Fri, 3 Jul 2026 11:16:55 +0200 Subject: [PATCH 19/25] Raised version --- system.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system.json b/system.json index 0ecc2e75..d754b8bf 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "2.4.2", + "version": "2.5.0", "compatibility": { "minimum": "14.364", "verified": "14.364", @@ -10,7 +10,7 @@ }, "url": "https://github.com/Foundryborne/daggerheart", "manifest": "https://raw.githubusercontent.com/Foundryborne/daggerheart/v14/system.json", - "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.4.2/system.zip", + "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.5.0/system.zip", "authors": [ { "name": "WBHarry" From eccab6ad649baa1f84af5517c75b9e40bf48fc6e Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Fri, 3 Jul 2026 05:32:51 -0400 Subject: [PATCH 20/25] [UI] Make sheet tab scrollsbars stable, remove fieldsets, and adjust padding (#1944) * Remove certain fieldsets and adjust scrollsbars * Also remove fieldset for npc features and fix padding issue in full screen notes --- .../applications/sheets/actors/adversary.mjs | 2 +- styles/less/global/elements.less | 2 +- styles/less/global/sheet.less | 2 +- .../sheets/actors/actor-sheet-shared.less | 20 +- .../less/sheets/actors/adversary/effects.less | 5 +- .../sheets/actors/adversary/features.less | 8 +- .../less/sheets/actors/adversary/sheet.less | 2 +- .../sheets/actors/character/biography.less | 11 +- .../less/sheets/actors/character/effects.less | 5 +- .../sheets/actors/character/features.less | 5 +- .../less/sheets/actors/character/header.less | 19 +- .../less/sheets/actors/character/index.less | 2 +- .../sheets/actors/character/inventory.less | 8 +- .../less/sheets/actors/character/loadout.less | 7 +- .../less/sheets/actors/character/sheet.less | 2 +- .../less/sheets/actors/companion/details.less | 5 +- .../less/sheets/actors/companion/effects.less | 5 +- .../sheets/actors/environment/features.less | 8 +- .../less/sheets/actors/environment/index.less | 2 +- .../environment/potentialAdversaries.less | 5 +- .../less/sheets/actors/environment/sheet.less | 2 - styles/less/sheets/actors/npc/features.less | 8 +- .../sheets/actors/party/party-members.less | 3 +- styles/less/sheets/actors/party/sheet.less | 13 +- styles/less/utils/mixin.less | 10 +- .../sheets/actors/adversary/features.hbs | 21 +- .../sheets/actors/character/features.hbs | 50 +-- templates/sheets/actors/character/header.hbs | 326 +++++++++--------- .../sheets/actors/environment/features.hbs | 21 +- templates/sheets/actors/npc/features.hbs | 21 +- 30 files changed, 298 insertions(+), 302 deletions(-) diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs index bcfe3cbb..b6e17ddd 100644 --- a/module/applications/sheets/actors/adversary.mjs +++ b/module/applications/sheets/actors/adversary.mjs @@ -7,7 +7,7 @@ export default class AdversarySheet extends DHBaseActorSheet { /** @inheritDoc */ static DEFAULT_OPTIONS = { classes: ['adversary'], - position: { width: 645, height: 760 }, + position: { width: 645, height: 750 }, window: { resizable: true }, actions: { toggleHitPoints: AdversarySheet.#toggleHitPoints, diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index f7934b71..d570c08c 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -261,7 +261,7 @@ fieldset { align-items: center; - margin-top: 5px; + margin: 5px 0 0 0; border-radius: 6px; border-color: @color-fieldset-border; padding-inline: 0.625rem; diff --git a/styles/less/global/sheet.less b/styles/less/global/sheet.less index e3072da1..8381c7c3 100755 --- a/styles/less/global/sheet.less +++ b/styles/less/global/sheet.less @@ -54,7 +54,7 @@ body.game:is(.performance-low, .noblur) { position: relative; min-height: -webkit-fill-available; transition: opacity 0.3s ease; - padding-bottom: 20px; + padding-bottom: 16px; .tab { padding: 0 10px; diff --git a/styles/less/sheets/actors/actor-sheet-shared.less b/styles/less/sheets/actors/actor-sheet-shared.less index 5eb5b43c..3e233013 100644 --- a/styles/less/sheets/actors/actor-sheet-shared.less +++ b/styles/less/sheets/actors/actor-sheet-shared.less @@ -39,6 +39,20 @@ .window-header > .attribution-header-label { margin-right: var(--spacer-4); + pointer-events: none; + } + + .tab-navigation { + margin-bottom: 0; + } + + .tab { + flex: 1; + padding: 0; + overflow: hidden; + .search-section { + padding: 12px 14px var(--spacer-8) 12px; + } } .tab.inventory { @@ -46,7 +60,7 @@ display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 10px; - padding: 10px 10px 0; + padding: var(--spacer-8) 16px var(--spacer-8) 16px; .input { color: light-dark(@dark, @beige); @@ -57,7 +71,6 @@ .tab.notes.active { padding: 0; margin: 0; - margin-top: -10px; // will be removed once tab-navigation bottom margin is removed on all actor sheets scrollbar-gutter: unset; // Add padding around top level level prosemirrors used for note tabs @@ -66,6 +79,7 @@ .editor-content { scrollbar-gutter: stable; padding-right: @right-padding; + padding-bottom: 4px; } &.inactive { button.toggle { @@ -76,7 +90,7 @@ } } &.active { - padding: 8px 0 4px 16px; + padding: 8px 0 0 16px; } } diff --git a/styles/less/sheets/actors/adversary/effects.less b/styles/less/sheets/actors/adversary/effects.less index 4aa44e51..f489bee6 100644 --- a/styles/less/sheets/actors/adversary/effects.less +++ b/styles/less/sheets/actors/adversary/effects.less @@ -7,9 +7,8 @@ display: flex; flex-direction: column; gap: 10px; - overflow-y: auto; - padding-bottom: 20px; - .with-scroll-shadows(); + padding: 7px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/adversary/features.less b/styles/less/sheets/actors/adversary/features.less index 447d050e..49289d36 100644 --- a/styles/less/sheets/actors/adversary/features.less +++ b/styles/less/sheets/actors/adversary/features.less @@ -5,12 +5,8 @@ .application.sheet.daggerheart.actor.dh-style.adversary { .tab.features { .feature-section { - display: flex; - flex-direction: column; - gap: 10px; - overflow-y: auto; - padding-bottom: 20px; - .with-scroll-shadows(); + padding: 16px calc(16px - var(--scrollbar-width)) 4px 16px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/adversary/sheet.less b/styles/less/sheets/actors/adversary/sheet.less index 639af98b..1eb7d423 100644 --- a/styles/less/sheets/actors/adversary/sheet.less +++ b/styles/less/sheets/actors/adversary/sheet.less @@ -30,9 +30,9 @@ grid-row: 2; grid-column: 2; &.active { - overflow: hidden; display: flex; flex-direction: column; + margin: 0 0 10px 0; } } } \ No newline at end of file diff --git a/styles/less/sheets/actors/character/biography.less b/styles/less/sheets/actors/character/biography.less index 8548a2fb..9782a588 100644 --- a/styles/less/sheets/actors/character/biography.less +++ b/styles/less/sheets/actors/character/biography.less @@ -8,17 +8,14 @@ display: flex; flex-direction: column; gap: 10px; - height: 100%; - overflow-y: auto; - padding-top: 8px; - padding-bottom: 20px; - height: 100%; - .with-scroll-shadows(); + height: 100%; + padding: 12px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); } .characteristics-section { gap: 20px; - padding: 0 10px; + padding: 0 4px; } .biography-section { diff --git a/styles/less/sheets/actors/character/effects.less b/styles/less/sheets/actors/character/effects.less index 0ab1007d..8fac301c 100644 --- a/styles/less/sheets/actors/character/effects.less +++ b/styles/less/sheets/actors/character/effects.less @@ -8,9 +8,8 @@ display: flex; flex-direction: column; gap: 10px; - overflow-y: auto; - padding-bottom: 20px; - .with-scroll-shadows(); + padding: 7px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/character/features.less b/styles/less/sheets/actors/character/features.less index 52b41826..fcde3e38 100644 --- a/styles/less/sheets/actors/character/features.less +++ b/styles/less/sheets/actors/character/features.less @@ -8,9 +8,8 @@ display: flex; flex-direction: column; gap: 10px; - overflow-y: auto; - padding-bottom: 20px; - .with-scroll-shadows(); + padding: 7px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/character/header.less b/styles/less/sheets/actors/character/header.less index 91b3545a..81345715 100644 --- a/styles/less/sheets/actors/character/header.less +++ b/styles/less/sheets/actors/character/header.less @@ -19,16 +19,19 @@ .application.sheet.daggerheart.actor.dh-style.character { .character-header-sheet { - padding: 0 15px; padding-top: var(--header-height); width: 100%; + > *:not(line-div, .tab-navigation) { + padding-left: 15px; + padding-right: 15px; + } + .name-row { display: flex; gap: 6px; align-items: start; justify-content: space-between; - padding: 0; padding-top: 5px; flex: 1; @@ -100,8 +103,8 @@ .character-details { display: flex; justify-content: space-between; - padding: 5px 0; - margin-bottom: 8px; + margin-top: 5px; + margin-bottom: 10px; font-size: var(--font-size-12); color: @color-text-emphatic; @@ -130,7 +133,6 @@ .character-row { display: flex; align-items: center; - padding: 0; margin-bottom: 12px; .resource-section { @@ -218,12 +220,11 @@ .character-traits { display: flex; - padding: 0; margin-bottom: 15px; justify-content: space-between; max-width: 38.5rem; gap: 0.5rem; - padding-left: 0.5rem; + margin-left: 0.5rem; .trait { cursor: pointer; @@ -325,5 +326,9 @@ } } } + + .tab-navigation button[data-action="openSettings"] { + margin-right: 12px; + } } } diff --git a/styles/less/sheets/actors/character/index.less b/styles/less/sheets/actors/character/index.less index edefe0a1..f196d5bf 100644 --- a/styles/less/sheets/actors/character/index.less +++ b/styles/less/sheets/actors/character/index.less @@ -1,8 +1,8 @@ +@import './sheet.less'; @import './biography.less'; @import './effects.less'; @import './features.less'; @import './header.less'; @import './inventory.less'; @import './loadout.less'; -@import './sheet.less'; @import './sidebar.less'; diff --git a/styles/less/sheets/actors/character/inventory.less b/styles/less/sheets/actors/character/inventory.less index fcfbbee9..ce7a8cdb 100644 --- a/styles/less/sheets/actors/character/inventory.less +++ b/styles/less/sheets/actors/character/inventory.less @@ -7,11 +7,9 @@ .items-section { display: flex; flex-direction: column; - gap: 10px; - overflow-y: auto; - margin-top: 20px; - padding-bottom: 20px; - .with-scroll-shadows(); + gap: 10px; + padding: 8px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/character/loadout.less b/styles/less/sheets/actors/character/loadout.less index fa3e0176..cb1baaa6 100644 --- a/styles/less/sheets/actors/character/loadout.less +++ b/styles/less/sheets/actors/character/loadout.less @@ -50,11 +50,8 @@ display: flex; flex-direction: column; gap: 10px; - height: 100%; - overflow-y: auto; - margin-top: 20px; - padding-bottom: 20px; - .with-scroll-shadows(); + padding: 8px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/character/sheet.less b/styles/less/sheets/actors/character/sheet.less index 68792c99..43401b19 100644 --- a/styles/less/sheets/actors/character/sheet.less +++ b/styles/less/sheets/actors/character/sheet.less @@ -30,7 +30,7 @@ &.active { display: flex; flex-direction: column; - overflow: hidden; + margin: 0 0 10px 0; } } } diff --git a/styles/less/sheets/actors/companion/details.less b/styles/less/sheets/actors/companion/details.less index 2e43cac4..e10e7680 100644 --- a/styles/less/sheets/actors/companion/details.less +++ b/styles/less/sheets/actors/companion/details.less @@ -1,7 +1,10 @@ @import '../../../utils/colors.less'; @import '../../../utils/fonts.less'; -.application.sheet.daggerheart.actor.dh-style.companion { +.application.sheet.daggerheart.actor.dh-style.companion .tab.details.active { + padding: 12px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); + .partner-section, .attack-section, .experience-list { diff --git a/styles/less/sheets/actors/companion/effects.less b/styles/less/sheets/actors/companion/effects.less index c0cac669..ffe66da8 100644 --- a/styles/less/sheets/actors/companion/effects.less +++ b/styles/less/sheets/actors/companion/effects.less @@ -6,9 +6,8 @@ display: flex; flex-direction: column; gap: 10px; - overflow-y: auto; - padding-bottom: 20px; - .with-scroll-shadows(); + padding: 7px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/environment/features.less b/styles/less/sheets/actors/environment/features.less index 84cf26f8..c6fe3750 100644 --- a/styles/less/sheets/actors/environment/features.less +++ b/styles/less/sheets/actors/environment/features.less @@ -5,12 +5,8 @@ .application.sheet.daggerheart.actor.dh-style.environment { .tab.features { .feature-section { - display: flex; - flex-direction: column; - gap: 10px; - overflow-y: auto; - padding-bottom: 4px; - .with-scroll-shadows(); + padding: 16px calc(16px - var(--scrollbar-width)) 4px 16px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/environment/index.less b/styles/less/sheets/actors/environment/index.less index 211c8e60..df7e6fc2 100644 --- a/styles/less/sheets/actors/environment/index.less +++ b/styles/less/sheets/actors/environment/index.less @@ -1,4 +1,4 @@ +@import './sheet.less'; @import './features.less'; @import './header.less'; @import './potentialAdversaries.less'; -@import './sheet.less'; diff --git a/styles/less/sheets/actors/environment/potentialAdversaries.less b/styles/less/sheets/actors/environment/potentialAdversaries.less index f112c0d2..07dc5f92 100644 --- a/styles/less/sheets/actors/environment/potentialAdversaries.less +++ b/styles/less/sheets/actors/environment/potentialAdversaries.less @@ -6,9 +6,8 @@ display: flex; flex-direction: column; gap: 10px; - overflow-y: auto; - padding-bottom: 4px; - .with-scroll-shadows(); + padding: 7px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/environment/sheet.less b/styles/less/sheets/actors/environment/sheet.less index 2d9cc188..ef9d542a 100644 --- a/styles/less/sheets/actors/environment/sheet.less +++ b/styles/less/sheets/actors/environment/sheet.less @@ -14,9 +14,7 @@ .application.sheet.daggerheart.actor.dh-style.environment { .tab { - flex: 1; overflow-y: auto; - &.active { overflow: hidden; display: flex; diff --git a/styles/less/sheets/actors/npc/features.less b/styles/less/sheets/actors/npc/features.less index a579d9f8..f68df8a8 100644 --- a/styles/less/sheets/actors/npc/features.less +++ b/styles/less/sheets/actors/npc/features.less @@ -7,12 +7,8 @@ } .feature-section { - display: flex; - flex-direction: column; - gap: 10px; - overflow-y: auto; - padding-bottom: 4px; - .with-scroll-shadows(); + padding: 16px calc(16px - var(--scrollbar-width)) 4px 16px; + .stable-scroll-container(); } } } diff --git a/styles/less/sheets/actors/party/party-members.less b/styles/less/sheets/actors/party/party-members.less index 3d882345..ca384322 100644 --- a/styles/less/sheets/actors/party/party-members.less +++ b/styles/less/sheets/actors/party/party-members.less @@ -3,7 +3,8 @@ @import '../../../utils/mixin.less'; .application.sheet.daggerheart.actor.dh-style.party .tab.partyMembers { - overflow: auto; + padding: 12px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); .actions-section { display: flex; diff --git a/styles/less/sheets/actors/party/sheet.less b/styles/less/sheets/actors/party/sheet.less index d24c712c..bf589696 100644 --- a/styles/less/sheets/actors/party/sheet.less +++ b/styles/less/sheets/actors/party/sheet.less @@ -17,15 +17,8 @@ }); .application.sheet.daggerheart.actor.dh-style.party { - .tab { - flex: 1; - overflow-y: auto; - scrollbar-gutter: stable; - - &.active { - overflow: auto; - display: flex; - flex-direction: column; - } + .tab.active { + display: flex; + flex-direction: column; } } diff --git a/styles/less/utils/mixin.less b/styles/less/utils/mixin.less index e2ef85ef..2ce85166 100644 --- a/styles/less/utils/mixin.less +++ b/styles/less/utils/mixin.less @@ -174,10 +174,10 @@ --fade-start: 0; } 10%, 100% { - --fade-start: 12px; + --fade-start: 14px; } 0%, 90% { - --fade-end: 12px; + --fade-end: 14px; } 100% { --fade-end: 0; @@ -198,3 +198,9 @@ transparent 100% ); } + +.stable-scroll-container() { + overflow-y: auto; + scrollbar-gutter: stable; + .with-scroll-shadows(); +} \ No newline at end of file diff --git a/templates/sheets/actors/adversary/features.hbs b/templates/sheets/actors/adversary/features.hbs index 3b495e74..05e95737 100644 --- a/templates/sheets/actors/adversary/features.hbs +++ b/templates/sheets/actors/adversary/features.hbs @@ -1,14 +1,15 @@
-
- {{> 'daggerheart.inventory-items' - title=tabs.features.label - type='feature' - collection=@root.features - hideContextMenu=true - hideModifyControls=true - canCreate=@root.editable - showActions=@root.editable - }} +
+ {{#each @root.features as |item|}} + {{> "daggerheart.inventory-item" + item=item + type="feature" + actorType=@root.document.type + hideContextMenu=true + hideModifyControls=true + showActions=@root.editable + }} + {{/each}}
\ No newline at end of file diff --git a/templates/sheets/actors/character/features.hbs b/templates/sheets/actors/character/features.hbs index b2760900..c96ab623 100644 --- a/templates/sheets/actors/character/features.hbs +++ b/templates/sheets/actors/character/features.hbs @@ -1,26 +1,26 @@ -
-
- {{#each document.system.sheetLists as |category|}} - {{#if (eq category.type 'feature' )}} - {{> 'daggerheart.inventory-items' - title=category.title - type='feature' - actorType='character' - collection=category.values - canCreate=@root.editable - showActions=@root.editable - }} - {{else if category.values}} - {{> 'daggerheart.inventory-items' - title=category.title - type='feature' - actorType='character' - collection=category.values - canCreate=false - showActions=@root.editable - }} - {{/if}} - {{/each}} -
+
+
+ {{#each document.system.sheetLists as |category|}} + {{#if (eq category.type 'feature' )}} + {{> 'daggerheart.inventory-items' + title=category.title + type='feature' + actorType='character' + collection=category.values + canCreate=@root.editable + showActions=@root.editable + }} + {{else if category.values}} + {{> 'daggerheart.inventory-items' + title=category.title + type='feature' + actorType='character' + collection=category.values + canCreate=false + showActions=@root.editable + }} + {{/if}} + {{/each}} +
\ No newline at end of file diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index 459911af..a40c336a 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -1,165 +1,163 @@ -
- -
-

{{source.name}}

-
-

- {{#if @root.editable}} - {{#if document.system.needsCharacterSetup}} - - {{else if document.system.levelData.canLevelUp}} - - {{/if}} - {{/if}} - {{#unless document.system.needsCharacterSetup}} - {{localize 'DAGGERHEART.GENERAL.level'}} - - {{/unless}} -

-
-
-
-
- {{#if document.system.class.value}} - {{document.system.class.value.name}} - {{else}} - {{localize 'TYPES.Item.class'}} - {{/if}} - - {{#if document.system.class.subclass}} - {{document.system.class.subclass.name}} - {{else}} - {{localize 'TYPES.Item.subclass'}} - {{/if}} - - {{#if document.system.community}} - {{document.system.community.name}} - {{else}} - {{localize 'TYPES.Item.community'}} - {{/if}} - - {{#if document.system.ancestry}} - {{document.system.ancestry.name}} - {{else}} - {{localize 'TYPES.Item.ancestry'}} - {{/if}} -
- - {{#if (or document.system.multiclass.value document.system.multiclass.subclass)}} -
- {{#if document.system.multiclass.value}} - {{document.system.multiclass.value.name}} - {{else}} - {{localize 'DAGGERHEART.GENERAL.multiclass'}} - {{/if}} - - {{#if document.system.multiclass.subclass}} - {{document.system.multiclass.subclass.name}} - {{else}} - {{localize 'TYPES.Item.subclass'}} - {{/if}} -
- {{/if}} - - -
- -
-
-
-

{{localize "DAGGERHEART.GENERAL.hope"}}

- {{#times document.system.resources.hope.max}} - - {{#if (gte ../document.system.resources.hope.value (add this 1))}} - - {{else}} - - {{/if}} - - {{/times}} - {{#times document.system.scars}} - - - - {{/times}} - {{#if hasExtraResources}}{{/if}} -
-
- {{#if document.system.class.value}} -
- {{#each document.system.domainData as |data|}} -
- -
- {{/each}} -
- {{/if}} -
- {{#if document.parties.size}} - - {{/if}} - {{#if @root.editable}} - - - {{/if}} -
-
- -
- {{#each this.attributes as |attribute key|}} -
-
-
- {{attribute.label}} -
-
- - - - -
- {{#if (gt attribute.value 0)}} - +{{attribute.value}} - {{else}} - {{attribute.value}} - {{/if}} -
- {{#if isSpellcasting}} -
- -
- {{/if}} -
-
- {{/each}} -
- - {{#> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }} - - {{/'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}} +
+ +
+

{{source.name}}

+
+

+ {{#if @root.editable}} + {{#if document.system.needsCharacterSetup}} + + {{else if document.system.levelData.canLevelUp}} + + {{/if}} + {{/if}} + {{#unless document.system.needsCharacterSetup}} + {{localize 'DAGGERHEART.GENERAL.level'}} + + {{/unless}} +

+
+
+
+
+ {{#if document.system.class.value}} + {{document.system.class.value.name}} + {{else}} + {{localize 'TYPES.Item.class'}} + {{/if}} + + {{#if document.system.class.subclass}} + {{document.system.class.subclass.name}} + {{else}} + {{localize 'TYPES.Item.subclass'}} + {{/if}} + + {{#if document.system.community}} + {{document.system.community.name}} + {{else}} + {{localize 'TYPES.Item.community'}} + {{/if}} + + {{#if document.system.ancestry}} + {{document.system.ancestry.name}} + {{else}} + {{localize 'TYPES.Item.ancestry'}} + {{/if}} +
+ + {{#if (or document.system.multiclass.value document.system.multiclass.subclass)}} +
+ {{#if document.system.multiclass.value}} + {{document.system.multiclass.value.name}} + {{else}} + {{localize 'DAGGERHEART.GENERAL.multiclass'}} + {{/if}} + + {{#if document.system.multiclass.subclass}} + {{document.system.multiclass.subclass.name}} + {{else}} + {{localize 'TYPES.Item.subclass'}} + {{/if}} +
+ {{/if}} +
+ +
+
+
+

{{localize "DAGGERHEART.GENERAL.hope"}}

+ {{#times document.system.resources.hope.max}} + + {{#if (gte ../document.system.resources.hope.value (add this 1))}} + + {{else}} + + {{/if}} + + {{/times}} + {{#times document.system.scars}} + + + + {{/times}} + {{#if hasExtraResources}}{{/if}} +
+
+ {{#if document.system.class.value}} +
+ {{#each document.system.domainData as |data|}} +
+ +
+ {{/each}} +
+ {{/if}} +
+ {{#if document.parties.size}} + + {{/if}} + {{#if @root.editable}} + + + {{/if}} +
+
+ +
+ {{#each this.attributes as |attribute key|}} +
+
+
+ {{attribute.label}} +
+
+ + + + +
+ {{#if (gt attribute.value 0)}} + +{{attribute.value}} + {{else}} + {{attribute.value}} + {{/if}} +
+ {{#if isSpellcasting}} +
+ +
+ {{/if}} +
+
+ {{/each}} +
+ + {{#> 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }} + + {{/'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs'}}
\ No newline at end of file diff --git a/templates/sheets/actors/environment/features.hbs b/templates/sheets/actors/environment/features.hbs index 35fcb038..85fa7066 100644 --- a/templates/sheets/actors/environment/features.hbs +++ b/templates/sheets/actors/environment/features.hbs @@ -3,15 +3,16 @@ data-tab='{{tabs.features.id}}' data-group='{{tabs.features.group}}' > -
- {{> 'daggerheart.inventory-items' - title=tabs.features.label - type='feature' - collection=@root.features - hideContextMenu=true - hideModifyControls=true - canCreate=@root.editable - showActions=@root.editable - }} +
+ {{#each @root.features as |item|}} + {{> "daggerheart.inventory-item" + item=item + type="feature" + actorType=@root.document.type + hideContextMenu=true + hideModifyControls=true + showActions=@root.editable + }} + {{/each}}
\ No newline at end of file diff --git a/templates/sheets/actors/npc/features.hbs b/templates/sheets/actors/npc/features.hbs index 3b495e74..05e95737 100644 --- a/templates/sheets/actors/npc/features.hbs +++ b/templates/sheets/actors/npc/features.hbs @@ -1,14 +1,15 @@
-
- {{> 'daggerheart.inventory-items' - title=tabs.features.label - type='feature' - collection=@root.features - hideContextMenu=true - hideModifyControls=true - canCreate=@root.editable - showActions=@root.editable - }} +
+ {{#each @root.features as |item|}} + {{> "daggerheart.inventory-item" + item=item + type="feature" + actorType=@root.document.type + hideContextMenu=true + hideModifyControls=true + showActions=@root.editable + }} + {{/each}}
\ No newline at end of file From cb43ffed9b0913ae757a2bf1160b96f57cf6b8cc Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Mon, 6 Jul 2026 00:36:05 +0200 Subject: [PATCH 21/25] Corrected the description of the vicious maul action (#2065) --- .../beastforms/feature_Vicious_Maul_jYUBi7yLHap5ljpa.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packs/beastforms/feature_Vicious_Maul_jYUBi7yLHap5ljpa.json b/src/packs/beastforms/feature_Vicious_Maul_jYUBi7yLHap5ljpa.json index a3494ed0..f9ec4933 100644 --- a/src/packs/beastforms/feature_Vicious_Maul_jYUBi7yLHap5ljpa.json +++ b/src/packs/beastforms/feature_Vicious_Maul_jYUBi7yLHap5ljpa.json @@ -10,7 +10,7 @@ "type": "damage", "_id": "Cc53vyYz5ggKdIbx", "systemPath": "actions", - "description": "

When you succeed on an attack against a target within Melee range, you can spend a Hope to clamp that opponent in your jaws, making them temporarily Restrained and Vulnerable.

", + "description": "", "chatDisplay": true, "actionType": "action", "cost": [ @@ -23,7 +23,7 @@ ], "uses": { "value": null, - "max": null, + "max": "", "recovery": null }, "damage": { From 5889476b64d48036b2e41188a40ad2ea2d82324a Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Mon, 6 Jul 2026 20:05:16 +0200 Subject: [PATCH 22/25] [Fix] SRD Towering Stalk Fix (#2063) * Removed a faulty research cost on the attack action of Towering Stalk * . --- ...nCard_Towering_Stalk_n0P3VS1WfxvmXbB6.json | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/packs/domains/domainCard_Towering_Stalk_n0P3VS1WfxvmXbB6.json b/src/packs/domains/domainCard_Towering_Stalk_n0P3VS1WfxvmXbB6.json index 70a1fbda..e154df8f 100644 --- a/src/packs/domains/domainCard_Towering_Stalk_n0P3VS1WfxvmXbB6.json +++ b/src/packs/domains/domainCard_Towering_Stalk_n0P3VS1WfxvmXbB6.json @@ -19,20 +19,20 @@ "actionType": "action", "cost": [ { - "key": "stress", - "itemId": null, - "value": 1, + "consumeOnSuccess": false, "scalable": false, - "step": null, - "consumeOnSuccess": false + "key": "stress", + "value": 1, + "itemId": null, + "step": null }, { - "key": "resource", - "itemId": "n0P3VS1WfxvmXbB6", - "value": 1, + "consumeOnSuccess": false, "scalable": false, - "step": null, - "consumeOnSuccess": false + "key": "resource", + "value": 1, + "itemId": "n0P3VS1WfxvmXbB6", + "step": null } ], "uses": { @@ -119,16 +119,17 @@ "cost": [ { "scalable": false, - "key": "hitPoints", + "key": "resource", "value": 1, + "itemId": "n0P3VS1WfxvmXbB6", "step": null, "consumeOnSuccess": false } ], "uses": { "value": null, - "max": "1", - "recovery": "shortRest" + "max": "", + "recovery": null }, "effects": [], "target": { @@ -140,7 +141,15 @@ "range": "" } }, - "resource": null, + "resource": { + "type": "simple", + "value": 1, + "max": "1", + "recovery": "shortRest", + "progression": "decreasing", + "dieFaces": "d4", + "icon": "fa-solid fa-seedling" + }, "attribution": { "source": "Daggerheart SRD", "page": 130, From 67b0d036a1836237b3a7e84abcc23106dbda4ba7 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Mon, 6 Jul 2026 20:06:08 +0200 Subject: [PATCH 23/25] Fixed so that a character having ReduceSeverity or ThresholdImmunity rules are considered for popping the DamageReductionDialog (#2064) --- module/documents/actor.mjs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 30901374..f8880597 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -622,22 +622,30 @@ export default class DhpActor extends Actor { return rollData; } - #canReduceDamage(hpDamage, type) { - const { stressDamageReduction, disabledArmor } = this.system.rules.damageReduction; + #canReduceDamage(hpDamage, types) { + const { stressDamageReduction, disabledArmor, reduceSeverity, thresholdImmunities } = + this.system.rules.damageReduction; if (disabledArmor) return false; const availableStress = this.system.resources.stress.max - this.system.resources.stress.value; const canUseArmor = this.system.armorScore.value < this.system.armorScore.max && - type.every(t => this.system.armorApplicableDamageTypes[t] === true); + types.every(t => this.system.armorApplicableDamageTypes[t] === true); + const canUseStress = Object.keys(stressDamageReduction).reduce((acc, x) => { const rule = stressDamageReduction[x]; if (damageKeyToNumber(x) <= hpDamage) return acc || (rule.enabled && availableStress >= rule.cost); return acc; }, false); - return canUseArmor || canUseStress; + const hasReduceSeverity = types.some(t => reduceSeverity[t]); + + const hasThresholdImmunity = Object.entries(thresholdImmunities) + .filter(([key, value]) => Boolean(value) && damageKeyToNumber(key) === hpDamage) + .length; + + return canUseArmor || canUseStress || hasReduceSeverity || hasThresholdImmunity; } async takeDamage(damages, isDirect = false) { From 7e6fc03230f862ee70a5a46c5543f68e4e5c4f86 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Mon, 6 Jul 2026 14:08:22 -0400 Subject: [PATCH 24/25] Fix padding of party sheet inventory (#2066) --- styles/less/sheets/actors/character/inventory.less | 2 +- styles/less/sheets/actors/party/inventory.less | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/styles/less/sheets/actors/character/inventory.less b/styles/less/sheets/actors/character/inventory.less index ce7a8cdb..2e40b349 100644 --- a/styles/less/sheets/actors/character/inventory.less +++ b/styles/less/sheets/actors/character/inventory.less @@ -7,7 +7,7 @@ .items-section { display: flex; flex-direction: column; - gap: 10px; + gap: 10px; padding: 8px calc(12px - var(--scrollbar-width)) 4px 12px; .stable-scroll-container(); } diff --git a/styles/less/sheets/actors/party/inventory.less b/styles/less/sheets/actors/party/inventory.less index 444c6a57..a0db5dfe 100644 --- a/styles/less/sheets/actors/party/inventory.less +++ b/styles/less/sheets/actors/party/inventory.less @@ -8,10 +8,8 @@ display: flex; flex-direction: column; gap: 10px; - overflow-y: auto; - margin-top: 20px; - padding-bottom: 4px; - .with-scroll-shadows(); + padding: 8px calc(12px - var(--scrollbar-width)) 4px 12px; + .stable-scroll-container(); } } } From 4b5550a097d49187b8526d586c29ffc889d3caa3 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Mon, 6 Jul 2026 20:08:44 +0200 Subject: [PATCH 25/25] Added Resource capability to armor items (#2060) --- module/data/item/armor.mjs | 3 ++- templates/sheets/actors/character/inventory.hbs | 1 - templates/sheets/items/armor/settings.hbs | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index 15bb620d..70fbbb47 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -8,7 +8,8 @@ export default class DHArmor extends AttachableItem { type: 'armor', hasDescription: true, isInventoryItem: true, - hasActions: true + hasActions: true, + hasResource: true }); } diff --git a/templates/sheets/actors/character/inventory.hbs b/templates/sheets/actors/character/inventory.hbs index 61f98ab5..8c84ee3b 100644 --- a/templates/sheets/actors/character/inventory.hbs +++ b/templates/sheets/actors/character/inventory.hbs @@ -30,7 +30,6 @@ collection=@root.inventory.armor isGlassy=true canCreate=@root.editable - hideResources=true }} {{> 'daggerheart.inventory-items' title='TYPES.Item.consumable' diff --git a/templates/sheets/items/armor/settings.hbs b/templates/sheets/items/armor/settings.hbs index 51bf1746..7ec6c109 100644 --- a/templates/sheets/items/armor/settings.hbs +++ b/templates/sheets/items/armor/settings.hbs @@ -19,4 +19,6 @@ {{ formField systemFields.baseThresholds.fields.severe value=source.system.baseThresholds.severe label=(localize "DAGGERHEART.ITEMS.Armor.baseThresholds.severe") }}
+ + {{> "systems/daggerheart/templates/sheets/global/partials/resource-section/resource-section.hbs" }} \ No newline at end of file