From c91d53b4d4e22c161e2f629c96a3791ae388731d Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Mon, 4 May 2026 06:32:02 -0400 Subject: [PATCH 01/14] Avoid shifting elements when mousing over a tags (#1866) --- styles/less/global/elements.less | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index afd56057..ff668bfd 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -251,8 +251,7 @@ a:hover, a.active { - font-weight: bold; - text-shadow: 0 0 8px light-dark(@dark-blue, @golden); + text-shadow: 0 0 1px currentColor, 0 0 1px currentColor, 0 0 8px light-dark(@dark-blue, @golden); } fieldset { From e95ea3c281dfabf9d198870bfd1b5efa1b0b4803 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Mon, 4 May 2026 06:34:52 -0400 Subject: [PATCH 02/14] [UI] Rework styling of traits section of the character sheet (#1865) * Rework styling of traits section of the character sheet * Adjust spacing in header a smidge --- .../applications/sheets/actors/character.mjs | 5 +- .../less/sheets/actors/character/header.less | 65 +++++++++++++------ .../less/sheets/actors/character/sidebar.less | 56 ---------------- styles/less/utils/fonts.less | 3 + templates/sheets/actors/character/header.hbs | 19 ++++-- templates/sheets/actors/character/sidebar.hbs | 11 ---- 6 files changed, 62 insertions(+), 97 deletions(-) diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 513ad3f3..b20fe513 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -209,8 +209,9 @@ export default class CharacterSheet extends DHBaseActorSheet { context.attributes = Object.keys(this.document.system.traits).reduce((acc, key) => { acc[key] = { ...this.document.system.traits[key], - name: game.i18n.localize(CONFIG.DH.ACTOR.abilities[key].name), - verbs: CONFIG.DH.ACTOR.abilities[key].verbs.map(x => game.i18n.localize(x)) + label: _loc(CONFIG.DH.ACTOR.abilities[key].label), + verbs: CONFIG.DH.ACTOR.abilities[key].verbs.map(x => game.i18n.localize(x)), + isSpellcasting: this.document.system.spellcastModifierTrait.key === key }; return acc; diff --git a/styles/less/sheets/actors/character/header.less b/styles/less/sheets/actors/character/header.less index 31fd4256..ffd01c83 100644 --- a/styles/less/sheets/actors/character/header.less +++ b/styles/less/sheets/actors/character/header.less @@ -5,20 +5,12 @@ // Theme header backgrounds .appTheme({ .character-header-sheet { - .trait { - background: url(../assets/svg/trait-shield.svg) no-repeat; - } - .character-row .domains-section img { filter: @golden-filter; } } }, { .character-header-sheet { - .trait { - background: url('../assets/svg/trait-shield-light.svg') no-repeat; - } - .character-row .domains-section img { filter: brightness(0) saturate(100%); } @@ -101,7 +93,7 @@ display: flex; justify-content: space-between; padding: 5px 0; - margin-bottom: 10px; + margin-bottom: 8px; font-size: var(--font-size-12); color: light-dark(@dark-blue, @golden); @@ -131,7 +123,7 @@ display: flex; align-items: center; padding: 0; - margin-bottom: 15px; + margin-bottom: 12px; .resource-section { display: flex; @@ -217,37 +209,68 @@ .character-traits { display: flex; - justify-content: space-between; padding: 0; margin-bottom: 15px; + gap: 8px; .trait { - height: 60px; - width: 60px; + height: 3.625rem; cursor: pointer; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + flex: 1; + position: relative; + background-color: light-dark(transparent, @dark-blue); + + display: flex; + justify-content: center; + flex-direction: column; .trait-name { display: flex; align-items: center; padding-top: 5px; color: light-dark(@dark-blue, @golden); - font-size: var(--font-size-14); + font-size: var(--font-size-12); font-weight: 600; align-items: center; justify-content: center; - gap: 3px; - - i { - line-height: 17px; - font-size: var(--font-size-10); - } } .trait-value { font-style: normal; - font-weight: 400; + font-weight: 600; font-size: var(--font-size-20); text-align: center; + margin-bottom: 0.375rem; + } + + .tier-mark, + .spellcasting-mark { + position: absolute; + opacity: 0.9; + color: light-dark(@dark-blue, @golden); + i { + line-height: 17px; + font-size: var(--font-size-11); + } + } + + .tier-mark { + bottom: 1px; + left: 3px; + } + + .spellcasting-mark { + bottom: 1px; + right: 3px; + } + + &:hover { + .trait-name { + color: light-dark(@dark, @beige); + text-shadow: 0 0 8px light-dark(@dark-80, @beige-80); + } } } } diff --git a/styles/less/sheets/actors/character/sidebar.less b/styles/less/sheets/actors/character/sidebar.less index b159a8e8..0e6e3d97 100644 --- a/styles/less/sheets/actors/character/sidebar.less +++ b/styles/less/sheets/actors/character/sidebar.less @@ -74,62 +74,6 @@ .death-roll-btn { display: none; } - - .icons-list { - position: absolute; - display: flex; - flex-direction: column; - gap: 5px; - align-items: end; - justify-content: center; - top: 45px; - right: 10px; - - .spellcast-icon { - display: flex; - align-items: center; - justify-content: end; - text-align: center; - padding-right: 8px; - max-width: 50px; - height: 50px; - font-size: 1.2rem; - background: light-dark(@dark-blue-60, @dark-golden-80); - backdrop-filter: blur(8px); - border: 4px double light-dark(@beige, @golden); - color: light-dark(@beige, @golden); - border-radius: 999px; - transition: all 0.3s ease; - - .spellcast-label { - font-size: var(--font-size-14); - opacity: 0; - margin-right: 0.3rem; - transition: all 0.3s ease; - } - - i { - height: 24px; - width: 24px; - align-content: center; - margin-right: 3px; - } - - &:not(.no-label):hover { - max-width: 300px; - padding: 0 10px; - border-radius: 60px; - - .spellcast-label { - opacity: 1; - } - - i { - margin-right: 0px; - } - } - } - } } .info-section { diff --git a/styles/less/utils/fonts.less b/styles/less/utils/fonts.less index 5c1e597a..d4902508 100755 --- a/styles/less/utils/fonts.less +++ b/styles/less/utils/fonts.less @@ -5,6 +5,9 @@ --dh-font-title: 'Cinzel Decorative'; --dh-font-subtitle: 'Cinzel'; --dh-font-body: 'Montserrat'; + + /* Include missing font sizes */ + --font-size-22: 1.375rem; } @font-title: ~"var(--dh-font-title, 'Cinzel Decorative'), serif"; diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index a75b2c2f..1b1c4965 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -125,14 +125,9 @@
{{#each this.attributes as |attribute key|}} -
+
- {{localize (concat 'DAGGERHEART.CONFIG.Traits.' key '.short')}} - {{#if attribute.tierMarked}} - - {{else}} - - {{/if}} + {{attribute.label}}
{{#if (gt attribute.value 0)}} @@ -141,6 +136,16 @@ {{attribute.value}} {{/if}}
+ {{#if attribute.tierMarked}} +
+ +
+ {{/if}} + {{#if isSpellcasting}} +
+ +
+ {{/if}}
{{/each}}
diff --git a/templates/sheets/actors/character/sidebar.hbs b/templates/sheets/actors/character/sidebar.hbs index a0f93121..91779e9c 100644 --- a/templates/sheets/actors/character/sidebar.hbs +++ b/templates/sheets/actors/character/sidebar.hbs @@ -1,17 +1,6 @@
- + + {{/each}} + + {{else}} + + {{/if}} {{#unless document.system.partyMembers.length}}
{{localize "DAGGERHEART.GENERAL.dropActorsHere"}} From 0128106de63e54ad55e45ecadd78a8dbda8e6715 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Mon, 4 May 2026 06:40:29 -0400 Subject: [PATCH 04/14] Don't show unusable non-weapons in character sidebar (#1862) --- module/applications/sheets/actors/character.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index b20fe513..7adffa1e 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -228,7 +228,7 @@ export default class CharacterSheet extends DHBaseActorSheet { context.resources.stress.max < maxResource ? maxResource - context.resources.stress.max : 0; context.equippedItems = sortBy( - this.document.items.filter(i => i.system.equipped), + this.document.items.filter(i => i.system.equipped && (i.type === 'weapon' || i.usable)), i => (i.type === 'weapon' ? (i.system.secondary ? 1 : 0) : 2) ); From 94852cec2168491387fd361e3345aa1643b26add Mon Sep 17 00:00:00 2001 From: WBHarry Date: Mon, 4 May 2026 16:45:17 +0200 Subject: [PATCH 05/14] Fixed character sheet error:ing out if opened when the character doesn't have a spellcastModifierTrait --- module/applications/sheets/actors/character.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 7adffa1e..f40c144a 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -211,7 +211,7 @@ export default class CharacterSheet extends DHBaseActorSheet { ...this.document.system.traits[key], label: _loc(CONFIG.DH.ACTOR.abilities[key].label), verbs: CONFIG.DH.ACTOR.abilities[key].verbs.map(x => game.i18n.localize(x)), - isSpellcasting: this.document.system.spellcastModifierTrait.key === key + isSpellcasting: this.document.system.spellcastModifierTrait?.key === key }; return acc; From cca468e8af8ef79e44b082361e68f758132c187e Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 5 May 2026 21:21:27 +0200 Subject: [PATCH 06/14] Raised foundry version --- system.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system.json b/system.json index ac581343..f7614790 100644 --- a/system.json +++ b/system.json @@ -5,7 +5,7 @@ "version": "2.2.3", "compatibility": { "minimum": "14.359", - "verified": "14.360", + "verified": "14.361", "maximum": "14" }, "url": "https://github.com/Foundryborne/daggerheart", From fb5e3672dce1a9742bad2b56423ccd77e8b3ab8b Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Tue, 5 May 2026 16:14:45 -0400 Subject: [PATCH 07/14] Disable spellcheck and autocorrect on all adversary sheets (#1869) --- templates/sheets/actors/adversary/header.hbs | 9 ++++++++- templates/sheets/actors/character/header.hbs | 9 ++++++++- templates/sheets/actors/companion/header.hbs | 2 ++ templates/sheets/actors/party/header.hbs | 4 +++- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/templates/sheets/actors/adversary/header.hbs b/templates/sheets/actors/adversary/header.hbs index 5bdfa421..fba96980 100644 --- a/templates/sheets/actors/adversary/header.hbs +++ b/templates/sheets/actors/adversary/header.hbs @@ -1,7 +1,14 @@
-

{{source.name}}

+

{{source.name}}

diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index 1b1c4965..8010dfa5 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -1,7 +1,14 @@
-

{{source.name}}

+

{{source.name}}

{{#if @root.editable}} diff --git a/templates/sheets/actors/companion/header.hbs b/templates/sheets/actors/companion/header.hbs index 8b63e396..d10c0640 100644 --- a/templates/sheets/actors/companion/header.hbs +++ b/templates/sheets/actors/companion/header.hbs @@ -6,6 +6,8 @@ name='name' value='{{document.name}}' placeholder='{{localize "DAGGERHEART.GENERAL.actorName"}}' + autocomplete="off" + spellcheck="false" />

{{#if useResourcePips}} diff --git a/templates/sheets/actors/party/header.hbs b/templates/sheets/actors/party/header.hbs index 3fdb137d..c48902c8 100644 --- a/templates/sheets/actors/party/header.hbs +++ b/templates/sheets/actors/party/header.hbs @@ -2,7 +2,9 @@
-

+

+ +

\ No newline at end of file From b7bc452bf5fd801781f5e859b09db01fc03d47c8 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Tue, 5 May 2026 22:15:21 +0200 Subject: [PATCH 08/14] [Fix] Improve Class-Subclass Linkage (#1846) * Initial thoughts * . * Fixed linting * Continue work on updating identifier * Change to uuid approach * Localization and minor fix * Fixed CompendiumBrowser Class filter for Subclass view * Fixed the class name display in the subclass view * Improved missing class visual for subclass * Fixed character creation * Rerender class sheets when subclass link is changed * Use compendium source over actual uuid in search --------- Co-authored-by: Carlos Fernandez --- lang/en.json | 5 +- .../characterCreation/characterCreation.mjs | 10 ++-- module/applications/sheets/items/class.mjs | 27 ++-------- module/applications/sheets/items/subclass.mjs | 32 ++++++++++++ module/applications/ui/itemBrowser.mjs | 11 ++-- module/config/itemBrowserConfig.mjs | 22 ++++---- module/data/item/base.mjs | 2 +- module/data/item/class.mjs | 19 ++++++- module/data/item/subclass.mjs | 5 +- module/helpers/utils.mjs | 8 +++ styles/less/global/feature-section.less | 50 +++++++++++-------- .../less/sheets/items/item-sheet-shared.less | 4 ++ templates/sheets/items/class/features.hbs | 16 +----- templates/sheets/items/class/header.hbs | 1 - .../sheets/items/subclass/description.hbs | 10 ++-- templates/sheets/items/subclass/features.hbs | 29 +++++++++++ 16 files changed, 167 insertions(+), 84 deletions(-) diff --git a/lang/en.json b/lang/en.json index aff0d60a..20c66a32 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2442,6 +2442,7 @@ "single": "Miss", "plural": "Miss" }, + "missingX": "Missing {x}", "maxWithThing": "Max {thing}", "missingDragDropThing": "Drop {thing} here", "multiclass": "Multiclass", @@ -2532,6 +2533,9 @@ "recovery": { "label": "Recovery" }, "type": { "label": "Type" }, "value": { "label": "Value" } + }, + "identifier": { + "label": "Identifier" } }, "Ancestry": { @@ -3219,7 +3223,6 @@ "subclassesAlreadyPresent": "You already have a class and multiclass subclass", "noDiceSystem": "Your selected dice {system} does not have a {faces} dice", "gmMenuRefresh": "You refreshed all actions and resources {types}", - "subclassAlreadyLinked": "{name} is already a subclass in the class {class}. Remove it from there if you want it to be a subclass to this class.", "gmRequired": "This action requires an online GM", "gmOnly": "This can only be accessed by the GM", "noActorOwnership": "You do not have permissions for this character", diff --git a/module/applications/characterCreation/characterCreation.mjs b/module/applications/characterCreation/characterCreation.mjs index 936bb79d..82ca9ccb 100644 --- a/module/applications/characterCreation/characterCreation.mjs +++ b/module/applications/characterCreation/characterCreation.mjs @@ -439,10 +439,13 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl 'system.domain': { key: 'system.domain', value: this.setup.class?.system.domains ?? null } }; - if (type === 'subclasses') + if (type === 'subclasses') { + const classItem = this.setup.class; + const uuid = classItem?._stats.compendiumSource ?? classItem?.uuid; presets.filter = { - 'system.linkedClass.uuid': { key: 'system.linkedClass.uuid', value: this.setup.class?.uuid } + 'system.linkedClass': { key: 'system.linkedClass', value: uuid } }; + } if (equipment.includes(type)) presets.filter = { @@ -610,7 +613,8 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl [foundry.utils.randomID()]: {} }; } else if (item.type === 'subclass' && event.target.closest('.subclass-card')) { - if (this.setup.class.system.subclasses.every(subclass => subclass.uuid !== item.uuid)) { + const classSubclasses = await this.setup.class.system.fetchSubclasses(); + if (classSubclasses.every(subclass => subclass.uuid !== item.uuid)) { ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.subclassNotInClass')); return; } diff --git a/module/applications/sheets/items/class.mjs b/module/applications/sheets/items/class.mjs index 05bb0229..25c631fe 100644 --- a/module/applications/sheets/items/class.mjs +++ b/module/applications/sheets/items/class.mjs @@ -104,9 +104,10 @@ export default class ClassSheet extends DHBaseItemSheet { } /**@inheritdoc */ - async _prepareContext(_options) { - const context = await super._prepareContext(_options); + async _prepareContext(options) { + const context = await super._prepareContext(options); context.domains = this.document.system.domains; + context.subclasses = await this.document.system.fetchSubclasses(); return context; } @@ -128,20 +129,8 @@ export default class ClassSheet extends DHBaseItemSheet { const item = await fromUuid(data.uuid); const itemType = data.type === 'ActiveEffect' ? data.type : item.type; const target = event.target.closest('fieldset.drop-section'); - if (itemType === 'subclass') { - if (item.system.linkedClass) { - return ui.notifications.warn( - game.i18n.format('DAGGERHEART.UI.Notifications.subclassAlreadyLinked', { - name: item.name, - class: this.document.name - }) - ); - } - await item.update({ 'system.linkedClass': this.document.uuid }); - await this.document.update({ - 'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid] - }); - } else if (['feature', 'ActiveEffect'].includes(itemType)) { + + if (['feature', 'ActiveEffect'].includes(itemType)) { super._onDrop(event); } else if (this.document.parent?.type !== 'character') { if (itemType === 'weapon') { @@ -200,12 +189,6 @@ export default class ClassSheet extends DHBaseItemSheet { static async #removeItemFromCollection(_event, element) { const { uuid, target } = element.dataset; const prop = foundry.utils.getProperty(this.document.system, target); - - if (target === 'subclasses') { - const subclass = await foundry.utils.fromUuid(uuid); - await subclass?.update({ 'system.linkedClass': null }); - } - await this.document.update({ [`system.${target}`]: prop.filter(i => i && i.uuid !== uuid).map(x => x.uuid) }); } diff --git a/module/applications/sheets/items/subclass.mjs b/module/applications/sheets/items/subclass.mjs index 5c731777..e9d8370e 100644 --- a/module/applications/sheets/items/subclass.mjs +++ b/module/applications/sheets/items/subclass.mjs @@ -40,4 +40,36 @@ export default class SubclassSheet extends DHBaseItemSheet { get relatedDocs() { return this.document.system.features.map(x => x.item); } + + async _prepareContext(options) { + const context = await super._prepareContext(options); + if (this.document.system.linkedClass) { + const classData = await fromUuid(this.document.system.linkedClass); + context.class = classData ?? { + name: _loc('DAGGERHEART.GENERAL.missingX', { x: _loc('TYPES.Item.class') }), + missing: true + }; + } + return context; + } + + async _onDrop(event) { + event.stopPropagation(); + const data = TextEditor.getDragEventData(event); + const item = await fromUuid(data.uuid); + const itemType = data.type === 'ActiveEffect' ? data.type : item.type; + if (itemType === 'class') { + const uuid = item._stats.compendiumSource ?? item.uuid; + if (this.document.system.linkedClass !== uuid) { + await this.document.update({ 'system.linkedClass': uuid }); + // Re-render all class sheets for instant feedback + for (const app of foundry.applications.instances.values()) { + if (app.document?.type === 'class') app.render(); + } + } + return; + } + + return super._onDrop(event); + } } diff --git a/module/applications/ui/itemBrowser.mjs b/module/applications/ui/itemBrowser.mjs index 67a16f6a..99b9a23d 100644 --- a/module/applications/ui/itemBrowser.mjs +++ b/module/applications/ui/itemBrowser.mjs @@ -277,7 +277,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { (await foundry.applications.ux.TextEditor.implementation.enrichHTML(item.description)); } - this.fieldFilter = this._createFieldFilter(); + this.fieldFilter = await this._createFieldFilter(); if (this.presets?.filter) { Object.entries(this.presets.filter).forEach(([k, v]) => { @@ -355,12 +355,12 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { ); } - _createFieldFilter() { + async _createFieldFilter() { const filters = ItemBrowser.getFolderConfig(this.selectedMenu.data, 'filters'); - filters.forEach(f => { + for (const f of filters) { if (typeof f.field === 'string') f.field = foundry.utils.getProperty(game, f.field); else if (typeof f.choices === 'function') { - f.choices = f.choices(this.items); + f.choices = await f.choices(this.items); } // Clear field label so template uses our custom label parameter @@ -370,7 +370,8 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { f.name ??= f.key; f.value = this.presets?.filter?.[f.name]?.value ?? null; - }); + } + return filters; } diff --git a/module/config/itemBrowserConfig.mjs b/module/config/itemBrowserConfig.mjs index 04b387cb..3e40c97b 100644 --- a/module/config/itemBrowserConfig.mjs +++ b/module/config/itemBrowserConfig.mjs @@ -383,7 +383,8 @@ export const typeConfig = { { key: 'system.linkedClass', label: 'TYPES.Item.class', - format: linkedClass => linkedClass?.name ?? 'DAGGERHEART.UI.ItemBrowser.missing' + format: linkedClass => + foundry.utils.fromUuidSync(linkedClass)?.name ?? 'DAGGERHEART.UI.ItemBrowser.missing' }, { key: 'system.spellcastingTrait', @@ -393,15 +394,18 @@ export const typeConfig = { ], filters: [ { - key: 'system.linkedClass.uuid', + key: 'system.linkedClass', label: 'TYPES.Item.class', - choices: items => { - const list = items - .filter(item => item.system.linkedClass) - .map(item => ({ - value: item.system.linkedClass.uuid, - label: item.system.linkedClass.name - })); + choices: async items => { + const list = []; + for (const item of items.filter(item => item.system.linkedClass)) { + const linkedClass = await foundry.utils.fromUuid(item.system.linkedClass); + list.push({ + value: linkedClass.uuid, + label: linkedClass.name + }); + } + return list.reduce((a, c) => { if (!a.find(i => i.value === c.value)) a.push(c); return a; diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index aebf33bf..ba114fda 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -200,7 +200,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { const features = []; for (let f of this.features) { const fBase = f.item ?? f; - const feature = fBase.system ? fBase : await foundry.utils.fromUuid(fBase.uuid); + const feature = fBase.pack ? await foundry.utils.fromUuid(fBase.uuid) : fBase; features.push( foundry.utils.mergeObject( feature.toObject(), diff --git a/module/data/item/class.mjs b/module/data/item/class.mjs index d3738318..7014e011 100644 --- a/module/data/item/class.mjs +++ b/module/data/item/class.mjs @@ -30,7 +30,6 @@ export default class DHClass extends BaseDataItem { }), evasion: new fields.NumberField({ initial: 0, integer: true, label: 'DAGGERHEART.GENERAL.evasion' }), features: new ItemLinkFields(), - subclasses: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }), inventory: new fields.SchemaField({ take: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }), choiceA: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }), @@ -70,6 +69,24 @@ export default class DHClass extends BaseDataItem { return this.features.filter(x => x.type === CONFIG.DH.ITEM.featureSubTypes.class).map(x => x.item); } + async fetchSubclasses() { + const uuids = [this.parent.uuid, this.parent._stats?.compendiumSource].filter(u => !!u); + const subclasses = game.items.filter(x => x.type === 'subclass' && uuids.includes(x.system.linkedClass)); + for (const pack of game.packs) { + const indexes = await pack.getIndex({ fields: ['system.linkedClass'] }); + for (const index of indexes) { + if (index.type !== 'subclass') continue; + if (!uuids.includes(index.system?.linkedClass)) continue; + if (subclasses.find(x => x.uuid === index.uuid)) continue; + + const subclass = await foundry.utils.fromUuid(index.uuid); + subclasses.push(subclass); + } + } + + return subclasses; + } + async _preCreate(data, options, user) { if (this.actor?.type === 'character') { const levelupAuto = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).levelupAuto; diff --git a/module/data/item/subclass.mjs b/module/data/item/subclass.mjs index d421cc6d..12d85c1e 100644 --- a/module/data/item/subclass.mjs +++ b/module/data/item/subclass.mjs @@ -28,7 +28,7 @@ export default class DHSubclass extends BaseDataItem { features: new ItemLinkFields(), featureState: new fields.NumberField({ required: true, initial: 1, min: 1 }), isMulticlass: new fields.BooleanField({ initial: false }), - linkedClass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true, initial: null }) + linkedClass: new fields.DocumentUUIDField({ type: 'Item', nullable: true, initial: null }) }; } @@ -83,7 +83,8 @@ export default class DHSubclass extends BaseDataItem { ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.missingClass')); return false; } - if (actorClass.system.subclasses.every(x => x.uuid !== dataUuid)) { + + if ((await actorClass.system.fetchSubclasses()).every(x => x.uuid !== dataUuid)) { ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.subclassNotInClass')); return false; } diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index cec493b4..90937db4 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -861,3 +861,11 @@ export function createShallowProxy(obj) { } }); } + +export function camelize(str) { + return str + .replace(/(?:^\w|[A-Z]|\b\w)/g, (part, index) => { + return index === 0 ? part.toLowerCase() : part.toUpperCase(); + }) + .replace(/\s+/g, ''); +} diff --git a/styles/less/global/feature-section.less b/styles/less/global/feature-section.less index 13feb92c..7d5099e1 100644 --- a/styles/less/global/feature-section.less +++ b/styles/less/global/feature-section.less @@ -19,28 +19,36 @@ &:last-child { margin-bottom: 0px; } - .feature-line { - display: grid; + } + .feature-line { + display: grid; + align-items: center; + grid-template-columns: 1fr 4fr 1fr; + h4 { + font-weight: lighter; + color: light-dark(@dark, @beige); + } + .image { + height: 40px; + width: 40px; + object-fit: cover; + border-radius: 6px; + border: none; + } + .image-icon { + font-size: 26px; + width: 40px; + height: 40px; + display: flex; + justify-content: center; align-items: center; - grid-template-columns: 1fr 4fr 1fr; - h4 { - font-weight: lighter; - color: light-dark(@dark, @beige); - } - .image { - height: 40px; - width: 40px; - object-fit: cover; - border-radius: 6px; - border: none; - } - .controls { - display: flex; - justify-content: center; - gap: 10px; - a { - text-shadow: none; - } + } + .controls { + display: flex; + justify-content: center; + gap: 10px; + a { + text-shadow: none; } } } diff --git a/styles/less/sheets/items/item-sheet-shared.less b/styles/less/sheets/items/item-sheet-shared.less index d0a8cc48..5155ad70 100644 --- a/styles/less/sheets/items/item-sheet-shared.less +++ b/styles/less/sheets/items/item-sheet-shared.less @@ -10,4 +10,8 @@ font-family: @font-body; color: light-dark(@chat-blue-bg, @beige-50); } + + button.plain.inline-control { + flex: 0 0 auto; + } } diff --git a/templates/sheets/items/class/features.hbs b/templates/sheets/items/class/features.hbs index 9d037b65..279ff52c 100644 --- a/templates/sheets/items/class/features.hbs +++ b/templates/sheets/items/class/features.hbs @@ -27,10 +27,7 @@
{{localize "TYPES.Item.subclass"}}
- {{#unless source.system.subclasses}} -
{{localize "DAGGERHEART.GENERAL.missingDragDropThing" thing=(localize "DAGGERHEART.GENERAL.subclasses")}}
- {{/unless}} - {{#each source.system.subclasses as |subclass index|}} + {{#each subclasses as |subclass index|}}
  • @@ -44,16 +41,7 @@ data-item-uuid={{subclass.uuid}} data-tooltip='{{localize "DAGGERHEART.UI.Tooltip.openItemWorld"}}' > - - - - +
  • diff --git a/templates/sheets/items/class/header.hbs b/templates/sheets/items/class/header.hbs index 6b60ada6..019825f8 100644 --- a/templates/sheets/items/class/header.hbs +++ b/templates/sheets/items/class/header.hbs @@ -3,7 +3,6 @@

    -

    {{localize 'TYPES.Item.class'}}

    {{localize "DAGGERHEART.GENERAL.Domain.plural"}} diff --git a/templates/sheets/items/subclass/description.hbs b/templates/sheets/items/subclass/description.hbs index 4591bd1a..0267eb9b 100644 --- a/templates/sheets/items/subclass/description.hbs +++ b/templates/sheets/items/subclass/description.hbs @@ -1,8 +1,10 @@
    -
    -

    {{localize "DAGGERHEART.ITEMS.Subclass.spellcastTrait"}}

    - {{spellcastTrait}} -
    + {{#if spellcastTrait}} +
    +

    {{localize "DAGGERHEART.ITEMS.Subclass.spellcastTrait"}}

    + {{spellcastTrait}} +
    + {{/if}}

    {{localize "DAGGERHEART.ITEMS.Subclass.foundationFeatures"}}

    {{#each foundationFeatures as | feature |}} diff --git a/templates/sheets/items/subclass/features.hbs b/templates/sheets/items/subclass/features.hbs index 1a75974e..c54e702e 100644 --- a/templates/sheets/items/subclass/features.hbs +++ b/templates/sheets/items/subclass/features.hbs @@ -3,6 +3,35 @@ data-tab='{{tabs.features.id}}' data-group='{{tabs.features.group}}' > +
    + {{localize "TYPES.Item.class"}} + {{#if class}} +
    +
  • + {{#if class.missing}} + + {{class.name}} + {{else}} + + {{class.name}} +
    + + + +
    + {{/if}} +
  • +
    + {{else}} +
    {{localize "DAGGERHEART.GENERAL.missingDragDropThing" thing=(localize "TYPES.Item.class")}}
    + {{/if}} +
    +
    {{localize "DAGGERHEART.GENERAL.Tabs.foundation"}} From 40804f333982529473aaf76c862de51ef5d4b22b Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Fri, 8 May 2026 08:58:14 -0400 Subject: [PATCH 09/14] Adjust spacing of character hope (#1864) --- styles/less/sheets/actors/character/header.less | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/styles/less/sheets/actors/character/header.less b/styles/less/sheets/actors/character/header.less index ffd01c83..ea397220 100644 --- a/styles/less/sheets/actors/character/header.less +++ b/styles/less/sheets/actors/character/header.less @@ -160,7 +160,7 @@ .domains-section { position: relative; display: flex; - gap: 10px; + gap: 4px; background-color: light-dark(transparent, @dark-blue); color: light-dark(@dark-blue, @golden); padding: 5px 10px; @@ -175,6 +175,7 @@ font-weight: bold; text-transform: uppercase; color: light-dark(@dark-blue, @golden); + margin-right: 4px; } .domain { From 9ef492969325a60defad971cd0a8def80d6b37cb Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Fri, 8 May 2026 09:08:25 -0400 Subject: [PATCH 10/14] Implement another traits redesign (#1871) * Implement another traits redesign * Adjust traits shape --- .../less/sheets/actors/character/header.less | 110 ++++++++++++------ styles/less/utils/colors.less | 2 + styles/less/utils/fonts.less | 2 + templates/sheets/actors/character/header.hbs | 40 ++++--- 4 files changed, 105 insertions(+), 49 deletions(-) diff --git a/styles/less/sheets/actors/character/header.less b/styles/less/sheets/actors/character/header.less index ea397220..f912a7f0 100644 --- a/styles/less/sheets/actors/character/header.less +++ b/styles/less/sheets/actors/character/header.less @@ -212,59 +212,101 @@ display: flex; padding: 0; margin-bottom: 15px; - gap: 8px; + justify-content: space-between; + max-width: 38.5rem; + gap: 0.5rem; + padding-left: 0.5rem; .trait { - height: 3.625rem; + --color-border: light-dark(@semi-transparent-dark-blue, @golden-60); cursor: pointer; - border: 1px solid light-dark(@dark-blue, @golden); - border-radius: 6px; - flex: 1; position: relative; - background-color: light-dark(transparent, @dark-blue); display: flex; + align-items: center; justify-content: center; flex-direction: column; + min-width: 4.375rem; .trait-name { - display: flex; - align-items: center; - padding-top: 5px; - color: light-dark(@dark-blue, @golden); + position: relative; + background-color: light-dark(@semi-transparent-dark-blue, @golden-40); + border: 1px solid var(--color-border); + border-radius: 3px; + color: light-dark(var(--color-light-1), @golden); font-size: var(--font-size-12); font-weight: 600; + height: 1rem; + line-height: 1rem; + white-space: nowrap; + width: 100%; + padding: 0 0.1876px 0 0.375rem; + margin-right: 0.125rem; /* makes it center SLIGHTLY */ + text-shadow: 1px 1px 2px @light-black; + + display: flex; align-items: center; justify-content: center; - } - .trait-value { - font-style: normal; - font-weight: 600; - font-size: var(--font-size-20); - text-align: center; - margin-bottom: 0.375rem; - } - - .tier-mark, - .spellcasting-mark { - position: absolute; - opacity: 0.9; - color: light-dark(@dark-blue, @golden); - i { - line-height: 17px; - font-size: var(--font-size-11); + .tier-mark { + position: absolute; + background-color: @dark-blue; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 50%; + width: 1rem; + height: 1rem; + right: calc(100% - 0.4375rem); + display: flex; + justify-content: center; + align-items: center; + &.marked::before { + content: ' '; + position: absolute; + width: 0.5rem; + height: 0.5rem; + border-radius: 50%; + background-color: @golden; + } } } - .tier-mark { - bottom: 1px; - left: 3px; - } + .trait-value-area { + --background: light-dark(#e8e6e3, @dark-blue); + display: flex; + position: relative; + .trait-value { + position: absolute; + inset: 0; + display: flex; + align-items: center; - .spellcasting-mark { - bottom: 1px; - right: 3px; + justify-content: center; + font-style: normal; + font-weight: 600; + font-size: var(--font-size-20); + text-align: center; + margin-bottom: 0.375rem; + } + + .spellcasting-mark { + position: absolute; + border: 1px solid light-dark(@dark-blue, @golden); + color: @golden; + left: 0; + right: 0; + bottom: -0.375rem; + margin-inline: auto; + border-radius: 50%; + width: 1.125rem; + height: 1.125rem; + background: radial-gradient(190.63% 190.63% at 50% -80.63%, #18152E 70%, #4D4494 80%, #A0837E 90%, var(--color-border) 100%); + font-size: var(--font-size-9); + text-shadow: 0 0 2px @light-black; + + display: flex; + align-items: center; + justify-content: center; + } } &:hover { diff --git a/styles/less/utils/colors.less b/styles/less/utils/colors.less index 8f6418bf..18b981ad 100755 --- a/styles/less/utils/colors.less +++ b/styles/less/utils/colors.less @@ -5,6 +5,7 @@ --golden: #f3c267; --golden-10: #f3c26710; --golden-40: #f3c26740; + --golden-60: #f3c26760; --golden-90: #f3c26790; --golden-bg: #f3c2671a; --golden-secondary: #eaaf42; @@ -89,6 +90,7 @@ @golden: var(--golden, #f3c267); @golden-10: var(--golden-10, #f3c26710); @golden-40: var(--golden-40, #f3c26740); +@golden-60: var(--golden-60, #f3c26760); @golden-90: var(--golden-90, #f3c26790); @golden-bg: var(--golden-bg, #f3c2671a); @golden-secondary: var(--golden-secondary, #eaaf42); diff --git a/styles/less/utils/fonts.less b/styles/less/utils/fonts.less index d4902508..201ea356 100755 --- a/styles/less/utils/fonts.less +++ b/styles/less/utils/fonts.less @@ -7,6 +7,8 @@ --dh-font-body: 'Montserrat'; /* Include missing font sizes */ + --font-size-8: 0.5rem; + --font-size-9: 0.5625rem; --font-size-22: 1.375rem; } diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index 8010dfa5..2a19e2c3 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -132,27 +132,37 @@
    {{#each this.attributes as |attribute key|}} -
    -
    - {{attribute.label}} -
    -
    - {{#if (gt attribute.value 0)}} - +{{attribute.value}} - {{else}} - {{attribute.value}} - {{/if}} -
    + {{!--
    + {{#if attribute.tierMarked}}
    {{/if}} - {{#if isSpellcasting}} -
    - +
    --}} +
    +
    +
    + {{attribute.label}} +
    +
    + + + + +
    + {{#if (gt attribute.value 0)}} + +{{attribute.value}} + {{else}} + {{attribute.value}} + {{/if}}
    - {{/if}} + {{#if isSpellcasting}} +
    + +
    + {{/if}} +
    {{/each}}
    From e8828b70dbaa458438e2748a92a261a2005302f8 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Sat, 9 May 2026 05:15:35 -0400 Subject: [PATCH 11/14] Remove white border around level input in character sheet (#1870) --- styles/less/sheets/actors/character/header.less | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/styles/less/sheets/actors/character/header.less b/styles/less/sheets/actors/character/header.less index f912a7f0..47297b59 100644 --- a/styles/less/sheets/actors/character/header.less +++ b/styles/less/sheets/actors/character/header.less @@ -25,13 +25,19 @@ .name-row { display: flex; - gap: 5px; + gap: 6px; align-items: start; justify-content: space-between; padding: 0; padding-top: 5px; flex: 1; + [contenteditable], + input { + border: 1px solid @soft-shadow; + background-color: light-dark(@dark-15, @dark-blue); + } + h1 { display: flex; flex: 1; @@ -57,14 +63,16 @@ .label { display: flex; - align-items: center; + align-items: baseline; gap: 4px; } input { + border: none; width: 40px; padding: 0; text-align: center; + font-weight: 600; } .level-button { From 4064701c164ad8b3470597cd258fb25487d37523 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sat, 9 May 2026 16:25:43 +0200 Subject: [PATCH 12/14] Fixed so that the overridden ?? label is actually applied for private messages (#1872) --- module/dice/dhRoll.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index 83dbbaf2..d6975f71 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -141,8 +141,8 @@ export default class DHRoll extends Roll { const metagamingSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming); const chatData = await this._prepareChatRenderContext({ flavor, isPrivate, ...options }); return foundry.applications.handlebars.renderTemplate(template, { - ...chatData, roll: this, + ...chatData, parent: chatData.parent, targetMode: chatData.targetMode, areas: chatData.action?.areas, From 80e314ca84809cd9a6730e933da319dc0d212a94 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 9 May 2026 16:28:43 +0200 Subject: [PATCH 13/14] Minor style changes for character/header.hbs --- styles/less/sheets/actors/character/header.less | 2 +- templates/sheets/actors/character/header.hbs | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/styles/less/sheets/actors/character/header.less b/styles/less/sheets/actors/character/header.less index 47297b59..21ea4846 100644 --- a/styles/less/sheets/actors/character/header.less +++ b/styles/less/sheets/actors/character/header.less @@ -35,7 +35,7 @@ [contenteditable], input { border: 1px solid @soft-shadow; - background-color: light-dark(@dark-15, @dark-blue); + background-color: light-dark(@dark-15, @soft-white-shadow); } h1 { diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index 2a19e2c3..dfc9af16 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -132,14 +132,6 @@
    {{#each this.attributes as |attribute key|}} - {{!--
    - - {{#if attribute.tierMarked}} -
    - -
    - {{/if}} -
    --}}
    From abd7824c967681f6752010c3c81f356cb7857a70 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sat, 9 May 2026 16:33:45 +0200 Subject: [PATCH 14/14] Raised version --- system.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system.json b/system.json index f7614790..16a1f74b 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.2.3", + "version": "2.2.4", "compatibility": { "minimum": "14.359", "verified": "14.361", @@ -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.2.3/system.zip", + "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.2.4/system.zip", "authors": [ { "name": "WBHarry"