From 3ec013ff5072700572b1cd32a87ec39f0c4528da Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 12 Apr 2026 00:25:43 +0200 Subject: [PATCH 01/11] Reworked summon action and clowncar functionality to work with levels (#1791) --- module/applications/hud/tokenHUD.mjs | 16 ++- module/canvas/tokens.mjs | 17 +-- module/data/fields/action/summonField.mjs | 38 +++--- module/documents/tokenManager.mjs | 138 ++++++++-------------- 4 files changed, 79 insertions(+), 130 deletions(-) diff --git a/module/applications/hud/tokenHUD.mjs b/module/applications/hud/tokenHUD.mjs index 77caaaff..943f3506 100644 --- a/module/applications/hud/tokenHUD.mjs +++ b/module/applications/hud/tokenHUD.mjs @@ -122,15 +122,14 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { async toggleClowncar(actors) { const animationDuration = 500; - const activeTokens = actors.flatMap(member => member.getActiveTokens()); + const scene = game.scenes.get(game.user.viewedScene); + /* getDependentTokens returns already removed tokens with id = null. Need to filter that until it's potentially fixed from Foundry */ + const activeTokens = actors.flatMap(member => member.getDependentTokens({ scenes: scene }).filter(x => x._id)); const { x: actorX, y: actorY } = this.document; if (activeTokens.length > 0) { for (let token of activeTokens) { - await token.document.update( - { x: actorX, y: actorY, alpha: 0 }, - { animation: { duration: animationDuration } } - ); - setTimeout(() => token.document.delete(), animationDuration); + await token.update({ x: actorX, y: actorY, alpha: 0 }, { animation: { duration: animationDuration } }); + setTimeout(() => token.delete(), animationDuration); } } else { const activeScene = game.scenes.find(x => x.id === game.user.viewedScene); @@ -140,11 +139,16 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { tokenData.push(data.toObject()); } + const viewedLevel = game.scenes.get(game.user.viewedScene).levels.get(game.user.viewedLevel); + const elevation = this.actor.token?.elevation ?? viewedLevel.elevation.bottom; + const newTokens = await activeScene.createEmbeddedDocuments( 'Token', tokenData.map(tokenData => ({ ...tokenData, alpha: 0, + level: viewedLevel, + elevation: elevation, x: actorX, y: actorY })) diff --git a/module/canvas/tokens.mjs b/module/canvas/tokens.mjs index 9813cd48..9ca140e0 100644 --- a/module/canvas/tokens.mjs +++ b/module/canvas/tokens.mjs @@ -1,16 +1 @@ -export default class DhTokenLayer extends foundry.canvas.layers.TokenLayer { - async _createPreview(createData, options) { - if (options.actor) { - const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes; - if (options.actor?.system.metadata.usesSize) { - const tokenSize = tokenSizes[options.actor.system.size]; - if (tokenSize && options.actor.system.size !== CONFIG.DH.ACTOR.tokenSize.custom.id) { - createData.width = tokenSize; - createData.height = tokenSize; - } - } - } - - return super._createPreview(createData, options); - } -} +export default class DhTokenLayer extends foundry.canvas.layers.TokenLayer {} diff --git a/module/data/fields/action/summonField.mjs b/module/data/fields/action/summonField.mjs index dce6414c..36ea1010 100644 --- a/module/data/fields/action/summonField.mjs +++ b/module/data/fields/action/summonField.mjs @@ -44,12 +44,18 @@ export default class DHSummonField extends fields.ArrayField { count = roll.total; } - const actor = DHSummonField.getWorldActor(await foundry.utils.fromUuid(summon.actorUUID)); + const actor = await DHSummonField.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.rolledCount = count; summon.actor = actor.toObject(); - summonData.push({ actor, count: count }); + const countNumber = Number.parseInt(count); + for (let i = 0; i < countNumber; i++) { + const remaining = countNumber - i; + summonData.push({ + actor, + tokenPreviewName: `${actor.prototypeToken.name}${remaining > 1 ? ` (${remaining}x)` : ''}` + }); + } } if (rolls.length) await Promise.all(rolls.map(roll => game.dice3d.showForRoll(roll, game.user, true))); @@ -58,32 +64,22 @@ 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 */ - static getWorldActor(baseActor) { + /* 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); - return worldActorCopy ?? baseActor; + if (worldActorCopy) return worldActorCopy; + + return await game.system.api.documents.DhpActor.create(baseActor.toObject()); } return baseActor; } - static async handleSummon(summonData, actionActor, summonIndex = 0) { - const summon = summonData[summonIndex]; - const result = await CONFIG.ux.TokenManager.createPreviewAsync(summon.actor, { - name: `${summon.actor.prototypeToken.name}${summon.count > 1 ? ` (${summon.count}x)` : ''}` - }); + static async handleSummon(summonData, actionActor) { + await CONFIG.ux.TokenManager.createTokensWithPreview(summonData, { elevation: actionActor.token?.elevation }); - if (!result) return actionActor.sheet?.maximize(); - summon.actor = result.actor; - - summon.count--; - if (summon.count <= 0) { - summonIndex++; - if (summonIndex === summonData.length) return actionActor.sheet?.maximize(); - } - - DHSummonField.handleSummon(summonData, actionActor, summonIndex); + return actionActor.sheet?.maximize(); } } diff --git a/module/documents/tokenManager.mjs b/module/documents/tokenManager.mjs index f766a677..3ccff4e2 100644 --- a/module/documents/tokenManager.mjs +++ b/module/documents/tokenManager.mjs @@ -1,104 +1,68 @@ /** - * A singleton class that handles preview tokens. + * A singleton class that handles creating tokens. */ export default class DhTokenManager { - #activePreview; - #actor; - #resolve; - /** - * Create a template preview, deactivating any existing ones. - * @param {object} data + * Create a token previer + * @param {Actor} actor + * @param {object} tokenData */ async createPreview(actor, tokenData) { - this.#actor = actor; - const token = await canvas.tokens._createPreview( - { - ...actor.prototypeToken, - displayName: 50, - ...tokenData - }, - { renderSheet: false, actor } + const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes; + if (actor?.system.metadata.usesSize) { + const tokenSize = tokenSizes[actor.system.size]; + if (tokenSize && actor.system.size !== CONFIG.DH.ACTOR.tokenSize.custom.id) { + tokenData.width = tokenSize; + tokenData.height = tokenSize; + } + } + + return await canvas.tokens.placeTokens( + [ + { + ...actor.prototypeToken.toObject(), + actorId: actor.id, + displayName: 50, + ...tokenData + } + ], + { create: false } ); - - this.#activePreview = { - document: token.document, - object: token, - origin: { x: token.document.x, y: token.document.y } - }; - - this.#activePreview.events = { - contextmenu: this.#cancelTemplate.bind(this), - mousedown: this.#confirmTemplate.bind(this), - mousemove: this.#onDragMouseMove.bind(this) - }; - - canvas.stage.on('mousemove', this.#activePreview.events.mousemove); - canvas.stage.on('mousedown', this.#activePreview.events.mousedown); - canvas.app.view.addEventListener('contextmenu', this.#activePreview.events.contextmenu); - } - - /* Currently intended for using as a preview of where to create a token. (note the flag) */ - async createPreviewAsync(actor, tokenData = {}) { - return new Promise(resolve => { - this.#resolve = resolve; - this.createPreview(actor, { ...tokenData, flags: { daggerheart: { createPlacement: true } } }); - }); } /** - * Handles the movement of the token preview on mousedrag. - * @param {mousemove Event} event + * Creates new tokens on the canvas by placing previews. + * @param {object} tokenData + * @param {object} options */ - #onDragMouseMove(event) { - event.stopPropagation(); - const { moveTime, object } = this.#activePreview; - const update = {}; + async createTokensWithPreview(tokensData, { elevation } = {}) { + const scene = game.scenes.get(game.user.viewedScene); + if (!scene) return; - const now = Date.now(); - if (now - (moveTime || 0) <= 16) return; - this.#activePreview.moveTime = now; + const level = scene.levels.get(game.user.viewedLevel); + if (!level) return; - let cursor = event.getLocalPosition(canvas.templates); + const createElevation = elevation ?? level.elevation.bottom; + for (const tokenData of tokensData) { + const previewTokens = await this.createPreview(tokenData.actor, { + name: tokenData.tokenPreviewName, + level: game.user.viewedLevel, + elevation: createElevation, + flags: { daggerheart: { createPlacement: true } } + }); + if (!previewTokens?.length) return null; - Object.assign(update, canvas.grid.getTopLeftPoint(cursor)); - - object.document.updateSource(update); - object.renderFlags.set({ refresh: true }); - } - - /** - * Cancels the preview token on right-click. - * @param {contextmenu Event} event - */ - #cancelTemplate(_event, resolved) { - const { mousemove, mousedown, contextmenu } = this.#activePreview.events; - this.#activePreview.object.destroy(); - - canvas.stage.off('mousemove', mousemove); - canvas.stage.off('mousedown', mousedown); - canvas.app.view.removeEventListener('contextmenu', contextmenu); - if (this.#resolve && !resolved) this.#resolve(false); - } - - /** - * Creates a real Actor and token at the preview location and cancels the preview. - * @param {click Event} event - */ - async #confirmTemplate(event) { - event.stopPropagation(); - this.#cancelTemplate(event, true); - - const actor = this.#actor.inCompendium - ? await game.system.api.documents.DhpActor.create(this.#actor.toObject()) - : this.#actor; - const tokenData = await actor.getTokenDocument(); - const result = await canvas.scene.createEmbeddedDocuments('Token', [ - { ...tokenData.toObject(), x: this.#activePreview.document.x, y: this.#activePreview.document.y } - ]); - - this.#activePreview = undefined; - if (this.#resolve && result.length) this.#resolve(result[0]); + await canvas.scene.createEmbeddedDocuments( + 'Token', + previewTokens.map(x => ({ + ...x.toObject(), + name: tokenData.actor.prototypeToken.name, + displayName: tokenData.actor.prototypeToken.displayName, + flags: tokenData.actor.prototypeToken.flags + })), + { controlObject: true, parent: canvas.scene } + ); + } } } From e2c97a7b61023d1a192db91b6ac4c756bb5e12fe Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 12 Apr 2026 00:32:59 +0200 Subject: [PATCH 02/11] Raised version --- system.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system.json b/system.json index 614b9cc0..babdde26 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.1.1", + "version": "2.1.2", "compatibility": { "minimum": "14.359", "verified": "14.360", @@ -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.1.1/system.zip", + "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.1.2/system.zip", "authors": [ { "name": "WBHarry" From a839ca006667de2db5a769ec511305886e935f4a Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 12 Apr 2026 00:36:24 +0200 Subject: [PATCH 03/11] Corrected deploy.yml for new branch --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e245c7fa..553a1a17 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -35,7 +35,7 @@ jobs: env: version: ${{steps.get_version.outputs.version-without-v}} url: https://github.com/${{github.repository}} - manifest: https://raw.githubusercontent.com/${{github.repository}}/main/system.json + manifest: https://raw.githubusercontent.com/${{github.repository}}/v14/system.json download: https://github.com/${{github.repository}}/releases/download/${{github.event.release.tag_name}}/system.zip # Create a zip file with all files required by the module to add to the release From 66c90d69e3aad53a2bb960780261e02ef43723d8 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 12 Apr 2026 11:10:02 +0200 Subject: [PATCH 04/11] Added saefety to updateActorsRangeDepenedentEffects --- daggerheart.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daggerheart.mjs b/daggerheart.mjs index 43aafce4..598c1a78 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -360,7 +360,7 @@ const updateActorsRangeDependentEffects = async token => { CONFIG.DH.SETTINGS.gameSettings.variantRules ).rangeMeasurement; - for (let effect of token.actor?.allApplicableEffects() ?? []) { + for (let effect of token.actor?.allApplicableEffects?.() ?? []) { if (!effect.system.rangeDependence?.enabled) continue; const { target, range, type } = effect.system.rangeDependence; From e003db3ec137fcc08f7e6ac4cfa0e87e2e183676 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 12 Apr 2026 11:22:00 +0200 Subject: [PATCH 05/11] Corrected updateActorsRangeDependentEffects when token is null --- daggerheart.mjs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/daggerheart.mjs b/daggerheart.mjs index 598c1a78..064b1670 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -355,12 +355,14 @@ Hooks.on(CONFIG.DH.HOOKS.hooksConfig.groupRollStart, async data => { }); const updateActorsRangeDependentEffects = async token => { + if (!token) return; + const rangeMeasurement = game.settings.get( CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules ).rangeMeasurement; - for (let effect of token.actor?.allApplicableEffects?.() ?? []) { + for (let effect of token.actor?.allApplicableEffects() ?? []) { if (!effect.system.rangeDependence?.enabled) continue; const { target, range, type } = effect.system.rangeDependence; From 56a6613a73d3edce24a8a52ed217be0788cc3606 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 12 Apr 2026 11:38:15 +0200 Subject: [PATCH 06/11] Fixed more missing translations (#1792) --- lang/en.json | 20 ++++++++++++----- module/applications/ui/itemBrowser.mjs | 2 +- module/config/itemBrowserConfig.mjs | 22 +++++++++++-------- .../footer.hbs | 2 +- templates/sheets/items/weapon/header.hbs | 4 ++-- templates/ui/tooltip/battlepoints.hbs | 6 ++--- templates/ui/tooltip/weapon.hbs | 2 +- 7 files changed, 36 insertions(+), 22 deletions(-) diff --git a/lang/en.json b/lang/en.json index a2c3dc79..45a3b414 100755 --- a/lang/en.json +++ b/lang/en.json @@ -74,9 +74,7 @@ "name": "Summon", "tooltip": "Create tokens in the scene.", "error": "You do not have permission to summon tokens or there is no active scene.", - "invalidDrop": "You can only drop Actor entities to summon.", - "chatMessageTitle": "Test2", - "chatMessageHeaderTitle": "Summoning" + "invalidDrop": "You can only drop Actor entities to summon." }, "transform": { "name": "Transform", @@ -2016,6 +2014,10 @@ "hint": "Multiply any damage dealt to you by this number" } }, + "Battlepoints": { + "full": "Battlepoints", + "short": "BP" + }, "Bonuses": { "rest": { "downtimeAction": "Downtime Action", @@ -2457,6 +2459,7 @@ "rollDamage": "Roll Damage", "rollWith": "{roll} Roll", "save": "Save", + "saveSettings": "Save Settings", "scalable": "Scalable", "scars": "Scars", "situationalBonus": "Situational Bonus", @@ -2611,8 +2614,14 @@ }, "Weapon": { "weaponType": "Weapon Type", - "primaryWeapon": "Primary Weapon", - "secondaryWeapon": "Secondary Weapon" + "primaryWeapon": { + "full": "Primary Weapon", + "short": "Primary" + }, + "secondaryWeapon": { + "full": "Secondary Weapon", + "short": "Secondary" + } } }, "MACROS": { @@ -3067,6 +3076,7 @@ }, "ItemBrowser": { "title": "Daggerheart Compendium Browser", + "windowTitle": "Compendium Browser", "hint": "Select a Folder in sidebar to start browsing through the compendium", "browserSettings": "Browser Settings", "columnName": "Name", diff --git a/module/applications/ui/itemBrowser.mjs b/module/applications/ui/itemBrowser.mjs index 9ca328a0..22de38ab 100644 --- a/module/applications/ui/itemBrowser.mjs +++ b/module/applications/ui/itemBrowser.mjs @@ -37,7 +37,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { tag: 'div', window: { frame: true, - title: 'Compendium Browser', + title: 'DAGGERHEART.UI.ItemBrowser.windowTitle', icon: 'fa-solid fa-book-atlas', positioned: true, resizable: true diff --git a/module/config/itemBrowserConfig.mjs b/module/config/itemBrowserConfig.mjs index 0a4154a8..f20e56d0 100644 --- a/module/config/itemBrowserConfig.mjs +++ b/module/config/itemBrowserConfig.mjs @@ -74,12 +74,13 @@ export const typeConfig = { columns: [ { key: 'type', - label: 'DAGGERHEART.GENERAL.type' + label: 'DAGGERHEART.GENERAL.type', + format: type => type ? `TYPES.Item.${type}` : '-' }, { key: 'system.secondary', label: 'DAGGERHEART.UI.ItemBrowser.subtype', - format: isSecondary => (isSecondary ? 'secondary' : isSecondary === false ? 'primary' : '-') + format: isSecondary => (isSecondary ? 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon.short' : isSecondary === false ? 'DAGGERHEART.ITEMS.Weapon.primaryWeapon.short' : '-') }, { key: 'system.tier', @@ -99,8 +100,8 @@ export const typeConfig = { key: 'system.secondary', label: 'DAGGERHEART.UI.ItemBrowser.subtype', choices: [ - { value: false, label: 'DAGGERHEART.ITEMS.Weapon.primaryWeapon' }, - { value: true, label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon' } + { value: false, label: 'DAGGERHEART.ITEMS.Weapon.primaryWeapon.full' }, + { value: true, label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon.full' } ] }, { @@ -258,11 +259,13 @@ export const typeConfig = { columns: [ { key: 'system.type', - label: 'DAGGERHEART.GENERAL.type' + label: 'DAGGERHEART.GENERAL.type', + format: type => type ? `DAGGERHEART.CONFIG.DomainCardTypes.${type}` : '-' }, { key: 'system.domain', - label: 'DAGGERHEART.GENERAL.Domain.single' + label: 'DAGGERHEART.GENERAL.Domain.single', + format: domain => domain ? CONFIG.DH.DOMAIN.allDomains()[domain].label : '-' }, { key: 'system.level', @@ -374,7 +377,7 @@ export const typeConfig = { columns: [ { key: 'system.linkedClass', - label: 'Class', + label: 'TYPES.Item.class', format: linkedClass => linkedClass?.name ?? 'DAGGERHEART.UI.ItemBrowser.missing' }, { @@ -386,7 +389,7 @@ export const typeConfig = { filters: [ { key: 'system.linkedClass.uuid', - label: 'Class', + label: 'TYPES.Item.class', choices: items => { const list = items .filter(item => item.system.linkedClass) @@ -410,7 +413,8 @@ export const typeConfig = { }, { key: 'system.mainTrait', - label: 'DAGGERHEART.GENERAL.Trait.single' + label: 'DAGGERHEART.GENERAL.Trait.single', + format: trait => (trait ? `DAGGERHEART.CONFIG.Traits.${trait}.name` : '-') } ], filters: [ diff --git a/templates/dialogs/compendiumBrowserSettingsDialog/footer.hbs b/templates/dialogs/compendiumBrowserSettingsDialog/footer.hbs index 9dc61cbe..d9bb378e 100644 --- a/templates/dialogs/compendiumBrowserSettingsDialog/footer.hbs +++ b/templates/dialogs/compendiumBrowserSettingsDialog/footer.hbs @@ -1,3 +1,3 @@
- +
\ No newline at end of file diff --git a/templates/sheets/items/weapon/header.hbs b/templates/sheets/items/weapon/header.hbs index 9bbd9511..fabbb07f 100644 --- a/templates/sheets/items/weapon/header.hbs +++ b/templates/sheets/items/weapon/header.hbs @@ -5,9 +5,9 @@

{{#if source.system.secondary}} -

{{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon"}}

+

{{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon.full"}}

{{else}} -

{{localize "DAGGERHEART.ITEMS.Weapon.primaryWeapon"}}

+

{{localize "DAGGERHEART.ITEMS.Weapon.primaryWeapon.full"}}

{{/if}}

{{localize (concat 'DAGGERHEART.CONFIG.Traits.' source.system.attack.roll.trait '.short')}} diff --git a/templates/ui/tooltip/battlepoints.hbs b/templates/ui/tooltip/battlepoints.hbs index 9672698a..f2f42f53 100644 --- a/templates/ui/tooltip/battlepoints.hbs +++ b/templates/ui/tooltip/battlepoints.hbs @@ -7,9 +7,9 @@ {{#each category as |grouping index|}}
{{#if grouping.nr}} - + {{else}} - + {{/if}}
{{/each}} @@ -26,7 +26,7 @@ {{else}} {{/if}} - +

{{/each}} diff --git a/templates/ui/tooltip/weapon.hbs b/templates/ui/tooltip/weapon.hbs index a672c883..4adb9c46 100644 --- a/templates/ui/tooltip/weapon.hbs +++ b/templates/ui/tooltip/weapon.hbs @@ -3,7 +3,7 @@

{{item.name}}

- {{#if item.system.secondary}}{{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon"}}{{else}}{{localize "DAGGERHEART.ITEMS.Weapon.primaryWeapon"}}{{/if}} + {{#if item.system.secondary}}{{localize "DAGGERHEART.ITEMS.Weapon.secondaryWeapon.full"}}{{else}}{{localize "DAGGERHEART.ITEMS.Weapon.primaryWeapon.full"}}{{/if}}
{{#with (lookup config.GENERAL.burden item.system.burden) as | burden |}} From f9000115102284082263a3a49573bfc7e309d4a9 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 12 Apr 2026 13:58:43 +0200 Subject: [PATCH 07/11] Fixed trait counting in CharacterCreation. Fixed description layout and labels in CharacterCreation. Fixed drag/drop images from Compendium Browser --- lang/en.json | 1 + .../characterCreation/characterCreation.mjs | 9 +++++++-- module/applications/ui/itemBrowser.mjs | 2 ++ .../selections-container.less | 14 +++++++++++++ .../characterCreation/tabs/experience.hbs | 20 +++++++++++++------ 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/lang/en.json b/lang/en.json index a2c3dc79..66df26b3 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2431,6 +2431,7 @@ "next": "Next", "none": "None", "noTarget": "No current target", + "optionalThing": "Optional {thing}", "partner": "Partner", "player": { "single": "Player", diff --git a/module/applications/characterCreation/characterCreation.mjs b/module/applications/characterCreation/characterCreation.mjs index e6c0f299..936bb79d 100644 --- a/module/applications/characterCreation/characterCreation.mjs +++ b/module/applications/characterCreation/characterCreation.mjs @@ -11,7 +11,10 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl this.character = character; this.setup = { - traits: this.character.system.traits, + traits: Object.keys(this.character.system.traits).reduce((acc, key) => { + acc[key] = { value: null }; + return acc; + }, {}), ancestryName: { primary: '', secondary: '' @@ -377,8 +380,10 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl ]; return Object.values(this.setup.traits).reduce((acc, x) => { const index = traitCompareArray.indexOf(x.value); + if (index === -1) return acc; + traitCompareArray.splice(index, 1); - acc += index !== -1; + acc += 1; return acc; }, 0); } diff --git a/module/applications/ui/itemBrowser.mjs b/module/applications/ui/itemBrowser.mjs index 9ca328a0..4c64c39e 100644 --- a/module/applications/ui/itemBrowser.mjs +++ b/module/applications/ui/itemBrowser.mjs @@ -583,7 +583,9 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { const { itemUuid } = event.target.closest('[data-item-uuid]').dataset, item = await foundry.utils.fromUuid(itemUuid), dragData = item.toDragData(); + event.dataTransfer.setData('text/plain', JSON.stringify(dragData)); + event.dataTransfer.setDragImage(event.target.querySelector('img'), 0, 0); } _canDragStart() { diff --git a/styles/less/dialog/character-creation/selections-container.less b/styles/less/dialog/character-creation/selections-container.less index f9569fca..a68cd36c 100644 --- a/styles/less/dialog/character-creation/selections-container.less +++ b/styles/less/dialog/character-creation/selections-container.less @@ -175,6 +175,11 @@ opacity: 0.2; } + &.no-horizontal-padding { + padding-left: 0; + padding-right: 0; + } + legend { margin-left: auto; margin-right: auto; @@ -278,6 +283,15 @@ flex-direction: column; gap: 5px; + &.separated { + border-bottom: 2px solid; + padding-bottom: 8px; + } + + .form-group { + padding: 0 0.75rem; + } + .experience-inner-container { position: relative; display: flex; diff --git a/templates/characterCreation/tabs/experience.hbs b/templates/characterCreation/tabs/experience.hbs index 3eb92834..66363084 100644 --- a/templates/characterCreation/tabs/experience.hbs +++ b/templates/characterCreation/tabs/experience.hbs @@ -4,17 +4,25 @@ data-group='{{tabs.experience.group}}' >
-
+
{{localize "DAGGERHEART.APPLICATIONS.CharacterCreation.initialExperiences"}} {{experience.nrSelected}}/{{experience.nrTotal}}
{{#each experience.values as |experience id|}} -
-
- - {{numberFormat this.value sign=true}} +
+
+ +
+ + {{numberFormat this.value sign=true}} +
- +
+ +
+ +
+
{{/each}}
From fb07938e54aefaa9528e7d5d4b27e5e955e4f90e Mon Sep 17 00:00:00 2001 From: Nikhil Nagarajan Date: Sun, 12 Apr 2026 13:10:51 -0400 Subject: [PATCH 08/11] container fix (#1795) --- .../less/dialog/character-creation/selections-container.less | 5 +++++ styles/less/sheets-settings/character-settings/sheet.less | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/styles/less/dialog/character-creation/selections-container.less b/styles/less/dialog/character-creation/selections-container.less index a68cd36c..2bbac484 100644 --- a/styles/less/dialog/character-creation/selections-container.less +++ b/styles/less/dialog/character-creation/selections-container.less @@ -255,6 +255,11 @@ height: 65px; background: url(../assets/svg/trait-shield.svg) no-repeat; background-size: 100%; + padding-top: 3px; + display: flex; + flex-direction: column; + align-items: center; + row-gap: 3px; div { filter: drop-shadow(0 0 3px black); diff --git a/styles/less/sheets-settings/character-settings/sheet.less b/styles/less/sheets-settings/character-settings/sheet.less index f7f16df4..eab29436 100644 --- a/styles/less/sheets-settings/character-settings/sheet.less +++ b/styles/less/sheets-settings/character-settings/sheet.less @@ -27,10 +27,11 @@ height: 65px; background: url(../assets/svg/trait-shield.svg) no-repeat; background-size: 100%; - padding-top: 4px; + padding-top: 3px; display: flex; flex-direction: column; align-items: center; + row-gap: 3px; span { font-size: var(--font-size-10); From 8d8dea81fe100220433252928254250673a09cf8 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Mon, 13 Apr 2026 20:41:28 +0200 Subject: [PATCH 09/11] [Fix] Compendium Advantage Sources (#1796) * Duration Description field didn't show initially for type temporary * Corrected SRD --- .../feature_Retract_UFR67BUOhNGLFyg9.json | 54 +++++++++++------- ...ure_Low_Light_Living_aMla3xQuCHEwORGD.json | 55 +++++++++---------- ...omainCard_Battle_Cry_Ef1JsUG50LIoKx2F.json | 44 +++++++++------ templates/sheets/activeEffect/settings.hbs | 8 +-- 4 files changed, 92 insertions(+), 69 deletions(-) diff --git a/src/packs/ancestries/feature_Retract_UFR67BUOhNGLFyg9.json b/src/packs/ancestries/feature_Retract_UFR67BUOhNGLFyg9.json index b17cd7da..eb9696b2 100644 --- a/src/packs/ancestries/feature_Retract_UFR67BUOhNGLFyg9.json +++ b/src/packs/ancestries/feature_Retract_UFR67BUOhNGLFyg9.json @@ -68,31 +68,33 @@ "type": "withinRange", "target": "hostile", "range": "melee" + }, + "changes": [ + { + "key": "system.resistance.physical.resistance", + "type": "override", + "value": 1, + "priority": null, + "phase": "initial" + }, + { + "key": "system.disadvantageSources", + "type": "add", + "value": "Action rolls", + "priority": null, + "phase": "initial" + } + ], + "duration": { + "type": "" } }, - "changes": [ - { - "key": "system.resistance.physical.resistance", - "mode": 5, - "value": "1", - "priority": null - }, - { - "key": "system.disadvantageSources", - "mode": 2, - "value": "Retract", - "priority": null - } - ], "disabled": true, "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, "description": "

While in your shell, you have resistance to physical damage, you have disadvantage on action rolls, and you canโ€™t move.

", "tint": "#ffffff", @@ -102,6 +104,16 @@ "_stats": { "compendiumSource": null }, + "start": { + "time": 0, + "combat": null, + "combatant": null, + "initiative": null, + "round": null, + "turn": null + }, + "showIcon": 1, + "folder": null, "_key": "!items.effects!UFR67BUOhNGLFyg9.3V4FPoyjJUnFP9WS" } ], diff --git a/src/packs/communities/feature_Low_Light_Living_aMla3xQuCHEwORGD.json b/src/packs/communities/feature_Low_Light_Living_aMla3xQuCHEwORGD.json index f1ed3ace..c95d9132 100644 --- a/src/packs/communities/feature_Low_Light_Living_aMla3xQuCHEwORGD.json +++ b/src/packs/communities/feature_Low_Light_Living_aMla3xQuCHEwORGD.json @@ -29,39 +29,28 @@ "type": "withinRange", "target": "hostile", "range": "melee" + }, + "changes": [ + { + "key": "system.advantageSources", + "type": "add", + "value": "Rolls to hide, investigate, or perceive details in low light", + "priority": null, + "phase": "initial" + } + ], + "duration": { + "type": "" } }, - "changes": [ - { - "key": "system.advantageSources", - "mode": 2, - "value": "In an area with low light or heavy shadow: hide, investigate, or perceive", - "priority": null - }, - { - "key": "system.advantageSources", - "mode": 2, - "value": "", - "priority": null - }, - { - "key": "", - "mode": 2, - "value": "", - "priority": null - } - ], "disabled": false, "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, - "description": "", + "description": "

When youโ€™re in an area with low light or heavy shadow, you have advantage on rolls to hide, investigate, or perceive details within that area.

", "origin": null, "tint": "#ffffff", "transfer": true, @@ -71,6 +60,16 @@ "_stats": { "compendiumSource": null }, + "start": { + "time": 0, + "combat": null, + "combatant": null, + "initiative": null, + "round": null, + "turn": null + }, + "showIcon": 1, + "folder": null, "_key": "!items.effects!aMla3xQuCHEwORGD.pCp32u7UwqxCI4WW" } ], diff --git a/src/packs/domains/domainCard_Battle_Cry_Ef1JsUG50LIoKx2F.json b/src/packs/domains/domainCard_Battle_Cry_Ef1JsUG50LIoKx2F.json index c9ae6071..1fde286d 100644 --- a/src/packs/domains/domainCard_Battle_Cry_Ef1JsUG50LIoKx2F.json +++ b/src/packs/domains/domainCard_Battle_Cry_Ef1JsUG50LIoKx2F.json @@ -132,27 +132,29 @@ "type": "withinRange", "target": "hostile", "range": "melee" + }, + "changes": [ + { + "key": "system.advantageSources", + "type": "add", + "value": "On Attacks", + "priority": null, + "phase": "initial" + } + ], + "duration": { + "type": "temporary", + "description": "

Until you or an ally rolls a failure with Fear.

" } }, - "changes": [ - { - "key": "system.advantageSources", - "mode": 2, - "value": "1", - "priority": null - } - ], "disabled": false, "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null + "value": null, + "units": "seconds", + "expiry": null, + "expired": false }, - "description": "", + "description": "

You gain advantage on attack rolls until you or an ally rolls a failure with Fear.

", "tint": "#ffffff", "statuses": [], "sort": 0, @@ -160,6 +162,16 @@ "_stats": { "compendiumSource": null }, + "start": { + "time": 0, + "combat": null, + "combatant": null, + "initiative": null, + "round": null, + "turn": null + }, + "showIcon": 1, + "folder": null, "_key": "!items.effects!Ef1JsUG50LIoKx2F.s7ma4TNgAvt0ZgEW" } ], diff --git a/templates/sheets/activeEffect/settings.hbs b/templates/sheets/activeEffect/settings.hbs index 9307ff65..09b78856 100644 --- a/templates/sheets/activeEffect/settings.hbs +++ b/templates/sheets/activeEffect/settings.hbs @@ -26,11 +26,11 @@ {{formGroup systemFields.duration.fields.type value=source.system.duration.type localize=true }} -
-
- {{formInput systemFields.duration.fields.description value=source.system.duration.description localize=true }} -
+
+
+ {{formInput systemFields.duration.fields.description value=source.system.duration.description localize=true }}
+
{{formGroup fields.start.fields.time value=source.start.time localize=true }} From a62d28cd96fdfdcdc74e3c8f9e6528edbf9840f9 Mon Sep 17 00:00:00 2001 From: CPTN_Cosmo Date: Tue, 14 Apr 2026 18:51:28 +0200 Subject: [PATCH 10/11] updated contributing guidelines (#1800) --- CONTRIBUTING.md | 79 ++++--------------------------------------------- 1 file changed, 5 insertions(+), 74 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b9099005..261c26bd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,78 +1,9 @@ -# Contributing to Foundryborne +# Contributing to Daggerheart -Welcome! This is a community-driven project to bring [Daggerheart](https://www.daggerheart.com/) to [FoundryVTT](https://foundryvtt.com/) as a full system. We're excited to have you here and appreciate your interest in contributing. +Thank you for your interest in contributing to the Foundryborne project! ---- +To ensure that all contributions align with our project goals and architectural standards, we ask that you **do not submit outside contributions without first receiving feedback from the development team.** -## ๐Ÿค How to Contribute +If you have an idea or a fix you'd like to contribute, please start a discussion or open an issue first. We'd love to hear from you and collaborate on the best way to move forward! -We welcome contributions of all kinds: - -- Bug reports -- Feature suggestions -- Code contributions -- UI/UX mockups -- Documentation improvements -- Questions and discussions - -Please be respectful and collaborative โ€” weโ€™re all here to build something great together. - -### Community Translations - -Please note that we are not accepting community translations in the main project. Instead, community translations should be published as a module. - ---- - -## ๐Ÿงญ General Guidelines - -- **Use GitHub Issues** to report bugs or propose features -- **Start a Discussion** for larger ideas or questions -- **Open a Pull Request** once you've confirmed your work aligns with project direction -- **Keep things modular and maintainable** โ€” if you're not sure how to structure something, ask! -- **Orient your code on existing examples**, and feel free to suggest a standard if it makes things clearer - ---- - -## ๐Ÿ—‚๏ธ Project Structure - -Please try to follow the general logic of the existing code when submitting PRs. - -We encourage contributors to leave comments or open Discussions when proposing structural or organizational changes. - ---- - -## ๐Ÿงพ Issue & PR Best Practices - -**For Issues:** - -- Use clear, descriptive titles -- Provide a concise explanation of the problem or idea -- Include reproduction steps or example scenarios if it's a bug -- Add screenshots or logs if helpful - -**For Pull Requests:** - -- Use a clear title summarizing the change -- Provide a brief description of what your code does and why -- Link to any related Issues -- Keep PRs focused โ€” smaller is better - ---- - -## ๐Ÿ”– Labels and Boards - -We use GitHub labels to help organize contributions. If your issue or PR relates to a specific category, feel free to tag it. There is also a GitHub Project Board to help track active work and priorities. - ---- - -## ๐Ÿ“ฃ Communication - -Discussions are currently happening on GitHub โ€” in Issues, PRs, and [GitHub Discussions](https://github.com/Foundryborne/daggerheart/discussions). You're welcome to use any of these, though we may consolidate to one in the future. - ---- - -## ๐Ÿค— Thank You! - -Whether you're fixing a typo or designing entire mechanics โ€” every contribution matters. Thank you for helping bring _Daggerheart_ to life in FoundryVTT through **Foundryborne**! - -๐Ÿธ๐Ÿ› ๏ธ +Thank you for your understanding and support. From 1176328f625c66a86fe5e6e497d1f01062d4ecef Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 14 Apr 2026 20:55:10 +0200 Subject: [PATCH 11/11] Updated deploy.yml --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e245c7fa..553a1a17 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -35,7 +35,7 @@ jobs: env: version: ${{steps.get_version.outputs.version-without-v}} url: https://github.com/${{github.repository}} - manifest: https://raw.githubusercontent.com/${{github.repository}}/main/system.json + manifest: https://raw.githubusercontent.com/${{github.repository}}/v14/system.json download: https://github.com/${{github.repository}}/releases/download/${{github.event.release.tag_name}}/system.zip # Create a zip file with all files required by the module to add to the release