From ff955bbe01fdfa6f355fbe66655847c688733707 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Fri, 8 Aug 2025 09:39:22 +0200 Subject: [PATCH 01/13] Fixed so compendium adversaries can be dragged from environmnets (#716) --- module/applications/sheets/actors/environment.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/module/applications/sheets/actors/environment.mjs b/module/applications/sheets/actors/environment.mjs index 70f833de..aa2759a2 100644 --- a/module/applications/sheets/actors/environment.mjs +++ b/module/applications/sheets/actors/environment.mjs @@ -101,8 +101,7 @@ export default class DhpEnvironment extends DHBaseActorSheet { const item = event.currentTarget.closest('.inventory-item'); if (item) { - const adversary = game.actors.find(x => x.type === 'adversary' && x.id === item.dataset.itemId); - const adversaryData = { type: 'Actor', uuid: adversary.uuid }; + const adversaryData = { type: 'Actor', uuid: item.dataset.itemUuid }; event.dataTransfer.setData('text/plain', JSON.stringify(adversaryData)); event.dataTransfer.setDragImage(item, 60, 0); } From 263e36500e955170569ce97a899f76ab56a21f34 Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Fri, 8 Aug 2025 21:14:28 +1000 Subject: [PATCH 02/13] Fix for #695 (#718) Co-authored-by: Chris Ryan --- src/packs/ancestries/feature_Tusks_YhxD1ujZpftPu19w.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packs/ancestries/feature_Tusks_YhxD1ujZpftPu19w.json b/src/packs/ancestries/feature_Tusks_YhxD1ujZpftPu19w.json index 25f0d1b2..4078ca03 100644 --- a/src/packs/ancestries/feature_Tusks_YhxD1ujZpftPu19w.json +++ b/src/packs/ancestries/feature_Tusks_YhxD1ujZpftPu19w.json @@ -45,7 +45,7 @@ }, "effects": [ { - "name": "Base", + "name": "Tusks", "type": "base", "_id": "klEyAxQa5YHXVnrl", "img": "icons/creatures/abilities/fang-tooth-blood-red.webp", From 1f5c3531be2040f44a6a7363f3970f1259130173 Mon Sep 17 00:00:00 2001 From: CPTN_Cosmo Date: Fri, 8 Aug 2025 18:37:28 +0200 Subject: [PATCH 03/13] added more credits (#723) --- .../journal_Welcome___Information_g7NhKvwltwafmMyR.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/packs/journals/journal_Welcome___Information_g7NhKvwltwafmMyR.json b/src/packs/journals/journal_Welcome___Information_g7NhKvwltwafmMyR.json index b08e4a1d..6620d62c 100644 --- a/src/packs/journals/journal_Welcome___Information_g7NhKvwltwafmMyR.json +++ b/src/packs/journals/journal_Welcome___Information_g7NhKvwltwafmMyR.json @@ -132,7 +132,7 @@ "image": {}, "text": { "format": 1, - "content": "

This product includes materials from the Daggerheart System Reference Document 1.0, © Critical Role, LLC. under the terms of the Darrington Press Community Gaming (DPCGL) License. More information can be found at https://www.daggerheart.com. There are no previous modifications by others.

The Foundryborne Team

The Foundryborne Team consists of:

With Art from:

We would also like to thank the FoundryVTT team for their support in publishing this system.

And, of course, special thanks to the teams at Critical Role and Darrington Press for making such a wonderful game and updating the license to allow a FoundryVTT version of the system.

The Foundryborne Community

Without our amazing community this project would not have been possible.

You kept us going with both direct contributions and just endless support!

We thank you with all our hearts.

Come join us!

" + "content": "

This product includes materials from the Daggerheart System Reference Document 1.0, © Critical Role, LLC. under the terms of the Darrington Press Community Gaming (DPCGL) License. More information can be found at https://www.daggerheart.com. There are no previous modifications by others.

The Foundryborne Team

The Foundryborne Team consists of:

With Art from:

And special thanks to our hard working community testers:

We would also like to thank the FoundryVTT team for their support in publishing this system.

And, of course, special thanks to the teams at Critical Role and Darrington Press for making such a wonderful game and updating the license to allow a FoundryVTT version of the system.

The Foundryborne Community

Without our amazing community this project would not have been possible.

You kept us going with both direct contributions and just endless support!

We thank you with all our hearts.

Come join us!

" }, "video": { "controls": true, @@ -153,8 +153,8 @@ "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1754225939902, - "modifiedTime": 1754226994508, - "lastModifiedBy": "l5jB3XmcVXOTQpRZ" + "modifiedTime": 1754668980876, + "lastModifiedBy": "Cf0YKwnZ1OHBZWl8" }, "_key": "!journal.pages!g7NhKvwltwafmMyR.dP6xSKEld4TSqHhK" } From 5d0a4382cc43cd4cdcfce0e5ecb64d66ff94eb66 Mon Sep 17 00:00:00 2001 From: Nikhil Nagarajan Date: Fri, 8 Aug 2025 13:05:20 -0400 Subject: [PATCH 04/13] Fixed default images (#725) --- .../domainCard_Gifted_Tracker_VZ2b4zfRzV73XTuT.json | 8 ++++---- .../domainCard_Healing_Hands_WTlhnQMajc1r8i50.json | 8 ++++---- .../domains/domainCard_Life_Ward_OszbCj0jTqq2ADx9.json | 8 ++++---- .../domains/domainCard_On_the_Brink_zbxPl81kbWEegKQN.json | 8 ++++---- .../subclasses/feature_Epic_Poetry_eCoEWkWuZPMZ9C6a.json | 7 ++++--- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/packs/domains/domainCard_Gifted_Tracker_VZ2b4zfRzV73XTuT.json b/src/packs/domains/domainCard_Gifted_Tracker_VZ2b4zfRzV73XTuT.json index c274b9ac..a9e66110 100644 --- a/src/packs/domains/domainCard_Gifted_Tracker_VZ2b4zfRzV73XTuT.json +++ b/src/packs/domains/domainCard_Gifted_Tracker_VZ2b4zfRzV73XTuT.json @@ -64,7 +64,7 @@ "effects": [ { "name": "Gifted Tracker", - "img": "icons/svg/item-bag.svg", + "img": "systems/daggerheart/assets/icons/domains/domain-card/sage.png", "origin": "Compendium.daggerheart.domains.Item.VZ2b4zfRzV73XTuT", "transfer": false, "_id": "47Oh2weCdmuvKHM9", @@ -104,12 +104,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1754114056078, - "modifiedTime": 1754114073478, - "lastModifiedBy": "Q9NoTaEarn3VMS6Z" + "modifiedTime": 1754670410126, + "lastModifiedBy": "49DaecTcBSc5d0DA" }, "_key": "!items.effects!VZ2b4zfRzV73XTuT.47Oh2weCdmuvKHM9" } diff --git a/src/packs/domains/domainCard_Healing_Hands_WTlhnQMajc1r8i50.json b/src/packs/domains/domainCard_Healing_Hands_WTlhnQMajc1r8i50.json index 622e2266..a7aeb6b2 100644 --- a/src/packs/domains/domainCard_Healing_Hands_WTlhnQMajc1r8i50.json +++ b/src/packs/domains/domainCard_Healing_Hands_WTlhnQMajc1r8i50.json @@ -389,7 +389,7 @@ "effects": [ { "name": "Healed by Healing Hands", - "img": "icons/svg/item-bag.svg", + "img": "systems/daggerheart/assets/icons/domains/domain-card/splendor.png", "origin": "Compendium.daggerheart.domains.Item.WTlhnQMajc1r8i50", "transfer": false, "_id": "sd5liP4ZcVeTMAoW", @@ -422,12 +422,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1754263407455, - "modifiedTime": 1754263727114, - "lastModifiedBy": "Q9NoTaEarn3VMS6Z" + "modifiedTime": 1754670504951, + "lastModifiedBy": "49DaecTcBSc5d0DA" }, "_key": "!items.effects!WTlhnQMajc1r8i50.sd5liP4ZcVeTMAoW" } diff --git a/src/packs/domains/domainCard_Life_Ward_OszbCj0jTqq2ADx9.json b/src/packs/domains/domainCard_Life_Ward_OszbCj0jTqq2ADx9.json index efdec150..78b2ec5e 100644 --- a/src/packs/domains/domainCard_Life_Ward_OszbCj0jTqq2ADx9.json +++ b/src/packs/domains/domainCard_Life_Ward_OszbCj0jTqq2ADx9.json @@ -66,7 +66,7 @@ "effects": [ { "name": "Life Ward", - "img": "icons/svg/item-bag.svg", + "img": "systems/daggerheart/assets/icons/domains/domain-card/splendor.png", "origin": "Compendium.daggerheart.domains.Item.OszbCj0jTqq2ADx9", "transfer": false, "_id": "E7Ou4OMEy3TeK1Gf", @@ -99,12 +99,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1754264687962, - "modifiedTime": 1754264717646, - "lastModifiedBy": "Q9NoTaEarn3VMS6Z" + "modifiedTime": 1754670535710, + "lastModifiedBy": "49DaecTcBSc5d0DA" }, "_key": "!items.effects!OszbCj0jTqq2ADx9.E7Ou4OMEy3TeK1Gf" } diff --git a/src/packs/domains/domainCard_On_the_Brink_zbxPl81kbWEegKQN.json b/src/packs/domains/domainCard_On_the_Brink_zbxPl81kbWEegKQN.json index a7276bba..b6f6548f 100644 --- a/src/packs/domains/domainCard_On_the_Brink_zbxPl81kbWEegKQN.json +++ b/src/packs/domains/domainCard_On_the_Brink_zbxPl81kbWEegKQN.json @@ -37,7 +37,7 @@ } }, "_id": "UJTsJlnhi5Zi0XQ2", - "img": "icons/magic/life/heart-cross-blue.webp", + "img": "systems/daggerheart/assets/icons/domains/domain-card/bone.png", "changes": [ { "key": "system.rules.damageReduction.thresholdImmunities.minor", @@ -67,12 +67,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", "createdTime": 1754303484332, - "modifiedTime": 1754303570504, - "lastModifiedBy": "MQSznptE5yLT7kj8" + "modifiedTime": 1754670012467, + "lastModifiedBy": "49DaecTcBSc5d0DA" }, "_key": "!items.effects!zbxPl81kbWEegKQN.UJTsJlnhi5Zi0XQ2" } diff --git a/src/packs/subclasses/feature_Epic_Poetry_eCoEWkWuZPMZ9C6a.json b/src/packs/subclasses/feature_Epic_Poetry_eCoEWkWuZPMZ9C6a.json index 3429f585..f80c05e3 100644 --- a/src/packs/subclasses/feature_Epic_Poetry_eCoEWkWuZPMZ9C6a.json +++ b/src/packs/subclasses/feature_Epic_Poetry_eCoEWkWuZPMZ9C6a.json @@ -23,7 +23,7 @@ } }, "_id": "RSmscgGyuHJucF6C", - "img": "icons/magic/life/heart-cross-blue.webp", + "img": "icons/sundries/documents/document-letter-blue.webp", "changes": [ { "key": "system.bonuses.rally", @@ -53,10 +53,11 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", "systemVersion": "0.0.1", - "lastModifiedBy": null + "lastModifiedBy": "49DaecTcBSc5d0DA", + "modifiedTime": 1754669077252 }, "_key": "!items.effects!eCoEWkWuZPMZ9C6a.RSmscgGyuHJucF6C" } From f9cb0954f914201ee3c7c12d2cc43657c39878e7 Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Fri, 8 Aug 2025 21:34:55 +0200 Subject: [PATCH 05/13] Bug/chat roll fixes (#726) * #635 & #637 * #653 * Fix: #681 #682 #685 #686 * Fix duplicate messages * Remove comments --- .../sheets/api/application-mixin.mjs | 14 + module/applications/ui/chatLog.mjs | 35 -- module/data/action/baseAction.mjs | 29 +- module/data/chat-message/adversaryRoll.mjs | 89 ++-- module/data/fields/action/targetField.mjs | 1 + module/data/fields/actionField.mjs | 1 + module/dice/d20Roll.mjs | 8 +- module/dice/damageRoll.mjs | 4 +- module/dice/dhRoll.mjs | 31 +- module/dice/dualityRoll.mjs | 2 +- module/documents/chatMessage.mjs | 93 +++-- .../feature_Rally_PydiMnNCKpd44SGS.json | 13 +- ...ture_Rally__Level_5__TVeEyqmPPiRa2r3i.json | 13 +- styles/less/global/chat.less | 8 + styles/less/ui/chat/chat.less | 382 ++---------------- templates/dialogs/dice-roll/costSelection.hbs | 12 +- templates/ui/chat/chat-message.hbs | 31 +- templates/ui/chat/parts/button-part.hbs | 6 +- templates/ui/chat/parts/roll-part.hbs | 8 +- templates/ui/chat/parts/target-part.hbs | 6 +- templates/ui/chat/roll.hbs | 2 +- 21 files changed, 242 insertions(+), 546 deletions(-) diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 2a02ba01..f8d2697f 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -333,6 +333,20 @@ export default function DHApplicationMixin(Base) { const doc = getDocFromElementSync(target); return doc?.system?.attack?.damage.parts.length || doc?.damage?.parts.length; }, + callback: async (target, event) => { + const doc = await getDocFromElement(target), + action = doc?.system?.attack ?? doc; + return action && action.use(event, { byPassRoll: true }) + } + }); + + options.unshift({ + name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem', + icon: 'fa-solid fa-burst', + condition: target => { + const doc = getDocFromElementSync(target); + return doc?.system?.attack?.damage.parts.length || doc?.damage?.parts.length; + }, callback: async (target, event) => { const doc = await getDocFromElement(target), action = doc?.system?.attack ?? doc; diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index fd9ab096..79d5e468 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -33,14 +33,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo html.querySelectorAll('.simple-roll-button').forEach(element => element.addEventListener('click', event => this.onRollSimple(event, data.message)) ); - html.querySelectorAll('.target-container').forEach(element => { - element.addEventListener('mouseenter', this.hoverTarget); - element.addEventListener('mouseleave', this.unhoverTarget); - element.addEventListener('click', this.clickTarget); - }); - html.querySelectorAll('.button-target-selection').forEach(element => { - element.addEventListener('click', event => this.onTargetSelection(event, data.message)); - }); html.querySelectorAll('.healing-button').forEach(element => element.addEventListener('click', event => this.onHealing(event, data.message)) ); @@ -138,33 +130,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo }); } - onTargetSelection(event, message) { - event.stopPropagation(); - const msg = ui.chat.collection.get(message._id); - msg.system.targetMode = Boolean(event.target.dataset.targetHit); - } - - hoverTarget(event) { - event.stopPropagation(); - const token = canvas.tokens.get(event.currentTarget.dataset.token); - if (!token?.controlled) token._onHoverIn(event, { hoverOutOthers: true }); - } - - unhoverTarget(event) { - const token = canvas.tokens.get(event.currentTarget.dataset.token); - if (!token?.controlled) token._onHoverOut(event); - } - - clickTarget(event) { - event.stopPropagation(); - const token = canvas.tokens.get(event.currentTarget.dataset.token); - if (!token) { - ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.attackTargetDoesNotExist')); - return; - } - game.canvas.pan(token); - } - async onRollSimple(event, message) { const buttonType = event.target.dataset.type ?? 'damage', total = message.rolls.reduce((a, c) => a + Roll.fromJSON(c).total, 0), diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 070864da..516419b9 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -115,7 +115,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel if (!this.actor) throw new Error("An Action can't be used outside of an Actor context."); if (this.chatDisplay) await this.toChat(); - let { byPassRoll } = options, config = this.prepareConfig(event, byPassRoll); for (let i = 0; i < this.constructor.extraSchemas.length; i++) { @@ -145,9 +144,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel if (this.rollDamage && this.damage.parts.length) await this.rollDamage(event, config); else if (this.trigger) await this.trigger(event, config); else if (this.hasSave || this.hasEffect) { - const roll = new Roll(''); + const roll = new CONFIG.Dice.daggerheart.DHRoll(''); roll._evaluated = true; - if (this.hasTarget) config.targetSelection = config.targets.length > 0; + if(config.hasTarget) config.targetSelection = config.targets.length > 0; await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config); } } @@ -180,7 +179,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel hasHealing: this.damage?.parts?.length && this.type === 'healing', hasEffect: !!this.effects?.length, hasSave: this.hasSave, - hasTarget: true, selectedRollMode: game.settings.get('core', 'rollMode'), isFastForward: event.shiftKey, data: this.getRollData(), @@ -248,8 +246,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel ) this.update({ 'uses.value': this.uses.value + 1 }); - if (config.roll?.success || successCost) - (config.message ?? config.parent).update({ 'system.successConsumed': true }); + if(config.roll?.success || successCost) { + setTimeout(() => { + (config.message ?? config.parent).update({'system.successConsumed': true}) + }, 50); + } } /* */ @@ -368,15 +369,15 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel async updateChatMessage(message, targetId, changes, chain = true) { setTimeout(async () => { - const chatMessage = ui.chat.collection.get(message._id), - msgTarget = - chatMessage.system.targets.find(mt => mt.id === targetId) ?? - chatMessage.system.oldTargets.find(mt => mt.id === targetId); - msgTarget.saved = changes; + const chatMessage = ui.chat.collection.get(message._id); + await chatMessage.update({ - system: { - targets: chatMessage.system.targets, - oldTargets: chatMessage.system.oldTargets + flags: { + [game.system.id]: { + "reactionRolls": { + [targetId]: changes + } + } } }); }, 100); diff --git a/module/data/chat-message/adversaryRoll.mjs b/module/data/chat-message/adversaryRoll.mjs index fa6e48a6..5e1835c0 100644 --- a/module/data/chat-message/adversaryRoll.mjs +++ b/module/data/chat-message/adversaryRoll.mjs @@ -25,7 +25,6 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { title: new fields.StringField(), roll: new fields.ObjectField(), targets: targetsField(), - oldTargets: targetsField(), targetSelection: new fields.BooleanField({ initial: false }), hasRoll: new fields.BooleanField({ initial: false }), hasDamage: new fields.BooleanField({ initial: false }), @@ -63,24 +62,14 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { return actionItem.system.actionsList?.find(a => a.id === this.source.action); } - get messageTemplate() { - return 'systems/daggerheart/templates/ui/chat/roll.hbs'; - } - get targetMode() { return this.targetSelection; } set targetMode(mode) { this.targetSelection = mode; - this.updateTargets(); this.registerTargetHook(); - this.parent.update({ - system: { - targetSelection: this.targetSelection, - oldTargets: this.oldTargets - } - }); + this.updateTargets(); } get hitTargets() { @@ -88,29 +77,25 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { } async updateTargets() { - this.currentTargets = this.getTargetList(); - if (!this.targetSelection) { - this.currentTargets.forEach(ct => { - if (this.targets.find(t => t.actorId === ct.actorId)) return; - const indexTarget = this.oldTargets.findIndex(ot => ot.actorId === ct.actorId); - if (indexTarget === -1) this.oldTargets.push(ct); - }); - if (this.hasSave) this.setPendingSaves(); - if (this.currentTargets.length) { - if (!this.parent._id) return; - const updates = await this.parent.update({ - system: { - oldTargets: this.oldTargets - } - }); - if (!updates && ui.chat.collection.get(this.parent.id)) ui.chat.updateMessage(this.parent); + if(!ui.chat.collection.get(this.parent.id)) return; + let targets; + if(this.targetSelection) + targets = this.targets; + else + targets = Array.from(game.user.targets).map(t => game.system.api.fields.ActionFields.TargetField.formatTarget(t)); + + this.parent.setFlag(game.system.id, "targets", targets); + await this.parent.updateSource({ + system: { + targetSelection: this.targetSelection } - } + }); } registerTargetHook() { - if (this.targetSelection && this.targetHook !== null) { - Hooks.off('targetToken', this.targetHook); + if(!this.parent.isAuthor) return; + if(this.targetSelection && this.targetHook !== null) { + Hooks.off("targetToken", this.targetHook); this.targetHook = null; } else if (!this.targetSelection && this.targetHook === null) { this.targetHook = Hooks.on('targetToken', foundry.utils.debounce(this.updateTargets.bind(this), 50)); @@ -120,35 +105,35 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { prepareDerivedData() { if (this.hasTarget) { this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0; - this.updateTargets(); - this.registerTargetHook(); - if (this.targetSelection === true) { - this.targetShort = this.targets.reduce( - (a, c) => { - if (c.hit) a.hit += 1; - else a.miss += 1; - return a; - }, - { hit: 0, miss: 0 } - ); + this.currentTargets = this.getTargetList(); + this. registerTargetHook(); + + if(this.targetSelection === true && this.hasRoll) { + this.targetShort = this.targets.reduce((a,c) => { + if(c.hit) a.hit += 1; + else a.miss += 1; + return a; + }, {hit: 0, miss: 0}) } if (this.hasSave) this.setPendingSaves(); } this.canViewSecret = this.parent.speakerActor?.testUserPermission(game.user, 'OBSERVER'); + this.canButtonApply = game.user.isGM; } getTargetList() { - return this.targetSelection !== true - ? Array.from(game.user.targets).map(t => { - const target = game.system.api.fields.ActionFields.TargetField.formatTarget(t), - oldTarget = - this.targets.find(ot => ot.actorId === target.actorId) ?? - this.oldTargets.find(ot => ot.actorId === target.actorId); - if (oldTarget) return oldTarget; - return target; - }) - : this.targets; + const targets = this.targetSelection && this.parent.isAuthor ? this.targets : (this.parent.getFlag(game.system.id, "targets") ?? this.targets), + reactionRolls = this.parent.getFlag(game.system.id, "reactionRolls"); + + if(reactionRolls) { + Object.entries(reactionRolls).forEach(([k, r]) => { + const target = targets.find(t => t.id === k); + if(target) target.saved = r; + }); + } + + return targets; } setPendingSaves() { diff --git a/module/data/fields/action/targetField.mjs b/module/data/fields/action/targetField.mjs index 681f8353..bfb01db9 100644 --- a/module/data/fields/action/targetField.mjs +++ b/module/data/fields/action/targetField.mjs @@ -15,6 +15,7 @@ export default class TargetField extends fields.SchemaField { static prepareConfig(config) { if (!this.target?.type) return []; + config.hasTarget = true; let targets; if (this.target?.type === CONFIG.DH.GENERAL.targetTypes.self.id) targets = [this.actor.token ?? this.actor.prototypeToken]; diff --git a/module/data/fields/actionField.mjs b/module/data/fields/actionField.mjs index c3bdcaaa..9300a02e 100644 --- a/module/data/fields/actionField.mjs +++ b/module/data/fields/actionField.mjs @@ -285,6 +285,7 @@ export function ActionMixin(Base) { } }; + ChatMessage.applyRollMode(msg, game.settings.get('core', 'rollMode')); cls.create(msg); } } diff --git a/module/dice/d20Roll.mjs b/module/dice/d20Roll.mjs index 45471532..d636fd27 100644 --- a/module/dice/d20Roll.mjs +++ b/module/dice/d20Roll.mjs @@ -139,17 +139,17 @@ export default class D20Roll extends DHRoll { static postEvaluate(roll, config = {}) { const data = super.postEvaluate(roll, config); data.type = config.roll?.type; + data.difficulty = config.roll.difficulty; if (config.targets?.length) { config.targetSelection = true; config.targets.forEach(target => { const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion; target.hit = roll.isCritical || roll.total >= difficulty; }); - data.success = config.targets.some(target => target.hit); - } else if (config.roll.difficulty) { - data.difficulty = config.roll.difficulty; + data.success = config.targets.some(target => target.hit) + } else if (config.roll.difficulty) data.success = roll.isCritical || roll.total >= config.roll.difficulty; - } + data.advantage = { type: config.roll.advantage, dice: roll.dAdvantage?.denomination, diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index 999a4bb8..31458516 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -30,16 +30,16 @@ export default class DamageRoll extends DHRoll { } static async buildPost(roll, config, message) { + const chatMessage = config.source?.message ? ui.chat.collection.get(config.source.message) : getDocumentClass('ChatMessage').applyRollMode({}, config.rollMode); if (game.modules.get('dice-so-nice')?.active) { const pool = foundry.dice.terms.PoolTerm.fromRolls( Object.values(config.damage).flatMap(r => r.parts.map(p => p.roll)) ), diceRoll = Roll.fromTerms([pool]); - await game.dice3d.showForRoll(diceRoll, game.user, true); + await game.dice3d.showForRoll(diceRoll, game.user, true, chatMessage.whisper, chatMessage.blind); } await super.buildPost(roll, config, message); if (config.source?.message) { - const chatMessage = ui.chat.collection.get(config.source.message); chatMessage.update({ 'system.damage': config.damage }); } } diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index 1b490921..710a2728 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -2,7 +2,7 @@ import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs'; export default class DHRoll extends Roll { baseTerms = []; - constructor(formula, data, options) { + constructor(formula, data = {}, options = {}) { super(formula, data, options); if (!this.data || !Object.keys(this.data).length) this.data = options.data; } @@ -15,6 +15,8 @@ export default class DHRoll extends Roll { static messageType = 'adversaryRoll'; + static CHAT_TEMPLATE = 'systems/daggerheart/templates/ui/chat/roll.hbs'; + static DefaultDialog = D20RollDialog; static async build(config = {}, message = {}) { @@ -92,9 +94,36 @@ export default class DHRoll extends Roll { system: config, rolls: [roll] }; + config.selectedRollMode ??= game.settings.get('core', 'rollMode'); if(roll._evaluated) return await cls.create(msg, { rollMode: config.selectedRollMode }); return msg; } + + /** @inheritDoc */ + async render({flavor, template=this.constructor.CHAT_TEMPLATE, isPrivate=false, ...options}={}) { + if ( !this._evaluated ) return; + const chatData = await this._prepareChatRenderContext({flavor, isPrivate, ...options}); + return foundry.applications.handlebars.renderTemplate(template, chatData); + } + + /** @inheritDoc */ + async _prepareChatRenderContext({flavor, isPrivate=false, ...options}={}) { + if(isPrivate) { + return { + user: game.user.id, + flavor: null, + title: "???", + roll: { + total: "??" + }, + hasRoll: true, + isPrivate + } + } else { + options.message.system.user = game.user.id; + return options.message.system; + } + } static applyKeybindings(config) { if (config.event) diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index 35bae725..030b4df2 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -149,7 +149,7 @@ export default class DualityRoll extends D20Roll { } if (this.rallyFaces) this.terms.push( - new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }), + new foundry.dice.terms.OperatorTerm({ operator: '+' }), new foundry.dice.terms.Die({ faces: this.rallyFaces }) ); } diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs index 302ba1d8..06a9f147 100644 --- a/module/documents/chatMessage.mjs +++ b/module/documents/chatMessage.mjs @@ -1,21 +1,31 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { async renderHTML() { - if (this.system.messageTemplate) - this.content = await foundry.applications.handlebars.renderTemplate(this.system.messageTemplate, { - ...this.system, - _source: this.system._source - }); - const actor = game.actors.get(this.speaker.actor); - const actorData = actor ?? { + const actorData = actor && this.isContentVisible ? actor : { img: this.author.avatar ? this.author.avatar : 'icons/svg/mystery-man.svg', name: '' }; /* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */ const html = await super.renderHTML({ actor: actorData, author: this.author }); - this.applyPermission(html); - if (this.type === 'dualityRoll') { + this.enrichChatMessage(html); + this.addChatListeners(html); + + return html; + } + + enrichChatMessage(html) { + const elements = html.querySelectorAll('[data-perm-id]'); + elements.forEach(e => { + const uuid = e.dataset.permId, + document = fromUuidSync(uuid); + if (!document) return; + + e.setAttribute('data-view-perm', document.testUserPermission(game.user, 'OBSERVER')); + e.setAttribute('data-use-perm', document.testUserPermission(game.user, 'OWNER')); + }); + + if (this.isContentVisible && this.type === 'dualityRoll') { html.classList.add('duality'); switch (this.system.roll?.result?.duality) { case 1: @@ -29,36 +39,9 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { break; } } - - this.enrichChatMessage(html); - - return html; } - applyPermission(html) { - const elements = html.querySelectorAll('[data-perm-id]'); - elements.forEach(e => { - const uuid = e.dataset.permId, - document = fromUuidSync(uuid); - if (!document) return; - - e.setAttribute('data-view-perm', document.testUserPermission(game.user, 'OBSERVER')); - e.setAttribute('data-use-perm', document.testUserPermission(game.user, 'OWNER')); - }); - } - - async _preCreate(data, options, user) { - options.speaker = ChatMessage.getSpeaker(); - const rollActorOwner = data.rolls?.[0]?.data?.parent?.owner; - if (rollActorOwner) { - data.author = rollActorOwner ? rollActorOwner.id : data.author; - await this.updateSource({ author: rollActorOwner ?? user }); - } - - return super._preCreate(data, options, rollActorOwner ?? user); - } - - enrichChatMessage(html) { + addChatListeners(html) { html.querySelectorAll('.damage-button').forEach(element => element.addEventListener('click', this.onDamage.bind(this)) ); @@ -66,6 +49,16 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { html.querySelectorAll('.duality-action-effect').forEach(element => element.addEventListener('click', this.onApplyEffect.bind(this)) ); + + html.querySelectorAll('.roll-target').forEach(element => { + element.addEventListener('mouseenter', this.hoverTarget); + element.addEventListener('mouseleave', this.unhoverTarget); + element.addEventListener('click', this.clickTarget); + }); + + html.querySelectorAll('.button-target-selection').forEach(element => { + element.addEventListener('click', this.onTargetSelection.bind(this)); + }); } getTargetList() { @@ -146,4 +139,30 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { if (action) action.consume(this.system, true); } } + + hoverTarget(event) { + event.stopPropagation(); + const token = canvas.tokens.get(event.currentTarget.dataset.token); + if (!token?.controlled) token._onHoverIn(event, { hoverOutOthers: true }); + } + + unhoverTarget(event) { + const token = canvas.tokens.get(event.currentTarget.dataset.token); + if (!token?.controlled) token._onHoverOut(event); + } + + clickTarget(event) { + event.stopPropagation(); + const token = canvas.tokens.get(event.currentTarget.dataset.token); + if (!token) { + ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.attackTargetDoesNotExist')); + return; + } + game.canvas.pan(token); + } + + onTargetSelection(event) { + event.stopPropagation(); + this.system.targetMode = Boolean(event.target.dataset.targetHit); + } } diff --git a/src/packs/classes/feature_Rally_PydiMnNCKpd44SGS.json b/src/packs/classes/feature_Rally_PydiMnNCKpd44SGS.json index 4b509228..c5933ca8 100644 --- a/src/packs/classes/feature_Rally_PydiMnNCKpd44SGS.json +++ b/src/packs/classes/feature_Rally_PydiMnNCKpd44SGS.json @@ -14,16 +14,7 @@ "description": "", "chatDisplay": false, "actionType": "action", - "cost": [ - { - "scalable": false, - "key": "hitPoints", - "value": 1, - "keyIsID": false, - "step": null, - "consumeOnSuccess": false - } - ], + "cost": [], "uses": { "value": null, "max": "1", @@ -67,7 +58,7 @@ { "key": "system.bonuses.rally", "mode": 2, - "value": "1d6", + "value": "d6", "priority": null } ], diff --git a/src/packs/classes/feature_Rally__Level_5__TVeEyqmPPiRa2r3i.json b/src/packs/classes/feature_Rally__Level_5__TVeEyqmPPiRa2r3i.json index bba20558..da124244 100644 --- a/src/packs/classes/feature_Rally__Level_5__TVeEyqmPPiRa2r3i.json +++ b/src/packs/classes/feature_Rally__Level_5__TVeEyqmPPiRa2r3i.json @@ -14,16 +14,7 @@ "description": "", "chatDisplay": true, "actionType": "action", - "cost": [ - { - "scalable": false, - "key": "hitPoints", - "value": 1, - "keyIsID": false, - "step": null, - "consumeOnSuccess": false - } - ], + "cost": [], "uses": { "value": null, "max": "1", @@ -67,7 +58,7 @@ { "key": "system.bonuses.rally", "mode": 2, - "value": "1d8", + "value": "d8", "priority": null } ], diff --git a/styles/less/global/chat.less b/styles/less/global/chat.less index 1e37309d..37ec993d 100644 --- a/styles/less/global/chat.less +++ b/styles/less/global/chat.less @@ -32,6 +32,7 @@ .message-header-metadata { flex: none; display: flex; + flex-direction: column; .message-metadata { font-family: @font-body; @@ -73,6 +74,13 @@ .message-content { padding-bottom: 8px; + .flavor-text { + font-size: var(--font-size-12); + line-height: 20px; + color: var(--color-dark-4); + text-align: center; + display: block; + } } } } diff --git a/styles/less/ui/chat/chat.less b/styles/less/ui/chat/chat.less index 9afa32d3..6ffd00cf 100644 --- a/styles/less/ui/chat/chat.less +++ b/styles/less/ui/chat/chat.less @@ -11,333 +11,6 @@ } } - /* &.roll { - .dice-flavor { - text-align: center; - font-weight: bold; - } - .dice-tooltip { - .dice-rolls { - &.duality { - display: flex; - gap: 0.25rem; - - > .roll { - background-image: none; - - .reroll-button { - border: none; - background: initial; - width: 42px; - - &:hover { - background: var(--button-background-color); - border: 1px solid var(--button-border-color); - } - } - } - } - - &.rerollable { - position: relative; - flex: none; - - .dice-rerolled { - z-index: 2; - position: absolute; - right: 0; - font-size: 12px; - cursor: help; - } - - .reroll-button { - border: none; - background: initial; - - &:hover { - background: var(--button-background-color); - border: 1px solid var(--button-border-color); - } - } - } - - // margin: 0; - > .roll { - display: flex; - align-items: center; - justify-content: center; - gap: 4px; - margin-bottom: 4px; - - .dice-container { - display: flex; - flex-direction: column; - gap: 2px; - position: relative; - - .dice-title { - color: var(--color-light-1); - text-shadow: 0 0 1px black; - } - - .dice-rerolled { - z-index: 2; - position: absolute; - right: -2px; - font-size: 12px; - cursor: help; - } - - .dice-inner-container { - display: flex; - align-items: center; - justify-content: center; - position: relative; - &.hope, - &.fear { - .dice-wrapper { - clip-path: polygon( - 50% 0%, - 80% 10%, - 100% 35%, - 100% 70%, - 80% 90%, - 50% 100%, - 20% 90%, - 0% 70%, - 0% 35%, - 20% 10% - ); - } - } - .dice-wrapper { - height: 24px; - width: 24px; - position: relative; - display: flex; - align-items: center; - justify-content: center; - - .dice { - height: 26px; - width: 26px; - max-width: unset; - position: absolute; - } - } - - .dice-value { - position: absolute; - font-weight: bold; - font-size: 16px; - } - - &.hope { - .dice-wrapper { - background: black; - - .dice { - filter: brightness(0) saturate(100%) invert(79%) sepia(79%) saturate(333%) - hue-rotate(352deg) brightness(102%) contrast(103%); - } - } - - .dice-value { - color: var(--color-dark-1); - text-shadow: 0 0 4px white; - } - } - - &.fear { - .dice-wrapper { - background: white; - - .dice { - filter: brightness(0) saturate(100%) invert(12%) sepia(88%) saturate(4321%) - hue-rotate(221deg) brightness(92%) contrast(110%); - } - } - - .dice-value { - color: var(--color-light-1); - text-shadow: 0 0 4px black; - } - } - - &.advantage { - .dice-wrapper { - .dice { - filter: brightness(0) saturate(100%) invert(18%) sepia(92%) saturate(4133%) - hue-rotate(96deg) brightness(104%) contrast(107%); - } - } - } - - &.disadvantage { - .dice-wrapper { - .dice { - filter: brightness(0) saturate(100%) invert(9%) sepia(78%) saturate(6903%) - hue-rotate(11deg) brightness(93%) contrast(117%); - } - } - } - } - } - } - } - - .damage-resource { - font-weight: 600; - margin-top: 5px; - } - } - - .dice-total { - &.duality { - &.hope { - border-color: @hope; - border-width: 3px; - background: rgba(@hope, 0.5); - } - &.fear { - border-color: @fear; - border-width: 3px; - background: rgba(@fear, 0.5); - } - &.critical { - border-color: @critical; - border-width: 3px; - background: rgba(@critical, 0.5); - } - } - - .dice-total-value { - .hope { - color: @hope; - } - .fear { - color: @fear; - } - .critical { - color: @critical; - } - } - } - - .dice-total-label { - font-size: 12px; - font-weight: bold; - font-variant: all-small-caps; - margin: -@fullMargin 0; - } - - .target-selection { - display: flex; - justify-content: space-around; - input[type='radio'] { - display: none; - &:checked + label { - text-shadow: 0px 0px 4px #ce5937; - } - &:not(:checked) + label { - opacity: 0.75; - } - } - label { - cursor: pointer; - opacity: 0.75; - &.target-selected { - text-shadow: 0px 0px 4px #ce5937; - opacity: 1; - } - } - } - - .target-section { - margin-top: 5px; - - .target-container { - display: flex; - transition: all 0.2s ease-in-out; - - &:hover { - filter: drop-shadow(0 0 3px @secondaryShadow); - border-color: gold; - } - - &.hidden { - display: none; - border: 0; - } - - &.hit { - background: @hit; - } - - &.miss { - background: @miss; - } - - img, - .target-save-container { - width: 22px; - height: 22px; - align-self: center; - border-color: transparent; - } - - img { - flex: 0; - margin-left: 8px; - } - - .target-save-container { - margin-right: 8px; - justify-content: center; - display: flex; - align-items: center; - min-height: unset; - border: 1px solid black; - } - - .target-inner-container { - flex: 1; - display: flex; - justify-content: center; - font-size: var(--font-size-16); - } - - &:not(:has(.target-save-container)) .target-inner-container { - margin-right: @hugeMargin; - } - } - } - - .dice-actions { - display: flex; - gap: 4px; - - button { - flex: 1; - height: 40px; - font-family: @font-body; - font-weight: 600; - } - } - - .dice-result { - .roll-damage-button, - .damage-button, - .duality-action { - margin-top: 5px; - } - } - - &:not(.expanded) .dice-tooltip { - grid-template-rows: 0fr; - } - } */ - button { &.inner-button { --button-size: 1.25rem; @@ -349,19 +22,6 @@ } } } - - [data-use-perm='false'] { - pointer-events: none; - border-color: transparent; - } - [data-view-perm='false'] { - > * { - display: none; - } - &::after { - content: '??'; - } - } } .daggerheart, @@ -370,6 +30,19 @@ --text-color: light-dark(@dark-blue, @golden); --bg-color: light-dark(@dark-blue-40, @golden-40); + [data-use-perm='false'] { + pointer-events: none; + border-color: transparent; + } + [data-view-perm='false'] { + &[data-perm-hidden='true'], > * { + display: none; + } + &::after { + content: '??'; + } + } + &.duality { &.hope { --text-color: @golden; @@ -412,7 +85,7 @@ grid-template-columns: 1fr auto 1fr; align-items: center; color: light-dark(@dark, @beige); - margin: 5px 0; + margin: 10px 0; span { display: flex; @@ -450,7 +123,6 @@ flex-direction: column; align-items: center; gap: 5px; - padding: 5px 0; .dice-tooltip { width: 100%; @@ -489,6 +161,7 @@ color: var(--text-color); font-weight: 700; font-family: 'Cinzel', sans-serif; + line-height: .75; .roll-result-value { font-size: var(--font-size-24); @@ -503,10 +176,6 @@ } } } - - .roll-difficulty { - margin-top: -5px; - } } } @@ -580,7 +249,7 @@ .button-target-selection { flex: 1; text-align: center; - padding: 5px 0; + margin: -5px 0; } .button-target-selection:hover, @@ -600,6 +269,11 @@ width: 100%; gap: 10px; align-items: center; + border-radius: 3px; + + &:hover { + background-color: rgba(255,255,255,.1); + } .target-img { border-radius: 50%; @@ -640,6 +314,7 @@ padding: 3px 5px; width: fit-content; margin: auto; + white-space: nowrap; } .roll-difficulty, @@ -706,6 +381,19 @@ margin-top: 0; } + .damage-section[data-action='expandRoll'] { + .on-reduced { + .wrapper { + flex-wrap: wrap; + gap: 10px 5px; + } + + .roll-formula { + font-size: var(--font-size-16); + } + } + } + .target-section { .roll-part-content { gap: 10px; diff --git a/templates/dialogs/dice-roll/costSelection.hbs b/templates/dialogs/dice-roll/costSelection.hbs index 765fbaf9..84bc3399 100644 --- a/templates/dialogs/dice-roll/costSelection.hbs +++ b/templates/dialogs/dice-roll/costSelection.hbs @@ -5,25 +5,25 @@
  • - - + +
    - +
  • {{/if}} {{#each costs as | cost index |}}
  • - - + +
    {{#if (and scalable maxStep)}} {{/if}} - +
  • {{/each}} diff --git a/templates/ui/chat/chat-message.hbs b/templates/ui/chat/chat-message.hbs index 85cf03e2..94527f16 100644 --- a/templates/ui/chat/chat-message.hbs +++ b/templates/ui/chat/chat-message.hbs @@ -3,17 +3,19 @@
    - {{#if (eq message.type 'base')}} -
    -

    {{actor.name}}

    -
    {{author.name}}
    -
    - {{else}} -
    -

    {{ifThen message.title message.title alias}}

    -
    {{actor.name}} {{#if author.isGM}}(GM){{/if}}
    -
    - {{/if}} +
    + {{#unless actor.name}} +

    {{author.name}}

    + {{else}} + {{#if (eq message.type 'base')}} +

    {{actor.name}}

    +
    {{author.name}}
    + {{else}} +

    {{ifThen message.title message.title alias}}

    +
    {{actor.name}} {{#if author.isGM}}(GM){{/if}}
    + {{/if}} + {{/unless}} +
    {{{message.content}}} + {{#if message.flavor}} + {{{message.flavor}}} + {{/if}}
    \ No newline at end of file diff --git a/templates/ui/chat/parts/button-part.hbs b/templates/ui/chat/parts/button-part.hbs index f28f1a7b..f83972f7 100644 --- a/templates/ui/chat/parts/button-part.hbs +++ b/templates/ui/chat/parts/button-part.hbs @@ -1,17 +1,17 @@
    {{#if hasDamage}} {{#unless (empty damage)}} - + {{#if canButtonApply}}{{/if}} {{else}} {{/unless}} {{/if}} {{#if hasHealing}} {{#unless (empty damage)}} - + {{#if canButtonApply}}{{/if}} {{else}} {{/unless}} {{/if}} - {{#if hasEffect}}{{/if}} + {{#if (and hasEffect canButtonApply)}}{{/if}}
    \ No newline at end of file diff --git a/templates/ui/chat/parts/roll-part.hbs b/templates/ui/chat/parts/roll-part.hbs index d4e46b1f..1d7f3d2b 100644 --- a/templates/ui/chat/parts/roll-part.hbs +++ b/templates/ui/chat/parts/roll-part.hbs @@ -14,14 +14,15 @@ {{#if roll.difficulty}} - {{#if canViewSecret}} + {{!-- {{#if canViewSecret}} --}} difficulty {{roll.difficulty}} - {{else}} + {{!-- {{else}} {{localize (ifThen roll.success "DAGGERHEART.GENERAL.success" "DAGGERHEART.GENERAL.failure")}} - {{/if}} + {{/if}} --}} {{/if}} + {{#unless isPrivate}}
    {{localize "DAGGERHEART.GENERAL.formula"}}
    @@ -87,4 +88,5 @@
    + {{/unless}} \ No newline at end of file diff --git a/templates/ui/chat/parts/target-part.hbs b/templates/ui/chat/parts/target-part.hbs index 94810a03..51b9e79c 100644 --- a/templates/ui/chat/parts/target-part.hbs +++ b/templates/ui/chat/parts/target-part.hbs @@ -14,7 +14,7 @@
    - {{#if targets.length}} + {{#if (and parent.isAuthor targets.length)}}
    @@ -29,7 +29,7 @@
    -
    {{name}}
    +
    {{name}}
    {{#if (and ../targetSelection ../hasRoll)}}
    {{#if hit}} @@ -40,7 +40,7 @@
    {{/if}}
    - {{#if (and ../hasSave (or hit (not @root.targetSelection)))}} + {{#if (and ../hasSave (or hit (not @root.hasRoll)))}}
    diff --git a/templates/ui/chat/roll.hbs b/templates/ui/chat/roll.hbs index 37e1aef2..2e6f5d24 100644 --- a/templates/ui/chat/roll.hbs +++ b/templates/ui/chat/roll.hbs @@ -5,4 +5,4 @@ {{#if hasTarget}}{{> 'systems/daggerheart/templates/ui/chat/parts/target-part.hbs'}}{{/if}}
    -{{> 'systems/daggerheart/templates/ui/chat/parts/button-part.hbs'}} \ No newline at end of file +{{#if (or parent.isAuthor canButtonApply)}}{{> 'systems/daggerheart/templates/ui/chat/parts/button-part.hbs'}}{{/if}} \ No newline at end of file From 45b3569cba3df236cd4a31d2cc4e9e8020ce590e Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Fri, 8 Aug 2025 23:31:01 +0200 Subject: [PATCH 06/13] Feature/666 experience hope cost (#728) * h * Character & Companion Experience Hope Cost --- module/applications/dialogs/d20RollDialog.mjs | 18 +++++++++++++-- .../applications/sheets/actors/character.mjs | 23 ++++++++++++++++++- module/data/action/baseAction.mjs | 9 ++++---- module/data/fields/action/costField.mjs | 12 ++++++++-- module/dice/dhRoll.mjs | 4 +++- templates/dialogs/dice-roll/costSelection.hbs | 6 ++++- .../partials/inventory-fieldset-items-V2.hbs | 2 +- 7 files changed, 61 insertions(+), 13 deletions(-) diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs index 4b8749a3..a06708e6 100644 --- a/module/applications/dialogs/d20RollDialog.mjs +++ b/module/applications/dialogs/d20RollDialog.mjs @@ -45,6 +45,10 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio return this.config.title; } + get actor() { + return this.config?.data?.parent; + } + /** @override */ static PARTS = { header: { @@ -69,9 +73,10 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio icon })); + this.config.costs ??= []; if (this.config.costs?.length) { const updatedCosts = game.system.api.fields.ActionFields.CostField.calcCosts.call( - this.action, + this.action ?? { actor: this.actor }, this.config.costs ); context.costs = updatedCosts.map(x => ({ @@ -80,7 +85,10 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio ? this.action.parent.parent.name : game.i18n.localize(CONFIG.DH.GENERAL.abilityCosts[x.key].label) })); - context.canRoll = game.system.api.fields.ActionFields.CostField.hasCost.call(this.action, updatedCosts); + context.canRoll = game.system.api.fields.ActionFields.CostField.hasCost.call( + this.action ?? { actor: this.actor }, + updatedCosts + ); this.config.data.scale = this.config.costs[0].total; } if (this.config.uses?.max) { @@ -143,6 +151,12 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio this.config.experiences.indexOf(button.dataset.key) > -1 ? this.config.experiences.filter(x => x !== button.dataset.key) : [...this.config.experiences, button.dataset.key]; + if(this.config?.data?.parent?.type === 'character' || this.config?.data?.parent?.type === 'companion') { + this.config.costs = + this.config.costs.indexOf(this.config.costs.find(c => c.extKey === button.dataset.key)) > -1 + ? this.config.costs.filter(x => x.extKey !== button.dataset.key) + : [...this.config.costs, { extKey: button.dataset.key, key: 'hope', value: 1, name: this.config.data?.experiences?.[button.dataset.key]?.name }]; + } this.render(); } diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index 55662f90..e93de7ed 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -631,13 +631,34 @@ export default class CharacterSheet extends DHBaseActorSheet { }, hasRoll: true }; - this.document.diceRoll({ + const result = await this.document.diceRoll({ ...config, headerTitle: `${game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll')}: ${this.actor.name}`, title: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { ability: abilityLabel }) }); + + setTimeout(() => { + this.consumeResource(result?.costs); + }, 50); + } + + async consumeResource(costs) { + if(!costs?.length) return; + const usefulResources = foundry.utils.deepClone(this.actor.system.resources); + const resources = game.system.api.fields.ActionFields.CostField.getRealCosts(costs) + .map(c => { + const resource = usefulResources[c.key]; + return { + key: c.key, + value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1), + target: resource.target, + keyIsID: resource.keyIsID + }; + }); + + await this.actor.modifyResource(resources); } //TODO: redo toggleEquipItem method diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 516419b9..954e5328 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -221,12 +221,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel } } - const resources = config.costs + const resources = game.system.api.fields.ActionFields.CostField.getRealCosts(config.costs) .filter( c => - c.enabled !== false && - ((!successCost && (!c.consumeOnSuccess || config.roll?.success)) || - (successCost && c.consumeOnSuccess)) + (!successCost && (!c.consumeOnSuccess || config.roll?.success)) || + (successCost && c.consumeOnSuccess) ) .map(c => { const resource = usefulResources[c.key]; @@ -238,7 +237,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel }; }); - await this.actor.modifyResource(resources); + await (this.actor.system.partner ?? this.actor).modifyResource(resources); if ( config.uses?.enabled && ((!successCost && (!config.uses?.consumeOnSuccess || config.roll?.success)) || diff --git a/module/data/fields/action/costField.mjs b/module/data/fields/action/costField.mjs index b9495e8b..4972a9a0 100644 --- a/module/data/fields/action/costField.mjs +++ b/module/data/fields/action/costField.mjs @@ -47,6 +47,7 @@ export default class CostField extends fields.ArrayField { static hasCost(costs) { const realCosts = CostField.getRealCosts.call(this, costs), hasFearCost = realCosts.findIndex(c => c.key === 'fear'); + if (hasFearCost > -1) { const fearCost = realCosts.splice(hasFearCost, 1)[0]; if ( @@ -70,7 +71,8 @@ export default class CostField extends fields.ArrayField { } static getResources(costs) { - const actorResources = this.actor.system.resources; + const actorResources = foundry.utils.deepClone(this.actor.system.resources); + if(this.actor.system.partner) actorResources.hope = foundry.utils.deepClone(this.actor.system.partner.system.resources.hope); const itemResources = {}; for (let itemResource of costs) { if (itemResource.keyIsID) { @@ -89,7 +91,13 @@ export default class CostField extends fields.ArrayField { static getRealCosts(costs) { const realCosts = costs?.length ? costs.filter(c => c.enabled) : []; - return realCosts; + let mergedCosts = []; + realCosts.forEach(c => { + const getCost = Object.values(mergedCosts).find(gc => gc.key === c.key); + if(getCost) getCost.total += c.total; + else mergedCosts.push(c); + }); + return mergedCosts; } static formatMax(max) { diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index 710a2728..889b24b3 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -236,7 +236,9 @@ export const registerRollDiceHooks = () => { if (updates.length) { const target = actor.system.partner ?? actor; if (!['dead', 'unconcious'].some(x => actor.statuses.has(x))) { - target.modifyResource(updates); + setTimeout(() => { + target.modifyResource(updates); + }, 50); } } diff --git a/templates/dialogs/dice-roll/costSelection.hbs b/templates/dialogs/dice-roll/costSelection.hbs index 84bc3399..775e681c 100644 --- a/templates/dialogs/dice-roll/costSelection.hbs +++ b/templates/dialogs/dice-roll/costSelection.hbs @@ -17,7 +17,11 @@
    - +
    {{#if (and scalable maxStep)}} diff --git a/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs b/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs index 8fbd5800..1ef065d5 100644 --- a/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs +++ b/templates/sheets/global/partials/inventory-fieldset-items-V2.hbs @@ -26,7 +26,7 @@ Parameters: {{localize title}} {{#if canCreate}} - Date: Fri, 8 Aug 2025 18:32:32 -0300 Subject: [PATCH 07/13] [BUG] - Sending an active effect to chat doesnt work (#727) Fixes #616 Co-authored-by: Joaquin Pereyra --- lang/en.json | 5 ---- .../sheets/api/application-mixin.mjs | 24 ++++++++++++------- module/applications/sheets/api/base-actor.mjs | 4 +--- module/applications/sheets/api/base-item.mjs | 4 +--- module/data/chat-message/abilityUse.mjs | 1 - module/documents/activeEffect.mjs | 15 ++++++++---- module/documents/item.mjs | 14 ++++------- 7 files changed, 33 insertions(+), 34 deletions(-) diff --git a/lang/en.json b/lang/en.json index d03ce369..fd278e89 100755 --- a/lang/en.json +++ b/lang/en.json @@ -2259,11 +2259,6 @@ "abilityCheckTitle": "{ability} Check" }, "featureTitle": "Class Feature", - "foundationCard": { - "ancestryTitle": "Ancestry Card", - "communityTitle": "Community Card", - "subclassFeatureTitle": "Subclass Feature" - }, "healingRoll": { "title": "Heal - {damage}", "heal": "Heal", diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index f8d2697f..7f646460 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -51,9 +51,8 @@ import { ItemBrowser } from '../../ui/itemBrowser.mjs'; */ /** - * @template {Constructor} BaseDocumentSheet - * @param {BaseDocumentSheet} Base - The base class to extend. - * @returns {BaseDocumentSheet} + * @template {new (...args: any[]) => {}} T + * @arg Base {T} */ export default function DHApplicationMixin(Base) { class DHSheetV2 extends HandlebarsApplicationMixin(Base) { @@ -123,12 +122,13 @@ export default function DHApplicationMixin(Base) { super._attachPartListeners(partId, htmlElement, options); this._dragDrop.forEach(d => d.bind(htmlElement)); } + /**@inheritdoc */ async _onFirstRender(context, options) { await super._onFirstRender(context, options); const docs = []; - for (var docData of this.relatedDocs) { + for (const docData of this.relatedDocs) { const doc = await foundry.utils.fromUuid(docData.uuid); docs.push(doc); } @@ -247,6 +247,9 @@ export default function DHApplicationMixin(Base) { /* Context Menu */ /* -------------------------------------------- */ + /** + * Create all configured context menus for this application ins tance. + */ _createContextMenus() { for (const config of this.options.contextMenus) { const { handler, selector, options } = config; @@ -257,9 +260,9 @@ export default function DHApplicationMixin(Base) { /* -------------------------------------------- */ /** - * Get the set of ContextMenu options for DomainCards. + * Get the set of ContextMenu options for ActiveEffects. * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance - * @this {CharacterSheet} + * @this {DHSheetV2} * @protected */ static #getEffectContextOptions() { @@ -305,8 +308,13 @@ export default function DHApplicationMixin(Base) { } /** - * Get the set of ContextMenu options. - * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance + * Get the common ContextMenu options for an element. + * @param {Object} options + * @param {boolean} [options.usable=false] - Whether to include an option to use the item or apply damage. + * @param {boolean} [options.toChat=false] - Whether to include an option to send the item to chat. + * @param {boolean} [options.deletable=true] - Whether to include an option to delete the item. + * + * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} */ _getContextMenuCommonOptions({ usable = false, toChat = false, deletable = true }) { const options = [ diff --git a/module/applications/sheets/api/base-actor.mjs b/module/applications/sheets/api/base-actor.mjs index b664929c..67cec44f 100644 --- a/module/applications/sheets/api/base-actor.mjs +++ b/module/applications/sheets/api/base-actor.mjs @@ -7,8 +7,6 @@ const { ActorSheetV2 } = foundry.applications.sheets; /** * A base actor sheet extending {@link ActorSheetV2} via {@link DHApplicationMixin} - * @extends ActorSheetV2 - * @mixes DHSheetV2 */ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { /** @inheritDoc */ @@ -106,7 +104,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) { /** * Get the set of ContextMenu options for Features. * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance - * @this {DHSheetV2} + * @this {DHBaseActorSheet} * @protected */ static #getFeatureContextOptions() { diff --git a/module/applications/sheets/api/base-item.mjs b/module/applications/sheets/api/base-item.mjs index b5573a0c..6bd91ae8 100644 --- a/module/applications/sheets/api/base-item.mjs +++ b/module/applications/sheets/api/base-item.mjs @@ -7,8 +7,6 @@ const { ItemSheetV2 } = foundry.applications.sheets; /** * A base item sheet extending {@link ItemSheetV2} via {@link DHApplicationMixin} - * @extends ItemSheetV2 - * @mixes DHSheetV2 */ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { /** @inheritDoc */ @@ -108,7 +106,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { /** * Get the set of ContextMenu options for Features. * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance - * @this {DHSheetV2} + * @this {DHBaseItemSheet} * @protected */ static #getFeatureContextOptions() { diff --git a/module/data/chat-message/abilityUse.mjs b/module/data/chat-message/abilityUse.mjs index c431f14a..07209fe2 100644 --- a/module/data/chat-message/abilityUse.mjs +++ b/module/data/chat-message/abilityUse.mjs @@ -3,7 +3,6 @@ export default class DHAbilityUse extends foundry.abstract.TypeDataModel { const fields = foundry.data.fields; return { - title: new fields.StringField({}), origin: new fields.StringField({}), img: new fields.StringField({}), name: new fields.StringField({}), diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index 3aced0bf..bf535b78 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -124,15 +124,20 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { return tags; } + /** + * Create a new ChatMessage to display this document’s data. + * @param {String} origin - uuid of a document. TODO: This needs to be reviewed. + */ async toChat(origin) { + /**@type {foundry.documents.ChatMessage} */ const cls = getDocumentClass('ChatMessage'); - const actor = game.actors.get(cls.getSpeaker().actor); + const speaker = cls.getSpeaker(); + const actor = cls.getSpeakerActor(speaker); const systemData = { action: { img: this.img, name: this.name }, - actor: { name: actor.name, img: actor.img }, - author: this.author, - speaker: cls.getSpeaker(), - origin: origin, + actor: { name: actor?.name, img: actor?.img }, + speaker, + origin, description: this.description, actions: [] }; diff --git a/module/documents/item.mjs b/module/documents/item.mjs index a261677a..fb558e8c 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -142,19 +142,16 @@ export default class DHItem extends foundry.documents.Item { } } + /** + * Create a new ChatMessage to display this document’s data + * @param {String} origin - uuid of a document. TODO: This needs to be reviewed. + */ async toChat(origin) { + /**@type {foundry.documents.ChatMessage} */ const cls = getDocumentClass('ChatMessage'); const item = await foundry.utils.fromUuid(origin); const systemData = { - title: - this.type === 'ancestry' - ? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.ancestryTitle') - : this.type === 'community' - ? game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.communityTitle') - : this.type === 'feature' - ? game.i18n.localize('TYPES.Item.feature') - : game.i18n.localize('DAGGERHEART.UI.Chat.foundationCard.subclassFeatureTitle'), origin: origin, img: this.img, item: { @@ -170,7 +167,6 @@ export default class DHItem extends foundry.documents.Item { type: 'abilityUse', user: game.user.id, actor: item.parent, - author: this.author, speaker: cls.getSpeaker(), system: systemData, title: game.i18n.localize('DAGGERHEART.ACTIONS.Config.displayInChat'), From 3e83730c342f1981c9165cf24aadcdcbf897c49d Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Sat, 9 Aug 2025 21:39:19 +1000 Subject: [PATCH 08/13] Implementation of Dice So Nice settings preview #710 (#732) * Fix for #695 * Implements DiceSoNice preview in config settings #710 * Remove incorrect class * Fix Preview button position * Remove extra line * Fix formatting --------- Co-authored-by: Chris Ryan --- .../settings/appearanceSettings.mjs | 20 ++++++++++++++++++- module/config/generalConfig.mjs | 15 +++++++------- styles/less/global/elements.less | 10 ++++++++++ templates/settings/appearance-settings.hbs | 8 +++++++- 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/module/applications/settings/appearanceSettings.mjs b/module/applications/settings/appearanceSettings.mjs index 491c9799..f0310477 100644 --- a/module/applications/settings/appearanceSettings.mjs +++ b/module/applications/settings/appearanceSettings.mjs @@ -1,4 +1,5 @@ import DhAppearance from '../../data/settings/Appearance.mjs'; +import { getDiceSoNicePreset } from '../../config/generalConfig.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -25,7 +26,8 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App }, actions: { reset: this.reset, - save: this.save + save: this.save, + preview: this.preview }, form: { handler: this.updateData, submitOnChange: true } }; @@ -89,6 +91,22 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App this.render(); } + static async preview() { + const source = this.settings._source.diceSoNice[this.tabGroups.diceSoNice]; + let faces = 'd12'; + switch (this.tabGroups.diceSoNice) { + case 'advantage': + case 'disadvantage': + faces = 'd6'; + } + const preset = await getDiceSoNicePreset(source, faces); + const diceSoNiceRoll = await new Roll(`1${faces}`).evaluate(); + diceSoNiceRoll.dice[0].options.appearance = preset.appearance; + diceSoNiceRoll.dice[0].options.modelFile = preset.modelFile; + + await game.dice3d.showForRoll(diceSoNiceRoll, game.user, false); + } + static async reset() { this.settings = new DhAppearance(); this.render(); diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 7785b6a6..bd6ef44e 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -494,9 +494,7 @@ export const diceSetNumbers = { flat: 'Flat' }; -export const getDiceSoNicePresets = async (hopeFaces, fearFaces, advantageFaces = 'd6', disadvantageFaces = 'd6') => { - const { diceSoNice } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance); - const getPreset = async (type, faces) => { +export const getDiceSoNicePreset = async (type, faces) => { const system = game.dice3d.DiceFactory.systems.get(type.system).dice.get(faces); if (!system) { ui.notifications.error( @@ -523,11 +521,14 @@ export const getDiceSoNicePresets = async (hopeFaces, fearFaces, advantageFaces }; }; +export const getDiceSoNicePresets = async (hopeFaces, fearFaces, advantageFaces = 'd6', disadvantageFaces = 'd6') => { + const { diceSoNice } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance); + return { - hope: await getPreset(diceSoNice.hope, hopeFaces), - fear: await getPreset(diceSoNice.fear, fearFaces), - advantage: await getPreset(diceSoNice.advantage, advantageFaces), - disadvantage: await getPreset(diceSoNice.disadvantage, disadvantageFaces) + hope: await getDiceSoNicePreset(diceSoNice.hope, hopeFaces), + fear: await getDiceSoNicePreset(diceSoNice.fear, fearFaces), + advantage: await getDiceSoNicePreset(diceSoNice.advantage, advantageFaces), + disadvantage: await getDiceSoNicePreset(diceSoNice.disadvantage, disadvantageFaces) }; }; diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index 7f8cdd94..688df165 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -571,6 +571,16 @@ white-space: nowrap; } } + + .button-container { + display: grid; + grid-template-columns: 1fr; + gap: 10px; + text-align: center; + display: flex; + justify-content: center; + width: 100% + } } footer { diff --git a/templates/settings/appearance-settings.hbs b/templates/settings/appearance-settings.hbs index e7c1d757..ab30eefd 100644 --- a/templates/settings/appearance-settings.hbs +++ b/templates/settings/appearance-settings.hbs @@ -69,7 +69,13 @@ {{selectOptions diceSoNiceMaterials selected=diceTab.source.material valueAttr="key" labelAttr="name" localize=true}}
    -
    +
    + +
    +
    {{/if}} From 7f3a83564b96a191a9deb563e93de1637c6a7655 Mon Sep 17 00:00:00 2001 From: Murilo Brito <91566541+moliloo@users.noreply.github.com> Date: Sat, 9 Aug 2025 08:39:56 -0300 Subject: [PATCH 09/13] remove very far field from variant rules range section (#730) --- templates/settings/variant-rules.hbs | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/settings/variant-rules.hbs b/templates/settings/variant-rules.hbs index 03027f69..5cc5c90c 100644 --- a/templates/settings/variant-rules.hbs +++ b/templates/settings/variant-rules.hbs @@ -11,7 +11,6 @@ {{formGroup settingFields.schema.fields.rangeMeasurement.fields.veryClose value=settingFields._source.rangeMeasurement.veryClose localize=true}} {{formGroup settingFields.schema.fields.rangeMeasurement.fields.close value=settingFields._source.rangeMeasurement.close localize=true}} {{formGroup settingFields.schema.fields.rangeMeasurement.fields.far value=settingFields._source.rangeMeasurement.far localize=true}} - {{formGroup settingFields.schema.fields.rangeMeasurement.fields.veryFar value=settingFields._source.rangeMeasurement.veryFar localize=true}}
    From 1c000c7cfe912c8b30dcdbc42d120eb8b564d137 Mon Sep 17 00:00:00 2001 From: Murilo Brito <91566541+moliloo@users.noreply.github.com> Date: Sat, 9 Aug 2025 08:40:20 -0300 Subject: [PATCH 10/13] fix item list columns and pretier itemBrowser.mjs (#729) --- module/applications/ui/itemBrowser.mjs | 82 +++++++++---------- styles/less/ui/item-browser/item-browser.less | 6 +- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/module/applications/ui/itemBrowser.mjs b/module/applications/ui/itemBrowser.mjs index 69de5249..3763bd36 100644 --- a/module/applications/ui/itemBrowser.mjs +++ b/module/applications/ui/itemBrowser.mjs @@ -17,7 +17,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { this.config = CONFIG.DH.ITEMBROWSER.compendiumConfig; this.presets = options.presets; - if(this.presets?.compendium && this.presets?.folder) + if (this.presets?.compendium && this.presets?.folder) ItemBrowser.selectFolder.call(this, null, null, this.presets.compendium, this.presets.folder); } @@ -26,7 +26,6 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { id: 'itemBrowser', classes: ['daggerheart', 'dh-style', 'dialog', 'compendium-browser'], tag: 'div', - // title: 'Item Browser', window: { frame: true, title: 'Compendium Browser', @@ -41,9 +40,8 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { sortList: this.sortList }, position: { - top: 330, - left: 120, - width: 800, + left: 100, + width: 850, height: 600 } }; @@ -88,16 +86,14 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { /** @inheritDoc */ async _preFirstRender(context, options) { - if(context.presets?.render?.noFolder || context.presets?.render?.lite) - options.position.width = 600; - + if (context.presets?.render?.noFolder || context.presets?.render?.lite) options.position.width = 600; + await super._preFirstRender(context, options); } /** @inheritDoc */ async _preRender(context, options) { - - if(context.presets?.render?.noFolder || context.presets?.render?.lite) + if (context.presets?.render?.noFolder || context.presets?.render?.lite) options.parts.splice(options.parts.indexOf('sidebar'), 1); await super._preRender(context, options); @@ -110,18 +106,17 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { this._createSearchFilter(); this._createFilterInputs(); this._createDragProcess(); - - if(context.presets?.render?.lite) - this.element.classList.add('lite'); - - if(context.presets?.render?.noFolder) - this.element.classList.add('no-folder'); - - if(context.presets?.render?.noFilter) - this.element.classList.add('no-filter'); - if(this.presets?.filter) { - Object.entries(this.presets.filter).forEach(([k,v]) => this.fieldFilter.find(c => c.name === k).value = v.value); + if (context.presets?.render?.lite) this.element.classList.add('lite'); + + if (context.presets?.render?.noFolder) this.element.classList.add('no-folder'); + + if (context.presets?.render?.noFilter) this.element.classList.add('no-filter'); + + if (this.presets?.filter) { + Object.entries(this.presets.filter).forEach( + ([k, v]) => (this.fieldFilter.find(c => c.name === k).value = v.value) + ); await this._onInputFilterBrowser(); } } @@ -198,6 +193,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { formatLabel(item, field) { const property = foundry.utils.getProperty(item, field.key); + if (Array.isArray(property)) property.join(', '); if (typeof field.format !== 'function') return property ?? '-'; return field.format(property); } @@ -315,19 +311,18 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { async _onInputFilterBrowser(event) { this.#filteredItems.browser.input.clear(); - if(event) this.fieldFilter.find(f => f.name === event.target.name).value = event.target.value; + if (event) this.fieldFilter.find(f => f.name === event.target.name).value = event.target.value; for (const li of this.element.querySelectorAll('.item-container')) { const itemUUID = li.dataset.itemUuid, item = this.items.find(i => i.uuid === itemUUID); - - if(!item) continue; + + if (!item) continue; const matchesMenu = this.fieldFilter.length === 0 || - this.fieldFilter.every(f => ( - !f.value && f.value !== false) || - ItemBrowser.evaluateFilter(item, this.createFilterData(f)) + this.fieldFilter.every( + f => (!f.value && f.value !== false) || ItemBrowser.evaluateFilter(item, this.createFilterData(f)) ); if (matchesMenu) this.#filteredItems.browser.input.add(item.id); @@ -335,21 +330,21 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { li.hidden = !(search.has(item.id) && matchesMenu); } } - + /** * Foundry evaluateFilter doesn't allow you to match if filter values are included into item data - * @param {*} obj - * @param {*} filter + * @param {*} obj + * @param {*} filter */ static evaluateFilter(obj, filter) { let docValue = foundry.utils.getProperty(obj, filter.field); let filterValue = filter.value; switch (filter.operator) { - case "contains2": + case 'contains2': filterValue = Array.isArray(filterValue) ? filterValue : [filterValue]; docValue = Array.isArray(docValue) ? docValue : [docValue]; return docValue.some(dv => filterValue.includes(dv)); - case "contains3": + case 'contains3': return docValue.some(f => f.value === filterValue); default: return foundry.applications.ux.SearchFilter.evaluateFilter(obj, filter); @@ -373,30 +368,33 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { this.render({ force: true }); } - static getFolderConfig(folder, property = "columns") { - if(!folder) return []; + static getFolderConfig(folder, property = 'columns') { + if (!folder) return []; return folder[property] ?? CONFIG.DH.ITEMBROWSER.typeConfig[folder.listType]?.[property] ?? []; } static sortList(_, target) { const key = target.dataset.sortKey, - type = !target.dataset.sortType || target.dataset.sortType === "DESC" ? "ASC" : "DESC", - itemListContainer = target.closest(".compendium-results").querySelector(".item-list"), - itemList = itemListContainer.querySelectorAll(".item-container"); + type = !target.dataset.sortType || target.dataset.sortType === 'DESC' ? 'ASC' : 'DESC', + itemListContainer = target.closest('.compendium-results').querySelector('.item-list'), + itemList = itemListContainer.querySelectorAll('.item-container'); - target.closest(".item-list-header").querySelectorAll('[data-sort-key]').forEach(b => b.dataset.sortType = ""); + target + .closest('.item-list-header') + .querySelectorAll('[data-sort-key]') + .forEach(b => (b.dataset.sortType = '')); target.dataset.sortType = type; - + const newOrder = [...itemList].reverse().sort((a, b) => { const aProp = a.querySelector(`[data-item-key="${key}"]`), - bProp = b.querySelector(`[data-item-key="${key}"]`) - if(type === "DESC") { + bProp = b.querySelector(`[data-item-key="${key}"]`); + if (type === 'DESC') { return aProp.innerText < bProp.innerText ? 1 : -1; } else { return aProp.innerText > bProp.innerText ? 1 : -1; } }); - + itemListContainer.replaceChildren(...newOrder); } diff --git a/styles/less/ui/item-browser/item-browser.less b/styles/less/ui/item-browser/item-browser.less index 3b13056b..e7ff3b12 100644 --- a/styles/less/ui/item-browser/item-browser.less +++ b/styles/less/ui/item-browser/item-browser.less @@ -227,14 +227,16 @@ display: flex; > * { - flex: 1; + flex: 2.5; + text-align: center; } .item-list-img { width: 40px; flex: unset; } .item-list-name { - flex-grow: 3 !important; + flex-grow: 3; + text-align: start; } } From 585601c13493ccda4a5e0e1b6189171c0f0c6cdc Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Sun, 10 Aug 2025 01:20:24 +0200 Subject: [PATCH 11/13] Feature/683 damage dialog options (#735) * Temp solution for specific weapon feature * Add Serrated & Self-Correcting * Remove comments --- module/applications/dialogs/damageDialog.mjs | 2 + .../sheets/api/application-mixin.mjs | 21 +--- module/config/itemConfig.mjs | 39 +----- module/data/actor/character.mjs | 12 -- module/dice/damageRoll.mjs | 111 +++++++++++++++++- module/dice/dhRoll.mjs | 6 + module/dice/dualityRoll.mjs | 2 +- module/helpers/utils.mjs | 20 ++++ ...n_Advanced_Longsword_9xkB3MWXahrsVP4N.json | 2 +- ...eapon_Advanced_Spear_pK6dsNABKKp1CIGN.json | 14 +-- ...n_Improved_Longsword_QyBZ5NxM8F9nCL9s.json | 2 +- ...eapon_Improved_Spear_j5Pt1thLfcvopBij.json | 14 +-- ..._Legendary_Longsword_14abPqQcROJfDChR.json | 2 +- ...apon_Legendary_Spear_4e5pWxi2qohuGsWh.json | 14 +-- .../weapon_Longsword_Iv8BZM1R24QMT72M.json | 2 +- .../weapon_Spear_TF85tKJetUjLwh54.json | 14 +-- styles/less/global/dialog.less | 2 +- .../dialogs/dice-roll/damageSelection.hbs | 16 +++ 18 files changed, 177 insertions(+), 118 deletions(-) diff --git a/module/applications/dialogs/damageDialog.mjs b/module/applications/dialogs/damageDialog.mjs index 2d372725..fbc584e4 100644 --- a/module/applications/dialogs/damageDialog.mjs +++ b/module/applications/dialogs/damageDialog.mjs @@ -56,12 +56,14 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application label, icon })); + context.modifiers = this.config.modifiers; return context; } static updateRollConfiguration(_event, _, formData) { const { ...rest } = foundry.utils.expandObject(formData.object); foundry.utils.mergeObject(this.config.roll, rest.roll); + foundry.utils.mergeObject(this.config.modifiers, rest.modifiers); this.config.selectedRollMode = rest.selectedRollMode; this.render(); diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 7f646460..f35ebb9f 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -333,7 +333,7 @@ export default function DHApplicationMixin(Base) { } ]; - if (usable) + if (usable) { options.unshift({ name: 'DAGGERHEART.GENERAL.damage', icon: 'fa-solid fa-explosion', @@ -353,24 +353,11 @@ export default function DHApplicationMixin(Base) { icon: 'fa-solid fa-burst', condition: target => { const doc = getDocFromElementSync(target); - return doc?.system?.attack?.damage.parts.length || doc?.damage?.parts.length; + return doc && !(doc.type === 'domainCard' && doc.system.inVault); }, - callback: async (target, event) => { - const doc = await getDocFromElement(target), - action = doc?.system?.attack ?? doc; - return action && action.use(event, { byPassRoll: true }); - } + callback: async (target, event) => (await getDocFromElement(target)).use(event) }); - - options.unshift({ - name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem', - icon: 'fa-solid fa-burst', - condition: target => { - const doc = getDocFromElementSync(target); - return doc && !(doc.type === 'domainCard' && doc.system.inVault); - }, - callback: async (target, event) => (await getDocFromElement(target)).use(event) - }); + } if (toChat) options.push({ diff --git a/module/config/itemConfig.mjs b/module/config/itemConfig.mjs index e9d8de4c..296dc927 100644 --- a/module/config/itemConfig.mjs +++ b/module/config/itemConfig.mjs @@ -1040,16 +1040,6 @@ export const weaponFeatures = { key: 'system.evasion', mode: 2, value: '-1' - }, - { - key: 'system.bonuses.damage.primaryWeapon.extraDice', - mode: 2, - value: '1' - }, - { - key: 'system.rules.weapon.dropLowestDamageDice', - mode: 5, - value: '1' } ] } @@ -1166,18 +1156,7 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.powerful.effects.powerful.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.powerful.effects.powerful.description', img: 'icons/magic/control/buff-flight-wings-runes-red-yellow.webp', - changes: [ - { - key: 'system.bonuses.damage.primaryWeapon.extraDice', - mode: 2, - value: '1' - }, - { - key: 'system.rules.weapon.dropLowestDamageDice', - mode: 5, - value: '1' - } - ] + changes: [] } ] }, @@ -1301,13 +1280,7 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.selfCorrecting.effects.selfCorrecting.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.selfCorrecting.effects.selfCorrecting.description', img: 'icons/weapons/ammunition/arrow-broadhead-glowing-orange.webp', - changes: [ - { - key: 'system.rules.damage.flipMinDiceValue', - mode: 5, - value: 1 - } - ] + changes: [] } ] }, @@ -1319,13 +1292,7 @@ export const weaponFeatures = { name: 'DAGGERHEART.CONFIG.WeaponFeature.serrated.effects.serrated.name', description: 'DAGGERHEART.CONFIG.WeaponFeature.serrated.effects.serrated.description', img: 'icons/weapons/ammunition/arrow-broadhead-glowing-orange.webp', - changes: [ - { - key: 'system.rules.damage.flipMinDiceValue', - mode: 5, - value: 1 - } - ] + changes: [] } ] }, diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index d4544d2c..c40e7e5d 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -287,18 +287,6 @@ export default class DhCharacter extends BaseDataActor { }) }) }), - weapon: new fields.SchemaField({ - /* Unimplemented - -> Should remove the lowest damage dice from weapon damage - -> Reflect this in the chat message somehow so players get feedback that their choice is helping them. - */ - dropLowestDamageDice: new fields.BooleanField({ initial: false }), - /* Unimplemented - -> Should flip any lowest possible dice rolls for weapon damage to highest - -> Reflect this in the chat message somehow so players get feedback that their choice is helping them. - */ - flipMinDiceValue: new fields.BooleanField({ intial: false }) - }), runeWard: new fields.BooleanField({ initial: false }), burden: new fields.SchemaField({ ignore: new fields.BooleanField() diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index 31458516..34973108 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -102,14 +102,14 @@ export default class DamageRoll extends DHRoll { } constructFormula(config) { - this.options.roll.forEach(part => { + this.options.roll.forEach((part, index) => { part.roll = new Roll(Roll.replaceFormulaData(part.formula, config.data)); - this.constructFormulaPart(config, part); + this.constructFormulaPart(config, part, index); }); return this.options.roll; } - constructFormulaPart(config, part) { + constructFormulaPart(config, part, index) { part.roll.terms = Roll.parse(part.roll.formula, config.data); if (part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) { @@ -120,6 +120,15 @@ export default class DamageRoll extends DHRoll { }); } + /* To Remove When Reaction System */ + if(index === 0 && part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) { + for(const mod in config.modifiers) { + const modifier = config.modifiers[mod]; + if(modifier.beforeCrit === true && (modifier.enabled || modifier.value)) + modifier.callback(part); + } + } + if (part.extraFormula) { part.roll.terms.push( new foundry.dice.terms.OperatorTerm({ operator: '+' }), @@ -132,6 +141,102 @@ export default class DamageRoll extends DHRoll { criticalBonus = tmpRoll.total - this.constructor.calculateTotalModifiers(tmpRoll); part.roll.terms.push(...this.formatModifier(criticalBonus)); } + + /* To Remove When Reaction System */ + if(index === 0 && part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) { + for(const mod in config.modifiers) { + const modifier = config.modifiers[mod]; + if(!modifier.beforeCrit && (modifier.enabled || modifier.value)) + modifier.callback(part); + } + } + return (part.roll._formula = this.constructor.getFormula(part.roll.terms)); } + + /* To Remove When Reaction System */ + static temporaryModifierBuilder(config) { + const mods = {}; + if(config.data?.parent) { + if(config.data.parent.appliedEffects) { + // Bardic Rally + mods.rally = { + label: "DAGGERHEART.CLASS.Feature.rallyDice", + values: config.data?.parent?.appliedEffects.reduce((a, c) => { + const change = c.changes.find(ch => ch.key === 'system.bonuses.rally'); + if (change) a.push({ value: c.id, label: change.value }); + return a; + }, []), + value: null, + beforeCrit: true, + callback: (part) => { + const rallyFaces = config.modifiers.rally.values.find(r => r.value === config.modifiers.rally.value)?.label; + part.roll.terms.push( + new foundry.dice.terms.OperatorTerm({ operator: '+' }), + ...this.parse(`1${rallyFaces}`) + ); + } + }; + } + + const item = config.data.parent.items?.get(config.source.item); + if(item) { + // Massive (Weapon Feature) + if(item.system.itemFeatures.find(f => f.value === "massive")) + mods.massive = { + label: CONFIG.DH.ITEM.weaponFeatures.massive.label, + enabled: true, + callback: (part) => { + part.roll.terms[0].modifiers.push(`kh${part.roll.terms[0].number}`); + part.roll.terms[0].number += 1; + } + }; + + // Powerful (Weapon Feature) + if(item.system.itemFeatures.find(f => f.value === "powerful")) + mods.powerful = { + label: CONFIG.DH.ITEM.weaponFeatures.powerful.label, + enabled: true, + callback: (part) => { + part.roll.terms[0].modifiers.push(`kh${part.roll.terms[0].number}`); + part.roll.terms[0].number += 1; + } + }; + + // Brutal (Weapon Feature) + if(item.system.itemFeatures.find(f => f.value === "brutal")) + mods.brutal = { + label: CONFIG.DH.ITEM.weaponFeatures.brutal.label, + enabled: true, + beforeCrit: true, + callback: (part) => { + part.roll.terms[0].modifiers.push(`x${part.roll.terms[0].faces}`); + } + }; + + // Serrated (Weapon Feature) + if(item.system.itemFeatures.find(f => f.value === "serrated")) + mods.serrated = { + label: CONFIG.DH.ITEM.weaponFeatures.serrated.label, + enabled: true, + callback: (part) => { + part.roll.terms[0].modifiers.push(`sc8`); + } + }; + + // Self-Correcting (Weapon Feature) + if(item.system.itemFeatures.find(f => f.value === "selfCorrecting")) + mods.selfCorrecting = { + label: CONFIG.DH.ITEM.weaponFeatures.selfCorrecting.label, + enabled: true, + callback: (part) => { + part.roll.terms[0].modifiers.push(`sc6`); + } + }; + } + } + + config.modifiers = mods; + return mods; + } } diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index 889b24b3..a785e508 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -36,6 +36,8 @@ export default class DHRoll extends Roll { this.applyKeybindings(config); + this.temporaryModifierBuilder(config); + let roll = new this(config.roll.formula, config.data, config); if (config.dialog.configure !== false) { // Open Roll Dialog @@ -207,6 +209,10 @@ export default class DHRoll extends Roll { } return modifierTotal; } + + static temporaryModifierBuilder(config) { + return {}; + } } export const registerRollDiceHooks = () => { diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index 030b4df2..35bae725 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -149,7 +149,7 @@ export default class DualityRoll extends D20Roll { } if (this.rallyFaces) this.terms.push( - new foundry.dice.terms.OperatorTerm({ operator: '+' }), + new foundry.dice.terms.OperatorTerm({ operator: this.hasDisadvantage ? '-' : '+' }), new foundry.dice.terms.Die({ faces: this.rallyFaces }) ); } diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 7b588fc7..f1483e31 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -172,6 +172,26 @@ Roll.replaceFormulaData = function (formula, data = {}, { missing, warn = false return nativeReplaceFormulaData(formula, data, { missing, warn }); }; +foundry.dice.terms.Die.MODIFIERS.sc = "selfCorrecting"; + +/** + * Return the configured value as result if 1 is rolled + * Example: 6d6sc6 Roll 6d6, each result of 1 will be changed into 6 + * @param {string} modifier The matched modifier query + */ +foundry.dice.terms.Die.prototype.selfCorrecting = function(modifier) { + const rgx = /(?:sc)([0-9]+)/i; + const match = modifier.match(rgx); + if ( !match ) return false; + let [target] = match.slice(1); + target = parseInt(target); + for ( const r of this.results ) { + if ( r.result === 1 ) { + r.result = target; + } + } +} + export const getDamageKey = damage => { return ['none', 'minor', 'major', 'severe'][damage]; }; diff --git a/src/packs/items/weapons/weapon_Advanced_Longsword_9xkB3MWXahrsVP4N.json b/src/packs/items/weapons/weapon_Advanced_Longsword_9xkB3MWXahrsVP4N.json index 79ce8416..8b44c759 100644 --- a/src/packs/items/weapons/weapon_Advanced_Longsword_9xkB3MWXahrsVP4N.json +++ b/src/packs/items/weapons/weapon_Advanced_Longsword_9xkB3MWXahrsVP4N.json @@ -43,7 +43,7 @@ "parts": [ { "value": { - "dice": "d8", + "dice": "d10", "bonus": 9, "multiplier": "prof", "flatMultiplier": 1, diff --git a/src/packs/items/weapons/weapon_Advanced_Spear_pK6dsNABKKp1CIGN.json b/src/packs/items/weapons/weapon_Advanced_Spear_pK6dsNABKKp1CIGN.json index 433533e3..0bfb4a8f 100644 --- a/src/packs/items/weapons/weapon_Advanced_Spear_pK6dsNABKKp1CIGN.json +++ b/src/packs/items/weapons/weapon_Advanced_Spear_pK6dsNABKKp1CIGN.json @@ -12,15 +12,7 @@ "equipped": false, "secondary": false, "burden": "twoHanded", - "weaponFeatures": [ - { - "value": "cumbersome", - "effectIds": [ - "hl0S2LrBY5Mg69q6" - ], - "actionIds": [] - } - ], + "weaponFeatures": [], "attack": { "name": "Attack", "img": "icons/skills/melee/blood-slash-foam-red.webp", @@ -51,8 +43,8 @@ "parts": [ { "value": { - "dice": "d10", - "bonus": 8, + "dice": "d8", + "bonus": 9, "multiplier": "prof", "flatMultiplier": 1, "custom": { diff --git a/src/packs/items/weapons/weapon_Improved_Longsword_QyBZ5NxM8F9nCL9s.json b/src/packs/items/weapons/weapon_Improved_Longsword_QyBZ5NxM8F9nCL9s.json index 867a563f..b064b1c2 100644 --- a/src/packs/items/weapons/weapon_Improved_Longsword_QyBZ5NxM8F9nCL9s.json +++ b/src/packs/items/weapons/weapon_Improved_Longsword_QyBZ5NxM8F9nCL9s.json @@ -43,7 +43,7 @@ "parts": [ { "value": { - "dice": "d8", + "dice": "d10", "bonus": 6, "multiplier": "prof", "flatMultiplier": 1, diff --git a/src/packs/items/weapons/weapon_Improved_Spear_j5Pt1thLfcvopBij.json b/src/packs/items/weapons/weapon_Improved_Spear_j5Pt1thLfcvopBij.json index beb295d6..367f80ad 100644 --- a/src/packs/items/weapons/weapon_Improved_Spear_j5Pt1thLfcvopBij.json +++ b/src/packs/items/weapons/weapon_Improved_Spear_j5Pt1thLfcvopBij.json @@ -12,15 +12,7 @@ "equipped": false, "secondary": false, "burden": "twoHanded", - "weaponFeatures": [ - { - "value": "cumbersome", - "effectIds": [ - "8twXPJELZpvFWA5K" - ], - "actionIds": [] - } - ], + "weaponFeatures": [], "attack": { "name": "Attack", "img": "icons/skills/melee/blood-slash-foam-red.webp", @@ -51,8 +43,8 @@ "parts": [ { "value": { - "dice": "d10", - "bonus": 5, + "dice": "d8", + "bonus": 6, "multiplier": "prof", "flatMultiplier": 1, "custom": { diff --git a/src/packs/items/weapons/weapon_Legendary_Longsword_14abPqQcROJfDChR.json b/src/packs/items/weapons/weapon_Legendary_Longsword_14abPqQcROJfDChR.json index 186cd1c1..636dd7a5 100644 --- a/src/packs/items/weapons/weapon_Legendary_Longsword_14abPqQcROJfDChR.json +++ b/src/packs/items/weapons/weapon_Legendary_Longsword_14abPqQcROJfDChR.json @@ -43,7 +43,7 @@ "parts": [ { "value": { - "dice": "d8", + "dice": "d10", "bonus": 12, "multiplier": "prof", "flatMultiplier": 1, diff --git a/src/packs/items/weapons/weapon_Legendary_Spear_4e5pWxi2qohuGsWh.json b/src/packs/items/weapons/weapon_Legendary_Spear_4e5pWxi2qohuGsWh.json index c1322de0..bfdcd4eb 100644 --- a/src/packs/items/weapons/weapon_Legendary_Spear_4e5pWxi2qohuGsWh.json +++ b/src/packs/items/weapons/weapon_Legendary_Spear_4e5pWxi2qohuGsWh.json @@ -12,15 +12,7 @@ "equipped": false, "secondary": false, "burden": "twoHanded", - "weaponFeatures": [ - { - "value": "cumbersome", - "effectIds": [ - "f44KWDgCQeKYfccr" - ], - "actionIds": [] - } - ], + "weaponFeatures": [], "attack": { "name": "Attack", "img": "icons/skills/melee/blood-slash-foam-red.webp", @@ -51,8 +43,8 @@ "parts": [ { "value": { - "dice": "d10", - "bonus": 11, + "dice": "d8", + "bonus": 12, "multiplier": "prof", "flatMultiplier": 1, "custom": { diff --git a/src/packs/items/weapons/weapon_Longsword_Iv8BZM1R24QMT72M.json b/src/packs/items/weapons/weapon_Longsword_Iv8BZM1R24QMT72M.json index 54d18b78..c5d9070b 100644 --- a/src/packs/items/weapons/weapon_Longsword_Iv8BZM1R24QMT72M.json +++ b/src/packs/items/weapons/weapon_Longsword_Iv8BZM1R24QMT72M.json @@ -43,7 +43,7 @@ "parts": [ { "value": { - "dice": "d8", + "dice": "d10", "bonus": 3, "multiplier": "prof", "flatMultiplier": 1, diff --git a/src/packs/items/weapons/weapon_Spear_TF85tKJetUjLwh54.json b/src/packs/items/weapons/weapon_Spear_TF85tKJetUjLwh54.json index 6aa8fe8c..77ba9a93 100644 --- a/src/packs/items/weapons/weapon_Spear_TF85tKJetUjLwh54.json +++ b/src/packs/items/weapons/weapon_Spear_TF85tKJetUjLwh54.json @@ -12,15 +12,7 @@ "equipped": false, "secondary": false, "burden": "twoHanded", - "weaponFeatures": [ - { - "value": "cumbersome", - "effectIds": [ - "Z5MnVI8EOOgzRdXC" - ], - "actionIds": [] - } - ], + "weaponFeatures": [], "attack": { "name": "Attack", "img": "icons/skills/melee/blood-slash-foam-red.webp", @@ -51,8 +43,8 @@ "parts": [ { "value": { - "dice": "d10", - "bonus": 2, + "dice": "d8", + "bonus": 3, "multiplier": "prof", "flatMultiplier": 1, "custom": { diff --git a/styles/less/global/dialog.less b/styles/less/global/dialog.less index aaa5c812..701d5025 100644 --- a/styles/less/global/dialog.less +++ b/styles/less/global/dialog.less @@ -53,7 +53,7 @@ font-weight: 500; font-size: 14px; line-height: 17px; - + white-space: nowrap; color: light-dark(@dark, @beige); } diff --git a/templates/dialogs/dice-roll/damageSelection.hbs b/templates/dialogs/dice-roll/damageSelection.hbs index 2967b675..be49906b 100644 --- a/templates/dialogs/dice-roll/damageSelection.hbs +++ b/templates/dialogs/dice-roll/damageSelection.hbs @@ -24,6 +24,22 @@
    {{/each}} + {{#if @root.modifiers}} +
    + {{localize "DAGGERHEART.GENERAL.Modifier.plural"}} + {{#each @root.modifiers}} + {{ localize label }} + {{#if (hasProperty this "values")}} + + {{/if}} + {{#if (hasProperty this "enabled")}} + + {{/if}} + {{/each}} +
    + {{/if}}
    {{#if directDamage}}