From 1f5c3531be2040f44a6a7363f3970f1259130173 Mon Sep 17 00:00:00 2001 From: CPTN_Cosmo Date: Fri, 8 Aug 2025 18:37:28 +0200 Subject: [PATCH 01/34] 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 02/34] 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 03/34] 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 04/34] 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 05/34] [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 06/34] 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 07/34] 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 08/34] 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 09/34] 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}} + + {{this.selectedResults}}/{{this.maxSelected}} Selected + + + + + {{/each}} +
    + {{/each}} + {{/each}} +
    \ No newline at end of file diff --git a/templates/dialogs/rerollDialog/footer.hbs b/templates/dialogs/rerollDialog/footer.hbs new file mode 100644 index 00000000..4aff2823 --- /dev/null +++ b/templates/dialogs/rerollDialog/footer.hbs @@ -0,0 +1,4 @@ +
    + + +
    \ No newline at end of file diff --git a/templates/dialogs/rerollDialog/main.hbs b/templates/dialogs/rerollDialog/main.hbs new file mode 100644 index 00000000..6f10ce33 --- /dev/null +++ b/templates/dialogs/rerollDialog/main.hbs @@ -0,0 +1,35 @@ +
    + {{#each damage}} +

    {{localize (concat 'DAGGERHEART.CONFIG.HealingType.' @key '.name')}}

    + {{#each this}} +
    + {{#each this}} +
    + + + + {{this.selectedResults}}/{{this.results.length}} Selected + + +
    + {{#each this.results}} +
    + {{this.result}} + {{#if this.active}} + + + + + {{/if}} +
    + {{/each}} +
    +
    + {{/each}} +
    + {{/each}} + {{/each}} +
    \ No newline at end of file diff --git a/templates/ui/chat/parts/damage-part.hbs b/templates/ui/chat/parts/damage-part.hbs index 7ec8bfd8..232b1303 100644 --- a/templates/ui/chat/parts/damage-part.hbs +++ b/templates/ui/chat/parts/damage-part.hbs @@ -29,7 +29,13 @@ {{#each results}} {{#unless discarded}}
    -
    {{result}}
    +
    + {{#if hasRerolls}}{{/if}} + {{result}} +
    {{/unless}} {{/each}} From d6316f30ba0efb574cb332e02d3a98e5d9438db7 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 10 Aug 2025 02:06:51 +0200 Subject: [PATCH 13/34] Updated the background image for the system --- assets/logos/FoundrybornBackgroundLogo.png | Bin 0 -> 55426 bytes system.json | 1 + 2 files changed, 1 insertion(+) create mode 100644 assets/logos/FoundrybornBackgroundLogo.png diff --git a/assets/logos/FoundrybornBackgroundLogo.png b/assets/logos/FoundrybornBackgroundLogo.png new file mode 100644 index 0000000000000000000000000000000000000000..9639b92fc563f053210bdca293ac4f1f852d4bda GIT binary patch literal 55426 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

    FxUNVIlbKxn+p5fHFaOLC``hR@`qk1DTxhn*D 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 NnrgbL#VXf={~v>26P*A6 literal 0 HcmV?d00001 diff --git a/system.json b/system.json index 7501cfa7..cde902c1 100644 --- a/system.json +++ b/system.json @@ -262,6 +262,7 @@ "abilityUse": {} } }, + "background": "systems/daggerheart/assets/logos/FoundrybornBackgroundLogo.png", "primaryTokenAttribute": "resources.hitPoints", "secondaryTokenAttribute": "resources.stress", "url": "https://your/hosted/system/repo/", From 589190849359bb0cbebcf3be690d6473b80a3e21 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 10 Aug 2025 02:26:28 +0200 Subject: [PATCH 14/34] Fixed so Weapon/Armor features are added again --- module/helpers/utils.mjs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 6124cfbe..3dc1f7ed 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -85,7 +85,13 @@ export const chunkify = (array, chunkSize, mappingFunc) => { export const tagifyElement = (element, baseOptions, onChange, tagifyOptions = {}) => { const { maxTags } = tagifyOptions; - const options = typeof baseOptions === 'object' ? Object.values(baseOptions) : baseOptions; + const options = + typeof baseOptions === 'object' + ? Object.keys(baseOptions).map(optionKey => ({ + ...baseOptions[optionKey], + id: optionKey + })) + : baseOptions; const tagifyElement = new Tagify(element, { tagTextProp: 'name', From 4d2c72fb849aea344f512fc6ea88df9216cd46df Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 10 Aug 2025 07:58:19 +0200 Subject: [PATCH 15/34] Fixed so fear is available as a resource to be deducted by actions (#757) --- module/data/action/baseAction.mjs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index ab515e88..a886bf49 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -208,7 +208,14 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel } async consume(config, successCost = false) { - const usefulResources = foundry.utils.deepClone(this.actor.system.resources); + const usefulResources = { + ...foundry.utils.deepClone(this.actor.system.resources), + fear: { + value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), + max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear, + reversed: false + } + }; for (var cost of config.costs) { if (cost.keyIsID) { From f8d9257cd01fd41b56f5f0b82b624bdb64f0bc62 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 10 Aug 2025 10:05:00 +0200 Subject: [PATCH 16/34] Changed to use the config labels and src --- README.md | 2 +- .../characterCreation/characterCreation.mjs | 5 +++++ module/data/item/class.mjs | 11 +++++++++++ module/data/item/domainCard.mjs | 9 +++++++-- styles/less/sheets/actors/character/header.less | 4 ++-- templates/characterCreation/setupTabs/domainCards.hbs | 4 +--- templates/sheets/actors/character/header.hbs | 4 ++-- 7 files changed, 29 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c40a27a3..fda78b4b 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ This is the community repo for the Foundry VTT system _Foundryborne_ Daggerheart ## User Install -1. **recommended** Searching for _Daggerheart_ or _Foundryborne_ in the System Instalaltion dialgoe of the FoundryVTT admin settings. +1. **Recommended** Searching for _Daggerheart_ or _Foundryborne_ in the System Installation dialogue of the FoundryVTT admin settings. 2. Pasting `https://raw.githubusercontent.com/Foundryborne/daggerheart/refs/heads/main/system.json` into the Install System dialog on the Setup menu of the application. 3. Downloading one of the .zip archives from the Releases page and extracting it into your foundry Data folder, under Data/systems/daggerheart. diff --git a/module/applications/characterCreation/characterCreation.mjs b/module/applications/characterCreation/characterCreation.mjs index d45fe54e..c160b2fd 100644 --- a/module/applications/characterCreation/characterCreation.mjs +++ b/module/applications/characterCreation/characterCreation.mjs @@ -359,6 +359,11 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl context.community = { ...this.setup.community, compendium: 'communities' }; context.class = { ...this.setup.class, compendium: 'classes' }; context.subclass = { ...this.setup.subclass, compendium: 'subclasses' }; + + const allDomainData = CONFIG.DH.DOMAIN.allDomains(); + context.classDomains = context.class.uuid + ? context.class.system.domains.map(key => game.i18n.localize(allDomainData[key].label)) + : []; context.domainCards = Object.keys(this.setup.domainCards).reduce((acc, x) => { acc[x] = { ...this.setup.domainCards[x], compendium: 'domains' }; return acc; diff --git a/module/data/item/class.mjs b/module/data/item/class.mjs index 45e8b4ab..cee17613 100644 --- a/module/data/item/class.mjs +++ b/module/data/item/class.mjs @@ -60,6 +60,17 @@ export default class DHClass extends BaseDataItem { /* -------------------------------------------- */ + get domainData() { + const allDomainData = CONFIG.DH.DOMAIN.allDomains(); + return this.domains.map(key => { + const domain = allDomainData[key]; + return { + ...domain, + label: game.i18n.localize(domain.label) + }; + }); + } + get hopeFeatures() { return this.features.filter(x => x.type === CONFIG.DH.ITEM.featureSubTypes.hope).map(x => x.item); } diff --git a/module/data/item/domainCard.mjs b/module/data/item/domainCard.mjs index 34754a41..7705adb1 100644 --- a/module/data/item/domainCard.mjs +++ b/module/data/item/domainCard.mjs @@ -33,6 +33,11 @@ export default class DHDomainCard extends BaseDataItem { }; } + get domainLabel() { + const allDomainData = CONFIG.DH.DOMAIN.allDomains(); + return game.i18n.localize(allDomainData[this.domain].label); + } + /* -------------------------------------------- */ /**@override */ @@ -71,7 +76,7 @@ export default class DHDomainCard extends BaseDataItem { _getTags() { const tags = [ game.i18n.localize(`DAGGERHEART.CONFIG.DomainCardTypes.${this.type}`), - game.i18n.localize(`DAGGERHEART.GENERAL.Domain.${this.domain}.label`), + this.domainLabel, `${game.i18n.localize('DAGGERHEART.ITEMS.DomainCard.recallCost')}: ${this.recallCost}` ]; @@ -85,7 +90,7 @@ export default class DHDomainCard extends BaseDataItem { _getLabels() { const labels = [ game.i18n.localize(`DAGGERHEART.CONFIG.DomainCardTypes.${this.type}`), - game.i18n.localize(`DAGGERHEART.GENERAL.Domain.${this.domain}.label`), + this.domainLabel, { value: `${this.recallCost}`, //converts the number to a string icons: ['fa-bolt'] diff --git a/styles/less/sheets/actors/character/header.less b/styles/less/sheets/actors/character/header.less index 5de7a827..7bfcc7c9 100644 --- a/styles/less/sheets/actors/character/header.less +++ b/styles/less/sheets/actors/character/header.less @@ -10,7 +10,7 @@ } .character-row .domains-section img { - filter: invert(88%) sepia(98%) saturate(1784%) hue-rotate(311deg) brightness(104%) contrast(91%); + filter: @golden-filter; } } }, { @@ -20,7 +20,7 @@ } .character-row .domains-section img { - filter: invert(87%) sepia(15%) saturate(343%) hue-rotate(333deg) brightness(110%) contrast(87%); + filter: brightness(0) saturate(100%); } } }); diff --git a/templates/characterCreation/setupTabs/domainCards.hbs b/templates/characterCreation/setupTabs/domainCards.hbs index 298d8859..66526689 100644 --- a/templates/characterCreation/setupTabs/domainCards.hbs +++ b/templates/characterCreation/setupTabs/domainCards.hbs @@ -10,9 +10,7 @@ {{#each domainCards as |domainCard id|}}

    {{#> "systems/daggerheart/templates/components/card-preview.hbs" domainCard }} - {{#each @root.class.system.domains }} -
    {{localize (concat "DAGGERHEART.GENERAL.Domain." this ".label")}}
    - {{/each}} + {{#each @root.classDomains }}
    {{this}}
    {{/each}} {{/"systems/daggerheart/templates/components/card-preview.hbs"}}
    {{/each}} diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index b6f51bcf..7f598f55 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -87,9 +87,9 @@ {{#if document.system.class.value}}
    - {{#each document.system.class.value.system.domains as |domain|}} + {{#each document.system.class.value.system.domainData as |data|}}
    - +
    {{/each}}
    From ea9e787382bdb4ced2757fd3f0f2073a6e67ef0b Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 10 Aug 2025 10:43:20 +0200 Subject: [PATCH 17/34] Updated Weapons --- module/config/itemConfig.mjs | 22 ++++++- .../weapon_Aantari_Bow_ijodu5yNBoMxpkHV.json | 35 ++++++----- ...ane_Frame_Wheelchair_la3sAWgnvadc4NvP.json | 33 +++++++---- ..._Advanced_Broadsword_WtQAGz0TUgz8Xg70.json | 35 ++++++----- ...dvanced_Small_Dagger_0thN0BpN05KT8Avx.json | 33 +++++++---- ...ane_Frame_Wheelchair_XRChepscgr75Uug7.json | 35 ++++++----- .../weapon_Broadsword_1cwWNt4sqlgA8gCT.json | 35 ++++++----- ...ane_Frame_Wheelchair_N9P695V5KKlJbAY5.json | 33 +++++++---- ..._Improved_Broadsword_OcKeLJxvmdT81VBc.json | 35 ++++++----- ..._Improved_Shortsword_rSyBNRwemBVuTo3H.json | 33 +++++++---- ...mproved_Small_Dagger_nMuF8ZDZ2aXZVTg6.json | 33 +++++++---- ...eapon_Keeper_s_Staff_q382JqMkqLaaFLIr.json | 35 ++++++----- ...weapon_Knuckle_Claws_SFqganS8Du4aEKjQ.json | 59 +++---------------- ...ane_Frame_Wheelchair_gA2tiET9VHGhwMoO.json | 33 +++++++---- ...Legendary_Broadsword_y3hfTPfZhMognyaJ.json | 35 ++++++----- ...Legendary_Shortsword_dEumq3BIZBk5xYTk.json | 33 +++++++---- ...gendary_Small_Dagger_Px3Rh3kIvAqyISxJ.json | 33 +++++++---- .../weapon_Primer_Shard_SxcblanBvqaest3A.json | 19 +++--- .../weapon_Shortsword_cjGZpXCoshEqi1FI.json | 33 +++++++---- .../weapon_Small_Dagger_wKklDxs5nkzILNp4.json | 33 +++++++---- ...weapon_Spiked_Shield_vzyzFwLUniWZV1rt.json | 33 +++++++---- .../weapon_Thistlebow_I1nDGpulg29GpWOW.json | 35 ++++++----- .../weapon_War_Scythe_z6yEdFYQJ5IzgTX3.json | 35 ++++++----- 23 files changed, 450 insertions(+), 328 deletions(-) diff --git a/module/config/itemConfig.mjs b/module/config/itemConfig.mjs index 296dc927..cb244469 100644 --- a/module/config/itemConfig.mjs +++ b/module/config/itemConfig.mjs @@ -780,7 +780,15 @@ export const weaponFeatures = { mode: 2, value: '1' } - ] + ], + system: { + rangeDependence: { + enabled: true, + range: 'melee', + target: 'hostile', + type: 'withinRange' + } + } } ] }, @@ -1079,7 +1087,15 @@ export const weaponFeatures = { mode: 2, value: 'ITEM.@system.tier + 1' } - ] + ], + system: { + rangeDependence: { + enabled: true, + range: 'melee', + target: 'hostile', + type: 'withinRange' + } + } } ] }, @@ -1208,7 +1224,7 @@ export const weaponFeatures = { img: 'icons/skills/melee/strike-sword-slashing-red.webp', changes: [ { - key: 'system.bonuses.roll.primaryWeapon.attack', + key: 'system.bonuses.roll.primaryWeapon.bonus', mode: 2, value: 1 } diff --git a/src/packs/items/weapons/weapon_Aantari_Bow_ijodu5yNBoMxpkHV.json b/src/packs/items/weapons/weapon_Aantari_Bow_ijodu5yNBoMxpkHV.json index 16c4b359..0dc1f068 100644 --- a/src/packs/items/weapons/weapon_Aantari_Bow_ijodu5yNBoMxpkHV.json +++ b/src/packs/items/weapons/weapon_Aantari_Bow_ijodu5yNBoMxpkHV.json @@ -16,7 +16,7 @@ { "value": "reliable", "effectIds": [ - "HrbJ0bI7lMAYUCux" + "nRNnU57i7RMJoklC" ], "actionIds": [] } @@ -109,14 +109,21 @@ "img": "icons/skills/melee/strike-sword-slashing-red.webp", "changes": [ { - "key": "system.bonuses.roll.primaryWeapon.attack", + "key": "system.bonuses.roll.primaryWeapon.bonus", "mode": 2, "value": "1" } ], - "_id": "HrbJ0bI7lMAYUCux", + "_id": "nRNnU57i7RMJoklC", "type": "base", - "system": {}, + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, "disabled": false, "duration": { "startTime": null, @@ -132,14 +139,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753835307838, - "modifiedTime": 1753835307838, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "systemVersion": "1.0.0", + "createdTime": 1754815224718, + "modifiedTime": 1754815224718, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!ijodu5yNBoMxpkHV.HrbJ0bI7lMAYUCux" + "_key": "!items.effects!ijodu5yNBoMxpkHV.nRNnU57i7RMJoklC" } ], "sort": 0, @@ -152,12 +159,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753835285790, - "modifiedTime": 1753835317605, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754815224721, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!ijodu5yNBoMxpkHV" } diff --git a/src/packs/items/weapons/weapon_Advanced_Arcane_Frame_Wheelchair_la3sAWgnvadc4NvP.json b/src/packs/items/weapons/weapon_Advanced_Arcane_Frame_Wheelchair_la3sAWgnvadc4NvP.json index f0796325..c5edd49f 100644 --- a/src/packs/items/weapons/weapon_Advanced_Arcane_Frame_Wheelchair_la3sAWgnvadc4NvP.json +++ b/src/packs/items/weapons/weapon_Advanced_Arcane_Frame_Wheelchair_la3sAWgnvadc4NvP.json @@ -15,7 +15,7 @@ { "value": "reliable", "effectIds": [ - "G561ymlNGmaFAYFB" + "VnV5X9MBMabhz47b" ], "actionIds": [] } @@ -108,14 +108,21 @@ "img": "icons/skills/melee/strike-sword-slashing-red.webp", "changes": [ { - "key": "system.bonuses.roll.primaryWeapon.attack", + "key": "system.bonuses.roll.primaryWeapon.bonus", "mode": 2, "value": "1" } ], - "_id": "G561ymlNGmaFAYFB", + "_id": "VnV5X9MBMabhz47b", "type": "base", - "system": {}, + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, "disabled": false, "duration": { "startTime": null, @@ -131,12 +138,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "lastModifiedBy": null + "systemVersion": "1.0.0", + "createdTime": 1754815300372, + "modifiedTime": 1754815300372, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!la3sAWgnvadc4NvP.G561ymlNGmaFAYFB" + "_key": "!items.effects!la3sAWgnvadc4NvP.VnV5X9MBMabhz47b" } ], "ownership": { @@ -148,12 +157,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753836715885, - "modifiedTime": 1753836789197, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754815300375, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_id": "la3sAWgnvadc4NvP", "sort": 0, diff --git a/src/packs/items/weapons/weapon_Advanced_Broadsword_WtQAGz0TUgz8Xg70.json b/src/packs/items/weapons/weapon_Advanced_Broadsword_WtQAGz0TUgz8Xg70.json index ad5ad7ae..19a6d9bf 100644 --- a/src/packs/items/weapons/weapon_Advanced_Broadsword_WtQAGz0TUgz8Xg70.json +++ b/src/packs/items/weapons/weapon_Advanced_Broadsword_WtQAGz0TUgz8Xg70.json @@ -16,7 +16,7 @@ { "value": "reliable", "effectIds": [ - "fRPKHzbKRz4yTHAF" + "wu2AmDvgeWI3hmRQ" ], "actionIds": [] } @@ -109,14 +109,21 @@ "img": "icons/skills/melee/strike-sword-slashing-red.webp", "changes": [ { - "key": "system.bonuses.roll.primaryWeapon.attack", + "key": "system.bonuses.roll.primaryWeapon.bonus", "mode": 2, "value": "1" } ], - "_id": "fRPKHzbKRz4yTHAF", + "_id": "wu2AmDvgeWI3hmRQ", "type": "base", - "system": {}, + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, "disabled": false, "duration": { "startTime": null, @@ -132,14 +139,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753831629563, - "modifiedTime": 1753831629563, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "systemVersion": "1.0.0", + "createdTime": 1754814950116, + "modifiedTime": 1754814950116, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!WtQAGz0TUgz8Xg70.fRPKHzbKRz4yTHAF" + "_key": "!items.effects!WtQAGz0TUgz8Xg70.wu2AmDvgeWI3hmRQ" } ], "sort": 0, @@ -152,12 +159,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753831599435, - "modifiedTime": 1753831629573, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754814950120, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!WtQAGz0TUgz8Xg70" } diff --git a/src/packs/items/weapons/weapon_Advanced_Small_Dagger_0thN0BpN05KT8Avx.json b/src/packs/items/weapons/weapon_Advanced_Small_Dagger_0thN0BpN05KT8Avx.json index cf3ae063..bfcef598 100644 --- a/src/packs/items/weapons/weapon_Advanced_Small_Dagger_0thN0BpN05KT8Avx.json +++ b/src/packs/items/weapons/weapon_Advanced_Small_Dagger_0thN0BpN05KT8Avx.json @@ -16,7 +16,7 @@ { "value": "paired", "effectIds": [ - "gJ7Ey9CfPZqYgxEO" + "MYgB3v3oQ5lIr3VE" ], "actionIds": [] } @@ -114,9 +114,16 @@ "value": "ITEM.@system.tier + 1" } ], - "_id": "gJ7Ey9CfPZqYgxEO", + "system": { + "rangeDependence": { + "enabled": true, + "range": "melee", + "target": "hostile", + "type": "withinRange" + } + }, + "_id": "MYgB3v3oQ5lIr3VE", "type": "base", - "system": {}, "disabled": false, "duration": { "startTime": null, @@ -132,14 +139,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753794991410, - "modifiedTime": 1753794991410, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "systemVersion": "1.0.0", + "createdTime": 1754814673988, + "modifiedTime": 1754814673988, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!0thN0BpN05KT8Avx.gJ7Ey9CfPZqYgxEO" + "_key": "!items.effects!0thN0BpN05KT8Avx.MYgB3v3oQ5lIr3VE" } ], "sort": 0, @@ -152,12 +159,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753794938643, - "modifiedTime": 1753794991413, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754814673991, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!0thN0BpN05KT8Avx" } diff --git a/src/packs/items/weapons/weapon_Arcane_Frame_Wheelchair_XRChepscgr75Uug7.json b/src/packs/items/weapons/weapon_Arcane_Frame_Wheelchair_XRChepscgr75Uug7.json index 6d18e14c..49e052b7 100644 --- a/src/packs/items/weapons/weapon_Arcane_Frame_Wheelchair_XRChepscgr75Uug7.json +++ b/src/packs/items/weapons/weapon_Arcane_Frame_Wheelchair_XRChepscgr75Uug7.json @@ -16,7 +16,7 @@ { "value": "reliable", "effectIds": [ - "G561ymlNGmaFAYFB" + "dXHsy9qr5FWZqsVq" ], "actionIds": [] } @@ -109,14 +109,21 @@ "img": "icons/skills/melee/strike-sword-slashing-red.webp", "changes": [ { - "key": "system.bonuses.roll.primaryWeapon.attack", + "key": "system.bonuses.roll.primaryWeapon.bonus", "mode": 2, "value": "1" } ], - "_id": "G561ymlNGmaFAYFB", + "_id": "dXHsy9qr5FWZqsVq", "type": "base", - "system": {}, + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, "disabled": false, "duration": { "startTime": null, @@ -132,14 +139,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753836707582, - "modifiedTime": 1753836707582, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "systemVersion": "1.0.0", + "createdTime": 1754815278217, + "modifiedTime": 1754815278217, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!XRChepscgr75Uug7.G561ymlNGmaFAYFB" + "_key": "!items.effects!XRChepscgr75Uug7.dXHsy9qr5FWZqsVq" } ], "sort": 0, @@ -152,12 +159,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753836689082, - "modifiedTime": 1753836707594, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754815278220, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!XRChepscgr75Uug7" } diff --git a/src/packs/items/weapons/weapon_Broadsword_1cwWNt4sqlgA8gCT.json b/src/packs/items/weapons/weapon_Broadsword_1cwWNt4sqlgA8gCT.json index 1934036f..959a8547 100644 --- a/src/packs/items/weapons/weapon_Broadsword_1cwWNt4sqlgA8gCT.json +++ b/src/packs/items/weapons/weapon_Broadsword_1cwWNt4sqlgA8gCT.json @@ -16,7 +16,7 @@ { "value": "reliable", "effectIds": [ - "GNwIa1EAaa0T0RZi" + "mqcpj2cFAprf2AmY" ], "actionIds": [] } @@ -109,14 +109,21 @@ "img": "icons/skills/melee/strike-sword-slashing-red.webp", "changes": [ { - "key": "system.bonuses.roll.primaryWeapon.attack", + "key": "system.bonuses.roll.primaryWeapon.bonus", "mode": 2, "value": "1" } ], - "_id": "GNwIa1EAaa0T0RZi", + "_id": "mqcpj2cFAprf2AmY", "type": "base", - "system": {}, + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, "disabled": false, "duration": { "startTime": null, @@ -132,14 +139,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753827762112, - "modifiedTime": 1753827762112, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "systemVersion": "1.0.0", + "createdTime": 1754814769818, + "modifiedTime": 1754814769818, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!1cwWNt4sqlgA8gCT.GNwIa1EAaa0T0RZi" + "_key": "!items.effects!1cwWNt4sqlgA8gCT.mqcpj2cFAprf2AmY" } ], "sort": 0, @@ -152,12 +159,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753827734892, - "modifiedTime": 1753827762118, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754814769821, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!1cwWNt4sqlgA8gCT" } diff --git a/src/packs/items/weapons/weapon_Improved_Arcane_Frame_Wheelchair_N9P695V5KKlJbAY5.json b/src/packs/items/weapons/weapon_Improved_Arcane_Frame_Wheelchair_N9P695V5KKlJbAY5.json index 36070ae1..e28a9c44 100644 --- a/src/packs/items/weapons/weapon_Improved_Arcane_Frame_Wheelchair_N9P695V5KKlJbAY5.json +++ b/src/packs/items/weapons/weapon_Improved_Arcane_Frame_Wheelchair_N9P695V5KKlJbAY5.json @@ -15,7 +15,7 @@ { "value": "reliable", "effectIds": [ - "G561ymlNGmaFAYFB" + "1f6fFhOLwZrmA6e5" ], "actionIds": [] } @@ -108,14 +108,21 @@ "img": "icons/skills/melee/strike-sword-slashing-red.webp", "changes": [ { - "key": "system.bonuses.roll.primaryWeapon.attack", + "key": "system.bonuses.roll.primaryWeapon.bonus", "mode": 2, "value": "1" } ], - "_id": "G561ymlNGmaFAYFB", + "_id": "1f6fFhOLwZrmA6e5", "type": "base", - "system": {}, + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, "disabled": false, "duration": { "startTime": null, @@ -131,12 +138,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "lastModifiedBy": null + "systemVersion": "1.0.0", + "createdTime": 1754815290648, + "modifiedTime": 1754815290648, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!N9P695V5KKlJbAY5.G561ymlNGmaFAYFB" + "_key": "!items.effects!N9P695V5KKlJbAY5.1f6fFhOLwZrmA6e5" } ], "ownership": { @@ -148,12 +157,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753836714712, - "modifiedTime": 1753836748404, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754815290653, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_id": "N9P695V5KKlJbAY5", "sort": 0, diff --git a/src/packs/items/weapons/weapon_Improved_Broadsword_OcKeLJxvmdT81VBc.json b/src/packs/items/weapons/weapon_Improved_Broadsword_OcKeLJxvmdT81VBc.json index dad2f4db..69e46858 100644 --- a/src/packs/items/weapons/weapon_Improved_Broadsword_OcKeLJxvmdT81VBc.json +++ b/src/packs/items/weapons/weapon_Improved_Broadsword_OcKeLJxvmdT81VBc.json @@ -16,7 +16,7 @@ { "value": "reliable", "effectIds": [ - "xU0DD5ydbwmXMKtF" + "228lcQpohdJ3Bbga" ], "actionIds": [] } @@ -109,14 +109,21 @@ "img": "icons/skills/melee/strike-sword-slashing-red.webp", "changes": [ { - "key": "system.bonuses.roll.primaryWeapon.attack", + "key": "system.bonuses.roll.primaryWeapon.bonus", "mode": 2, "value": "1" } ], - "_id": "xU0DD5ydbwmXMKtF", + "_id": "228lcQpohdJ3Bbga", "type": "base", - "system": {}, + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, "disabled": false, "duration": { "startTime": null, @@ -132,14 +139,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753829126416, - "modifiedTime": 1753829126416, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "systemVersion": "1.0.0", + "createdTime": 1754814935810, + "modifiedTime": 1754814935810, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!OcKeLJxvmdT81VBc.xU0DD5ydbwmXMKtF" + "_key": "!items.effects!OcKeLJxvmdT81VBc.228lcQpohdJ3Bbga" } ], "sort": 0, @@ -152,12 +159,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753829098118, - "modifiedTime": 1753829367508, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754814935815, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!OcKeLJxvmdT81VBc" } diff --git a/src/packs/items/weapons/weapon_Improved_Shortsword_rSyBNRwemBVuTo3H.json b/src/packs/items/weapons/weapon_Improved_Shortsword_rSyBNRwemBVuTo3H.json index 65f03b0b..fb585281 100644 --- a/src/packs/items/weapons/weapon_Improved_Shortsword_rSyBNRwemBVuTo3H.json +++ b/src/packs/items/weapons/weapon_Improved_Shortsword_rSyBNRwemBVuTo3H.json @@ -16,7 +16,7 @@ { "value": "paired", "effectIds": [ - "cSmiIOXeuw0xpjel" + "9iHHwd9BxkBsV9lY" ], "actionIds": [] } @@ -114,9 +114,16 @@ "value": "ITEM.@system.tier + 1" } ], - "_id": "cSmiIOXeuw0xpjel", + "system": { + "rangeDependence": { + "enabled": true, + "range": "melee", + "target": "hostile", + "type": "withinRange" + } + }, + "_id": "9iHHwd9BxkBsV9lY", "type": "base", - "system": {}, "disabled": false, "duration": { "startTime": null, @@ -132,14 +139,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753794021004, - "modifiedTime": 1753794021004, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "systemVersion": "1.0.0", + "createdTime": 1754814695260, + "modifiedTime": 1754814695260, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!rSyBNRwemBVuTo3H.cSmiIOXeuw0xpjel" + "_key": "!items.effects!rSyBNRwemBVuTo3H.9iHHwd9BxkBsV9lY" } ], "sort": 0, @@ -152,12 +159,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753744566951, - "modifiedTime": 1753794021010, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754814695265, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!rSyBNRwemBVuTo3H" } diff --git a/src/packs/items/weapons/weapon_Improved_Small_Dagger_nMuF8ZDZ2aXZVTg6.json b/src/packs/items/weapons/weapon_Improved_Small_Dagger_nMuF8ZDZ2aXZVTg6.json index 04379b71..e141fbc2 100644 --- a/src/packs/items/weapons/weapon_Improved_Small_Dagger_nMuF8ZDZ2aXZVTg6.json +++ b/src/packs/items/weapons/weapon_Improved_Small_Dagger_nMuF8ZDZ2aXZVTg6.json @@ -16,7 +16,7 @@ { "value": "paired", "effectIds": [ - "rOjtLkrnI9EZHJm8" + "JHIUGyZH5q83ODvd" ], "actionIds": [] } @@ -114,9 +114,16 @@ "value": "ITEM.@system.tier + 1" } ], - "_id": "rOjtLkrnI9EZHJm8", + "system": { + "rangeDependence": { + "enabled": true, + "range": "melee", + "target": "hostile", + "type": "withinRange" + } + }, + "_id": "JHIUGyZH5q83ODvd", "type": "base", - "system": {}, "disabled": false, "duration": { "startTime": null, @@ -132,14 +139,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753794340876, - "modifiedTime": 1753794340876, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "systemVersion": "1.0.0", + "createdTime": 1754814703717, + "modifiedTime": 1754814703717, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!nMuF8ZDZ2aXZVTg6.rOjtLkrnI9EZHJm8" + "_key": "!items.effects!nMuF8ZDZ2aXZVTg6.JHIUGyZH5q83ODvd" } ], "sort": 0, @@ -152,12 +159,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753794291887, - "modifiedTime": 1753794340879, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754814703719, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!nMuF8ZDZ2aXZVTg6" } diff --git a/src/packs/items/weapons/weapon_Keeper_s_Staff_q382JqMkqLaaFLIr.json b/src/packs/items/weapons/weapon_Keeper_s_Staff_q382JqMkqLaaFLIr.json index c7b5c32f..d934731d 100644 --- a/src/packs/items/weapons/weapon_Keeper_s_Staff_q382JqMkqLaaFLIr.json +++ b/src/packs/items/weapons/weapon_Keeper_s_Staff_q382JqMkqLaaFLIr.json @@ -16,7 +16,7 @@ { "value": "reliable", "effectIds": [ - "FlmOrbhYbieIAVJL" + "LvxPAfrKuRfgubGV" ], "actionIds": [] } @@ -109,14 +109,21 @@ "img": "icons/skills/melee/strike-sword-slashing-red.webp", "changes": [ { - "key": "system.bonuses.roll.primaryWeapon.attack", + "key": "system.bonuses.roll.primaryWeapon.bonus", "mode": 2, "value": "1" } ], - "_id": "FlmOrbhYbieIAVJL", + "_id": "LvxPAfrKuRfgubGV", "type": "base", - "system": {}, + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, "disabled": false, "duration": { "startTime": null, @@ -132,14 +139,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753831441325, - "modifiedTime": 1753831441325, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "systemVersion": "1.0.0", + "createdTime": 1754815023490, + "modifiedTime": 1754815023490, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!q382JqMkqLaaFLIr.FlmOrbhYbieIAVJL" + "_key": "!items.effects!q382JqMkqLaaFLIr.LvxPAfrKuRfgubGV" } ], "sort": 0, @@ -152,12 +159,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753831418620, - "modifiedTime": 1753831441332, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754815023493, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!q382JqMkqLaaFLIr" } diff --git a/src/packs/items/weapons/weapon_Knuckle_Claws_SFqganS8Du4aEKjQ.json b/src/packs/items/weapons/weapon_Knuckle_Claws_SFqganS8Du4aEKjQ.json index c65353db..f2f53dc7 100644 --- a/src/packs/items/weapons/weapon_Knuckle_Claws_SFqganS8Du4aEKjQ.json +++ b/src/packs/items/weapons/weapon_Knuckle_Claws_SFqganS8Du4aEKjQ.json @@ -7,63 +7,22 @@ "system": { "description": "", "actions": { - "fRKcEeShWSLkoExh": { + "B0uT1D1WRidiHxyh": { "type": "effect", "actionType": "action", "chatDisplay": true, "name": "Double Up", "description": "When you make an attack with your primary weapon, you can deal damage to another target within Melee range.", "img": "icons/skills/melee/strike-slashes-orange.webp", - "_id": "fRKcEeShWSLkoExh", + "_id": "B0uT1D1WRidiHxyh", "effects": [], "systemPath": "actions", "cost": [], "uses": { "value": null, "max": null, - "recovery": null - }, - "target": { - "type": "any", - "amount": null - } - }, - "lVsEmJwjYgpYL9l4": { - "type": "effect", - "actionType": "action", - "chatDisplay": true, - "name": "Double Up", - "description": "When you make an attack with your primary weapon, you can deal damage to another target within Melee range.", - "img": "icons/skills/melee/strike-slashes-orange.webp", - "_id": "lVsEmJwjYgpYL9l4", - "effects": [], - "systemPath": "actions", - "cost": [], - "uses": { - "value": null, - "max": null, - "recovery": null - }, - "target": { - "type": "any", - "amount": null - } - }, - "2ndqofzTHsEUMxsm": { - "type": "effect", - "actionType": "action", - "chatDisplay": true, - "name": "Double Up", - "description": "When you make an attack with your primary weapon, you can deal damage to another target within Melee range.", - "img": "icons/skills/melee/strike-slashes-orange.webp", - "_id": "2ndqofzTHsEUMxsm", - "effects": [], - "systemPath": "actions", - "cost": [], - "uses": { - "value": null, - "max": null, - "recovery": null + "recovery": null, + "consumeOnSuccess": false }, "target": { "type": "any", @@ -81,7 +40,7 @@ "value": "doubledUp", "effectIds": [], "actionIds": [ - "2ndqofzTHsEUMxsm" + "B0uT1D1WRidiHxyh" ] } ], @@ -177,12 +136,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753797258168, - "modifiedTime": 1753798882899, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754814600761, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!SFqganS8Du4aEKjQ" } diff --git a/src/packs/items/weapons/weapon_Legendary_Arcane_Frame_Wheelchair_gA2tiET9VHGhwMoO.json b/src/packs/items/weapons/weapon_Legendary_Arcane_Frame_Wheelchair_gA2tiET9VHGhwMoO.json index 16211d24..a431d0c6 100644 --- a/src/packs/items/weapons/weapon_Legendary_Arcane_Frame_Wheelchair_gA2tiET9VHGhwMoO.json +++ b/src/packs/items/weapons/weapon_Legendary_Arcane_Frame_Wheelchair_gA2tiET9VHGhwMoO.json @@ -15,7 +15,7 @@ { "value": "reliable", "effectIds": [ - "G561ymlNGmaFAYFB" + "TvsoAiqHCwgtYat1" ], "actionIds": [] } @@ -108,14 +108,21 @@ "img": "icons/skills/melee/strike-sword-slashing-red.webp", "changes": [ { - "key": "system.bonuses.roll.primaryWeapon.attack", + "key": "system.bonuses.roll.primaryWeapon.bonus", "mode": 2, "value": "1" } ], - "_id": "G561ymlNGmaFAYFB", + "_id": "TvsoAiqHCwgtYat1", "type": "base", - "system": {}, + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, "disabled": false, "duration": { "startTime": null, @@ -131,12 +138,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "lastModifiedBy": null + "systemVersion": "1.0.0", + "createdTime": 1754815308723, + "modifiedTime": 1754815308723, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!gA2tiET9VHGhwMoO.G561ymlNGmaFAYFB" + "_key": "!items.effects!gA2tiET9VHGhwMoO.TvsoAiqHCwgtYat1" } ], "ownership": { @@ -148,12 +157,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753836717240, - "modifiedTime": 1753836812453, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754815308726, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_id": "gA2tiET9VHGhwMoO", "sort": 0, diff --git a/src/packs/items/weapons/weapon_Legendary_Broadsword_y3hfTPfZhMognyaJ.json b/src/packs/items/weapons/weapon_Legendary_Broadsword_y3hfTPfZhMognyaJ.json index ca0622de..32d19734 100644 --- a/src/packs/items/weapons/weapon_Legendary_Broadsword_y3hfTPfZhMognyaJ.json +++ b/src/packs/items/weapons/weapon_Legendary_Broadsword_y3hfTPfZhMognyaJ.json @@ -16,7 +16,7 @@ { "value": "reliable", "effectIds": [ - "lmUzKw6J6RW3krRT" + "ujb6VAqjyXmfnnjy" ], "actionIds": [] } @@ -109,14 +109,21 @@ "img": "icons/skills/melee/strike-sword-slashing-red.webp", "changes": [ { - "key": "system.bonuses.roll.primaryWeapon.attack", + "key": "system.bonuses.roll.primaryWeapon.bonus", "mode": 2, "value": "1" } ], - "_id": "lmUzKw6J6RW3krRT", + "_id": "ujb6VAqjyXmfnnjy", "type": "base", - "system": {}, + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, "disabled": false, "duration": { "startTime": null, @@ -132,14 +139,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753834461846, - "modifiedTime": 1753834461846, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "systemVersion": "1.0.0", + "createdTime": 1754814961439, + "modifiedTime": 1754814961439, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!y3hfTPfZhMognyaJ.lmUzKw6J6RW3krRT" + "_key": "!items.effects!y3hfTPfZhMognyaJ.ujb6VAqjyXmfnnjy" } ], "sort": 0, @@ -152,12 +159,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753834430378, - "modifiedTime": 1753834465538, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754814961454, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!y3hfTPfZhMognyaJ" } diff --git a/src/packs/items/weapons/weapon_Legendary_Shortsword_dEumq3BIZBk5xYTk.json b/src/packs/items/weapons/weapon_Legendary_Shortsword_dEumq3BIZBk5xYTk.json index 2f832ee7..5f6f6cb2 100644 --- a/src/packs/items/weapons/weapon_Legendary_Shortsword_dEumq3BIZBk5xYTk.json +++ b/src/packs/items/weapons/weapon_Legendary_Shortsword_dEumq3BIZBk5xYTk.json @@ -16,7 +16,7 @@ { "value": "paired", "effectIds": [ - "DgZQSBJx9JmoOngB" + "VFt61c2Apfbli2dG" ], "actionIds": [] } @@ -114,9 +114,16 @@ "value": "ITEM.@system.tier + 1" } ], - "_id": "DgZQSBJx9JmoOngB", + "system": { + "rangeDependence": { + "enabled": true, + "range": "melee", + "target": "hostile", + "type": "withinRange" + } + }, + "_id": "VFt61c2Apfbli2dG", "type": "base", - "system": {}, "disabled": false, "duration": { "startTime": null, @@ -132,14 +139,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753796957432, - "modifiedTime": 1753796957432, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "systemVersion": "1.0.0", + "createdTime": 1754814544486, + "modifiedTime": 1754814544486, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!dEumq3BIZBk5xYTk.DgZQSBJx9JmoOngB" + "_key": "!items.effects!dEumq3BIZBk5xYTk.VFt61c2Apfbli2dG" } ], "sort": 0, @@ -152,12 +159,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753796913551, - "modifiedTime": 1753796957439, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754814544510, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!dEumq3BIZBk5xYTk" } diff --git a/src/packs/items/weapons/weapon_Legendary_Small_Dagger_Px3Rh3kIvAqyISxJ.json b/src/packs/items/weapons/weapon_Legendary_Small_Dagger_Px3Rh3kIvAqyISxJ.json index be3a5886..855ddd80 100644 --- a/src/packs/items/weapons/weapon_Legendary_Small_Dagger_Px3Rh3kIvAqyISxJ.json +++ b/src/packs/items/weapons/weapon_Legendary_Small_Dagger_Px3Rh3kIvAqyISxJ.json @@ -16,7 +16,7 @@ { "value": "paired", "effectIds": [ - "Wjl3MEwNdQPeY63u" + "rnVm0jSEtdWhKGCh" ], "actionIds": [] } @@ -114,9 +114,16 @@ "value": "ITEM.@system.tier + 1" } ], - "_id": "Wjl3MEwNdQPeY63u", + "system": { + "rangeDependence": { + "enabled": true, + "range": "melee", + "target": "hostile", + "type": "withinRange" + } + }, + "_id": "rnVm0jSEtdWhKGCh", "type": "base", - "system": {}, "disabled": false, "duration": { "startTime": null, @@ -132,14 +139,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753797088930, - "modifiedTime": 1753797088930, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "systemVersion": "1.0.0", + "createdTime": 1754814562988, + "modifiedTime": 1754814562988, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!Px3Rh3kIvAqyISxJ.Wjl3MEwNdQPeY63u" + "_key": "!items.effects!Px3Rh3kIvAqyISxJ.rnVm0jSEtdWhKGCh" } ], "sort": 0, @@ -152,12 +159,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753797057472, - "modifiedTime": 1753797088933, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754814562995, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!Px3Rh3kIvAqyISxJ" } diff --git a/src/packs/items/weapons/weapon_Primer_Shard_SxcblanBvqaest3A.json b/src/packs/items/weapons/weapon_Primer_Shard_SxcblanBvqaest3A.json index 2b39a221..e3dd3035 100644 --- a/src/packs/items/weapons/weapon_Primer_Shard_SxcblanBvqaest3A.json +++ b/src/packs/items/weapons/weapon_Primer_Shard_SxcblanBvqaest3A.json @@ -7,21 +7,22 @@ "system": { "description": "", "actions": { - "KGJJgz0SMdY0f0em": { + "dNN2KOfUxGzQ2yjY": { "type": "effect", "actionType": "action", "chatDisplay": true, "name": "Lock On", "description": "On a successful attack, your next attack against the same target with your primary weapon automatically succeeds.", "img": "icons/skills/targeting/crosshair-arrowhead-blue.webp", - "_id": "KGJJgz0SMdY0f0em", + "_id": "dNN2KOfUxGzQ2yjY", "effects": [], "systemPath": "actions", "cost": [], "uses": { "value": null, "max": null, - "recovery": null + "recovery": null, + "consumeOnSuccess": false }, "target": { "type": "any", @@ -36,10 +37,10 @@ "burden": "oneHanded", "weaponFeatures": [ { - "value": "lockedon", + "value": "lockedOn", "effectIds": [], "actionIds": [ - "KGJJgz0SMdY0f0em" + "dNN2KOfUxGzQ2yjY" ] } ], @@ -135,12 +136,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753797317938, - "modifiedTime": 1753797376548, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754814518028, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!SxcblanBvqaest3A" } diff --git a/src/packs/items/weapons/weapon_Shortsword_cjGZpXCoshEqi1FI.json b/src/packs/items/weapons/weapon_Shortsword_cjGZpXCoshEqi1FI.json index 92cb44b7..31cb74cd 100644 --- a/src/packs/items/weapons/weapon_Shortsword_cjGZpXCoshEqi1FI.json +++ b/src/packs/items/weapons/weapon_Shortsword_cjGZpXCoshEqi1FI.json @@ -16,7 +16,7 @@ { "value": "paired", "effectIds": [ - "VII5oRJrQTsSir0E" + "5RpOUFs0kDhzwltM" ], "actionIds": [] } @@ -114,9 +114,16 @@ "value": "ITEM.@system.tier + 1" } ], - "_id": "VII5oRJrQTsSir0E", + "system": { + "rangeDependence": { + "enabled": true, + "range": "melee", + "target": "hostile", + "type": "withinRange" + } + }, + "_id": "5RpOUFs0kDhzwltM", "type": "base", - "system": {}, "disabled": false, "duration": { "startTime": null, @@ -132,14 +139,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753793980974, - "modifiedTime": 1753793980974, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "systemVersion": "1.0.0", + "createdTime": 1754814729443, + "modifiedTime": 1754814729443, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!cjGZpXCoshEqi1FI.VII5oRJrQTsSir0E" + "_key": "!items.effects!cjGZpXCoshEqi1FI.5RpOUFs0kDhzwltM" } ], "sort": 0, @@ -152,12 +159,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753741549716, - "modifiedTime": 1753793980983, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754814729447, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!cjGZpXCoshEqi1FI" } diff --git a/src/packs/items/weapons/weapon_Small_Dagger_wKklDxs5nkzILNp4.json b/src/packs/items/weapons/weapon_Small_Dagger_wKklDxs5nkzILNp4.json index 4e9613f6..229162f8 100644 --- a/src/packs/items/weapons/weapon_Small_Dagger_wKklDxs5nkzILNp4.json +++ b/src/packs/items/weapons/weapon_Small_Dagger_wKklDxs5nkzILNp4.json @@ -16,7 +16,7 @@ { "value": "paired", "effectIds": [ - "2OLsxbZJqMgk680J" + "wK6ccFAirp9HYK5Q" ], "actionIds": [] } @@ -114,9 +114,16 @@ "value": "ITEM.@system.tier + 1" } ], - "_id": "2OLsxbZJqMgk680J", + "system": { + "rangeDependence": { + "enabled": true, + "range": "melee", + "target": "hostile", + "type": "withinRange" + } + }, + "_id": "wK6ccFAirp9HYK5Q", "type": "base", - "system": {}, "disabled": false, "duration": { "startTime": null, @@ -132,14 +139,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753794165509, - "modifiedTime": 1753794165509, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "systemVersion": "1.0.0", + "createdTime": 1754814738851, + "modifiedTime": 1754814738851, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!wKklDxs5nkzILNp4.2OLsxbZJqMgk680J" + "_key": "!items.effects!wKklDxs5nkzILNp4.wK6ccFAirp9HYK5Q" } ], "sort": 0, @@ -152,12 +159,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753744141625, - "modifiedTime": 1753794165511, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754814738854, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!wKklDxs5nkzILNp4" } diff --git a/src/packs/items/weapons/weapon_Spiked_Shield_vzyzFwLUniWZV1rt.json b/src/packs/items/weapons/weapon_Spiked_Shield_vzyzFwLUniWZV1rt.json index 8a40a800..9b9d691a 100644 --- a/src/packs/items/weapons/weapon_Spiked_Shield_vzyzFwLUniWZV1rt.json +++ b/src/packs/items/weapons/weapon_Spiked_Shield_vzyzFwLUniWZV1rt.json @@ -16,7 +16,7 @@ { "value": "doubleDuty", "effectIds": [ - "qo4VPSV0VZha1ya2" + "d3TJtlpoHBCztbom" ], "actionIds": [] } @@ -119,9 +119,16 @@ "value": "1" } ], - "_id": "qo4VPSV0VZha1ya2", + "system": { + "rangeDependence": { + "enabled": true, + "range": "melee", + "target": "hostile", + "type": "withinRange" + } + }, + "_id": "d3TJtlpoHBCztbom", "type": "base", - "system": {}, "disabled": false, "duration": { "startTime": null, @@ -137,14 +144,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753794551639, - "modifiedTime": 1753794551639, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "systemVersion": "1.0.0", + "createdTime": 1754814481208, + "modifiedTime": 1754814481208, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!vzyzFwLUniWZV1rt.qo4VPSV0VZha1ya2" + "_key": "!items.effects!vzyzFwLUniWZV1rt.d3TJtlpoHBCztbom" } ], "sort": 0, @@ -157,12 +164,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753794535926, - "modifiedTime": 1753794665373, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754814481212, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!vzyzFwLUniWZV1rt" } diff --git a/src/packs/items/weapons/weapon_Thistlebow_I1nDGpulg29GpWOW.json b/src/packs/items/weapons/weapon_Thistlebow_I1nDGpulg29GpWOW.json index ca641ad2..cec5b0d1 100644 --- a/src/packs/items/weapons/weapon_Thistlebow_I1nDGpulg29GpWOW.json +++ b/src/packs/items/weapons/weapon_Thistlebow_I1nDGpulg29GpWOW.json @@ -16,7 +16,7 @@ { "value": "reliable", "effectIds": [ - "YA5tbB6XBISWsf0p" + "G9mMGxBSexwjWTYV" ], "actionIds": [] } @@ -109,14 +109,21 @@ "img": "icons/skills/melee/strike-sword-slashing-red.webp", "changes": [ { - "key": "system.bonuses.roll.primaryWeapon.attack", + "key": "system.bonuses.roll.primaryWeapon.bonus", "mode": 2, "value": "1" } ], - "_id": "YA5tbB6XBISWsf0p", + "_id": "G9mMGxBSexwjWTYV", "type": "base", - "system": {}, + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, "disabled": false, "duration": { "startTime": null, @@ -132,14 +139,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753836322206, - "modifiedTime": 1753836322206, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "systemVersion": "1.0.0", + "createdTime": 1754815251133, + "modifiedTime": 1754815251133, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!I1nDGpulg29GpWOW.YA5tbB6XBISWsf0p" + "_key": "!items.effects!I1nDGpulg29GpWOW.G9mMGxBSexwjWTYV" } ], "sort": 0, @@ -152,12 +159,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753836302436, - "modifiedTime": 1753836334493, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754815251135, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!I1nDGpulg29GpWOW" } diff --git a/src/packs/items/weapons/weapon_War_Scythe_z6yEdFYQJ5IzgTX3.json b/src/packs/items/weapons/weapon_War_Scythe_z6yEdFYQJ5IzgTX3.json index 458d8136..5d65a310 100644 --- a/src/packs/items/weapons/weapon_War_Scythe_z6yEdFYQJ5IzgTX3.json +++ b/src/packs/items/weapons/weapon_War_Scythe_z6yEdFYQJ5IzgTX3.json @@ -16,7 +16,7 @@ { "value": "reliable", "effectIds": [ - "Gt0tHtJDQwdSActw" + "cOYeI9TxHXpDwszu" ], "actionIds": [] } @@ -109,14 +109,21 @@ "img": "icons/skills/melee/strike-sword-slashing-red.webp", "changes": [ { - "key": "system.bonuses.roll.primaryWeapon.attack", + "key": "system.bonuses.roll.primaryWeapon.bonus", "mode": 2, "value": "1" } ], - "_id": "Gt0tHtJDQwdSActw", + "_id": "cOYeI9TxHXpDwszu", "type": "base", - "system": {}, + "system": { + "rangeDependence": { + "enabled": false, + "type": "withinRange", + "target": "hostile", + "range": "melee" + } + }, "disabled": false, "duration": { "startTime": null, @@ -132,14 +139,14 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", - "createdTime": 1753829771677, - "modifiedTime": 1753829771677, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "systemVersion": "1.0.0", + "createdTime": 1754814992368, + "modifiedTime": 1754814992368, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, - "_key": "!items.effects!z6yEdFYQJ5IzgTX3.Gt0tHtJDQwdSActw" + "_key": "!items.effects!z6yEdFYQJ5IzgTX3.cOYeI9TxHXpDwszu" } ], "sort": 0, @@ -152,12 +159,12 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.346", + "coreVersion": "13.347", "systemId": "daggerheart", - "systemVersion": "0.0.1", + "systemVersion": "1.0.0", "createdTime": 1753829740082, - "modifiedTime": 1753829771680, - "lastModifiedBy": "FecEtPuoQh6MpjQ0" + "modifiedTime": 1754814992370, + "lastModifiedBy": "MQSznptE5yLT7kj8" }, "_key": "!items!z6yEdFYQJ5IzgTX3" } From 511d46fe0fafdc9ee9b27a5951810d6a9e1098bd Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 10 Aug 2025 11:12:37 +0200 Subject: [PATCH 18/34] Fixed so the decrease button of simple fear tracker is not visible when not hovered --- styles/less/ui/resources/resources.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/less/ui/resources/resources.less b/styles/less/ui/resources/resources.less index 1f609c5e..3982d990 100644 --- a/styles/less/ui/resources/resources.less +++ b/styles/less/ui/resources/resources.less @@ -108,7 +108,7 @@ box-shadow: unset; border-color: transparent; header, - .controls, + #resource-fear .controls, .window-resize-handle { opacity: 0; } From 52f940bb5171de7998d5e4e6b4f6e05967b2d9ed Mon Sep 17 00:00:00 2001 From: WBHarry Date: Sun, 10 Aug 2025 11:35:18 +0200 Subject: [PATCH 19/34] Fixed so armor preUpdate doesn't fail if no system changes are made --- module/data/item/armor.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index 01e1d186..a8c3dcec 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -60,7 +60,7 @@ export default class DHArmor extends AttachableItem { const allowed = await super._preUpdate(changes, options, user); if (allowed === false) return false; - if (changes.system.armorFeatures) { + if (changes.system?.armorFeatures) { const removed = this.armorFeatures.filter(x => !changes.system.armorFeatures.includes(x)); const added = changes.system.armorFeatures.filter(x => !this.armorFeatures.includes(x)); From c7ec2eb5ac232af46c0cbdd457d827085545c479 Mon Sep 17 00:00:00 2001 From: Chris Ryan <73275196+chrisryan10@users.noreply.github.com> Date: Sun, 10 Aug 2025 21:28:04 +1000 Subject: [PATCH 20/34] Updated .gitignore and author details (#777) * Add author details and name mapping for chrisryan10 (#773) Co-authored-by: Chris Ryan * Add build to ignore for my linux dev (#775) Co-authored-by: Chris Ryan --------- Co-authored-by: Chris Ryan --- .gitignore | 3 ++- system.json | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 61c6b176..264581a4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ node_modules /packs Build +build foundry -styles/daggerheart.css \ No newline at end of file +styles/daggerheart.css diff --git a/system.json b/system.json index cde902c1..a0c91923 100644 --- a/system.json +++ b/system.json @@ -44,6 +44,10 @@ "url": "https://github.com/joaquinpereyra98", "email": "joaquinpereyra98@gmail.com", "discord": "joaquinp98" + }, + { + "name": "chrisryan10", + "discord": "lazjen" } ], "esmodules": ["build/daggerheart.js"], From a038d4c2549b594a5ca0d398f5920dea8a574460 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 10 Aug 2025 14:05:43 +0200 Subject: [PATCH 21/34] Corrected sneak attack active effect (#780) --- src/packs/classes/feature_Sneak_Attack_5QqpEwmwkPfZHpMW.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packs/classes/feature_Sneak_Attack_5QqpEwmwkPfZHpMW.json b/src/packs/classes/feature_Sneak_Attack_5QqpEwmwkPfZHpMW.json index a0e98763..ec203e9a 100644 --- a/src/packs/classes/feature_Sneak_Attack_5QqpEwmwkPfZHpMW.json +++ b/src/packs/classes/feature_Sneak_Attack_5QqpEwmwkPfZHpMW.json @@ -59,13 +59,13 @@ { "key": "system.bonuses.damage.physical.dice", "mode": 2, - "value": "@system.tierd6", + "value": "@tierd6", "priority": null }, { "key": "system.bonuses.damage.magical.dice", "mode": 2, - "value": "@system.tierd6", + "value": "@tierd6", "priority": null } ], From 080b6bc2da6328b416760dc5fb90535110dd4e68 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 10 Aug 2025 14:06:53 +0200 Subject: [PATCH 22/34] Fixed a spelling error (#779) --- module/data/activeEffect/beastformEffect.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/data/activeEffect/beastformEffect.mjs b/module/data/activeEffect/beastformEffect.mjs index e040d8d8..b5cbdb28 100644 --- a/module/data/activeEffect/beastformEffect.mjs +++ b/module/data/activeEffect/beastformEffect.mjs @@ -32,7 +32,7 @@ export default class BeastformEffect extends BaseEffect { if (this.parent.parent?.type === 'character') { this.parent.parent.system.primaryWeapon?.update?.({ 'system.equipped': false }); - this.parent.parent.system.secondayWeapon?.update?.({ 'system.equipped': false }); + this.parent.parent.system.secondaryWeapon?.update?.({ 'system.equipped': false }); } } From df5373cd209d86fef95a9e8d50fe75f29488b0c6 Mon Sep 17 00:00:00 2001 From: Dapoulp <74197441+Dapoulp@users.noreply.github.com> Date: Sun, 10 Aug 2025 16:52:36 +0200 Subject: [PATCH 23/34] Fix bardic rally showing in damage dialog when it should not (#783) --- module/dice/damageRoll.mjs | 35 ++++++++++--------- .../dialogs/dice-roll/damageSelection.hbs | 4 +-- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index 44794faa..8a72d86e 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -159,25 +159,28 @@ export default class DamageRoll extends DHRoll { 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 rallyChoices = 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}`) - ); - } - }; + }, []) + if(rallyChoices.length) { + mods.rally = { + label: 'DAGGERHEART.CLASS.Feature.rallyDice', + values: rallyChoices, + 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); diff --git a/templates/dialogs/dice-roll/damageSelection.hbs b/templates/dialogs/dice-roll/damageSelection.hbs index be49906b..ba542666 100644 --- a/templates/dialogs/dice-roll/damageSelection.hbs +++ b/templates/dialogs/dice-roll/damageSelection.hbs @@ -24,7 +24,7 @@ {{/each}} - {{#if @root.modifiers}} + {{#unless (empty @root.modifiers)}}
    {{localize "DAGGERHEART.GENERAL.Modifier.plural"}} {{#each @root.modifiers}} @@ -39,7 +39,7 @@ {{/if}} {{/each}}
    - {{/if}} + {{/unless}}
    {{#if directDamage}} diff --git a/templates/sheets-settings/companion-settings/attack.hbs b/templates/sheets-settings/companion-settings/attack.hbs index bebe40b3..0ec43d35 100644 --- a/templates/sheets-settings/companion-settings/attack.hbs +++ b/templates/sheets-settings/companion-settings/attack.hbs @@ -5,16 +5,16 @@ >
    {{localize "DAGGERHEART.GENERAL.basics"}} - {{formGroup systemFields.attack.fields.img value=document.system.attack.img label="DAGGERHEART.GENERAL.imagePath" name="system.attack.img" localize=true}} - {{formGroup systemFields.attack.fields.name value=document.system.attack.name label="DAGGERHEART.ACTIONS.Settings.attackName" name="system.attack.name" localize=true}} + {{formGroup systemFields.attack.fields.img value=document._source.system.attack.img label="DAGGERHEART.GENERAL.imagePath" name="system.attack.img" localize=true}} + {{formGroup systemFields.attack.fields.name value=document._source.system.attack.name label="DAGGERHEART.ACTIONS.Settings.attackName" name="system.attack.name" localize=true}}
    {{localize "DAGGERHEART.GENERAL.attack"}} - {{formField systemFields.attack.fields.range value=document.system.attack.range label="DAGGERHEART.GENERAL.range" name="system.attack.range" localize=true}} + {{formField systemFields.attack.fields.range value=document._source.system.attack.range label="DAGGERHEART.GENERAL.range" name="system.attack.range" localize=true}} {{#if systemFields.attack.fields.target.fields}} - {{ formField systemFields.attack.fields.target.fields.type value=document.system.attack.target.type label="DAGGERHEART.GENERAL.Target.single" name="system.attack.target.type" localize=true}} - {{#if (and document.system.attack.target.type (not (eq document.system.attack.target.type 'self')))}} - {{ formField systemFields.attack.fields.target.fields.amount value=document.system.attack.target.amount label="DAGGERHEART.GENERAL.amount" name="system.attack.target.amount" localize=true}} + {{ formField systemFields.attack.fields.target.fields.type value=document._source.system.attack.target.type label="DAGGERHEART.GENERAL.Target.single" name="system.attack.target.type" localize=true}} + {{#if (and document._source.system.attack.target.type (not (eq document._source.system.attack.target.type 'self')))}} + {{ formField systemFields.attack.fields.target.fields.amount value=document._source.system.attack.target.amount label="DAGGERHEART.GENERAL.amount" name="system.attack.target.amount" localize=true}} {{/if}} {{/if}}
    diff --git a/templates/sheets-settings/companion-settings/details.hbs b/templates/sheets-settings/companion-settings/details.hbs index 4f1825d8..e4293443 100644 --- a/templates/sheets-settings/companion-settings/details.hbs +++ b/templates/sheets-settings/companion-settings/details.hbs @@ -6,18 +6,18 @@
    {{localize 'DAGGERHEART.GENERAL.basics'}}
    - {{formGroup systemFields.evasion value=document.system.evasion localize=true}} - {{formGroup systemFields.resources.fields.stress.fields.value value=document.system.resources.stress.value label='DAGGERHEART.ACTORS.Companion.FIELDS.resources.stress.currentStress.label' localize=true}} - {{formGroup systemFields.resources.fields.stress.fields.max value=document.system.resources.stress.max label='DAGGERHEART.ACTORS.Companion.FIELDS.resources.stress.maxStress.label' localize=true}} + {{formGroup systemFields.evasion value=document._source.system.evasion localize=true}} + {{formGroup systemFields.resources.fields.stress.fields.value value=document._source.system.resources.stress.value label='DAGGERHEART.ACTORS.Companion.FIELDS.resources.stress.currentStress.label' localize=true}} + {{formGroup systemFields.resources.fields.stress.fields.max value=document._source.system.resources.stress.max label='DAGGERHEART.ACTORS.Companion.FIELDS.resources.stress.maxStress.label' localize=true}}
    - + \ No newline at end of file diff --git a/templates/sheets-settings/environment-settings/details.hbs b/templates/sheets-settings/environment-settings/details.hbs index 1f8c45f2..ecb47ca8 100644 --- a/templates/sheets-settings/environment-settings/details.hbs +++ b/templates/sheets-settings/environment-settings/details.hbs @@ -6,11 +6,11 @@
    {{localize 'DAGGERHEART.GENERAL.basics'}}
    - {{formGroup systemFields.tier value=document.system.tier localize=true}} - {{formGroup systemFields.type value=document.system.type localize=true}} - {{formGroup systemFields.difficulty value=document.system.difficulty localize=true}} + {{formGroup systemFields.tier value=document._source.system.tier localize=true}} + {{formGroup systemFields.type value=document._source.system.type localize=true}} + {{formGroup systemFields.difficulty value=document._source.system.difficulty localize=true}}
    - {{formField systemFields.description value=document.system.description label=(localize "DAGGERHEART.ACTORS.Environment.FIELDS.description.label")}} - {{formField systemFields.impulses value=document.system.impulses label=(localize "DAGGERHEART.ACTORS.Environment.FIELDS.impulses.label")}} + {{formField systemFields.description value=document._source.system.description label=(localize "DAGGERHEART.ACTORS.Environment.FIELDS.description.label")}} + {{formField systemFields.impulses value=document._source.system.impulses label=(localize "DAGGERHEART.ACTORS.Environment.FIELDS.impulses.label")}}
    \ No newline at end of file From f81d0d250fa939eb29da68dfe989598ea116b585 Mon Sep 17 00:00:00 2001 From: "Josh Q." Date: Sun, 10 Aug 2025 14:18:01 -0400 Subject: [PATCH 31/34] Dragging features from one adversary to another (#788) --- lang/en.json | 1 + .../sheets-configs/adversary-settings.mjs | 14 ++++++++++---- .../adversary-settings/features.less | 19 ++++++++++++++++++- .../adversary-settings/features.hbs | 5 ++++- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/lang/en.json b/lang/en.json index 11064dab..f35e7a66 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1899,6 +1899,7 @@ "difficulty": "Difficulty", "downtime": "Downtime", "dropActorsHere": "Drop Actors here", + "dropFeaturesHere": "Drop Features here", "duality": "Duality", "dualityRoll": "Duality Roll", "enabled": "Enabled", diff --git a/module/applications/sheets-configs/adversary-settings.mjs b/module/applications/sheets-configs/adversary-settings.mjs index 57deea25..bcc8b1c9 100644 --- a/module/applications/sheets-configs/adversary-settings.mjs +++ b/module/applications/sheets-configs/adversary-settings.mjs @@ -98,11 +98,17 @@ export default class DHAdversarySettings extends DHBaseActorSettings { async _onDrop(event) { const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); - if (data.fromInternal) return; - + const item = await fromUuid(data.uuid); - if (item.type === 'feature') { - await this.actor.createEmbeddedDocuments('Item', [item]); + if (item?.type === 'feature') { + if (data.fromInternal && item.parent?.uuid === this.actor.uuid) { + return; + } + + const itemData = item.toObject(); + delete itemData._id; + + await this.actor.createEmbeddedDocuments('Item', [itemData]); } } } diff --git a/styles/less/sheets-settings/adversary-settings/features.less b/styles/less/sheets-settings/adversary-settings/features.less index 037a08ea..29c9874e 100644 --- a/styles/less/sheets-settings/adversary-settings/features.less +++ b/styles/less/sheets-settings/adversary-settings/features.less @@ -14,8 +14,14 @@ margin-bottom: 12px; } - .feature-list { + .feature-list, + .features-dragger { display: flex; + width: 100%; + font-family: @font-body; + } + + .feature-list { flex-direction: column; gap: 10px; @@ -45,5 +51,16 @@ } } } + + .features-dragger { + align-items: center; + justify-content: center; + box-sizing: border-box; + height: 40px; + margin-top: 10px; + border: 1px dashed light-dark(@dark-blue-50, @beige-50); + border-radius: 3px; + color: light-dark(@dark-blue-50, @beige-50); + } } } diff --git a/templates/sheets-settings/adversary-settings/features.hbs b/templates/sheets-settings/adversary-settings/features.hbs index a287c2a8..b26fa10c 100644 --- a/templates/sheets-settings/adversary-settings/features.hbs +++ b/templates/sheets-settings/adversary-settings/features.hbs @@ -10,7 +10,7 @@ {{localize tabs.features.label}}
      {{#each document.system.features as |feature|}} -
    • +
    • {{feature.name}} @@ -22,5 +22,8 @@
    • {{/each}}
    +
    + {{localize "DAGGERHEART.GENERAL.dropFeaturesHere"}} +
    \ No newline at end of file From b470a1dc513ee71c6aa0c2b0c56f7c46331e77f5 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 10 Aug 2025 20:19:37 +0200 Subject: [PATCH 32/34] [Fix] Levelup Fixes (#787) * Fixed crash on experience selection. Fixed subclass error on multiclassing * Fixed so multiclasses do not gain the hope feature for the class * Fixed so Class/Subclass features are properly deleted on delevel * Removed automatic deletion of features on delevel when not using levelup auto * Fixed so custom domains can be selected in levelup when multiclassing --- .../applications/levelup/characterLevelup.mjs | 2 +- .../applications/levelup/companionLevelup.mjs | 2 +- module/data/actor/character.mjs | 11 ++ module/data/item/class.mjs | 11 -- module/data/item/subclass.mjs | 5 +- module/documents/actor.mjs | 144 ++++++++++-------- module/helpers/utils.mjs | 13 +- templates/sheets/actors/character/header.hbs | 2 +- 8 files changed, 102 insertions(+), 88 deletions(-) diff --git a/module/applications/levelup/characterLevelup.mjs b/module/applications/levelup/characterLevelup.mjs index de28f241..0ae136c4 100644 --- a/module/applications/levelup/characterLevelup.mjs +++ b/module/applications/levelup/characterLevelup.mjs @@ -51,7 +51,7 @@ export default class DhCharacterLevelUp extends LevelUpBase { .filter(exp => exp.data.length > 0) .flatMap(exp => exp.data.map(data => { - const experience = Object.keys(this.actor.system.experiences).find(x => x === data); + const experience = Object.keys(this.actor.system.experiences)[data]; return this.actor.system.experiences[experience].name; }) ); diff --git a/module/applications/levelup/companionLevelup.mjs b/module/applications/levelup/companionLevelup.mjs index 2fcc42a0..c94d7d2b 100644 --- a/module/applications/levelup/companionLevelup.mjs +++ b/module/applications/levelup/companionLevelup.mjs @@ -39,7 +39,7 @@ export default class DhCompanionLevelUp extends BaseLevelUp { .filter(exp => exp.data.length > 0) .flatMap(exp => exp.data.map(data => { - const experience = Object.keys(this.actor.system.experiences).find(x => x === data); + const experience = Object.keys(this.actor.system.experiences)[data]; return this.actor.system.experiences[experience].name; }) ); diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index c40e7e5d..c5e250a4 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -351,6 +351,17 @@ export default class DhCharacter extends BaseDataActor { return [...classDomains, ...multiclassDomains]; } + get domainData() { + const allDomainData = CONFIG.DH.DOMAIN.allDomains(); + return this.domains.map(key => { + const domain = allDomainData[key]; + return { + ...domain, + label: game.i18n.localize(domain.label) + }; + }); + } + get domainCards() { const domainCards = this.parent.items.filter(x => x.type === 'domainCard'); const loadout = domainCards.filter(x => !x.system.inVault); diff --git a/module/data/item/class.mjs b/module/data/item/class.mjs index cee17613..45e8b4ab 100644 --- a/module/data/item/class.mjs +++ b/module/data/item/class.mjs @@ -60,17 +60,6 @@ export default class DHClass extends BaseDataItem { /* -------------------------------------------- */ - get domainData() { - const allDomainData = CONFIG.DH.DOMAIN.allDomains(); - return this.domains.map(key => { - const domain = allDomainData[key]; - return { - ...domain, - label: game.i18n.localize(domain.label) - }; - }); - } - get hopeFeatures() { return this.features.filter(x => x.type === CONFIG.DH.ITEM.featureSubTypes.hope).map(x => x.item); } diff --git a/module/data/item/subclass.mjs b/module/data/item/subclass.mjs index ce52fdc6..221785b3 100644 --- a/module/data/item/subclass.mjs +++ b/module/data/item/subclass.mjs @@ -21,7 +21,7 @@ export default class DHSubclass extends BaseDataItem { integer: false, nullable: true, initial: null, - label: "DAGGERHEART.ITEMS.Subclass.spellcastingTrait" + label: 'DAGGERHEART.ITEMS.Subclass.spellcastingTrait' }), features: new ItemLinkFields(), featureState: new fields.NumberField({ required: true, initial: 1, min: 1 }), @@ -50,7 +50,8 @@ export default class DHSubclass extends BaseDataItem { async _preCreate(data, options, user) { if (this.actor?.type === 'character') { - const dataUuid = data.uuid ?? data._stats?.compendiumSource ?? `Item.${data._id}`; + const dataUuid = + (data.uuid ?? data.folder) ? `Compendium.daggerheart.subclasses.Item.${data._id}` : `Item.${data._id}`; if (this.actor.system.class.subclass) { if (this.actor.system.multiclass.subclass) { ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.subclassesAlreadyPresent')); diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 8fc9b74a..c75db559 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -84,6 +84,8 @@ export default class DhpActor extends Actor { await this.update({ 'system.levelData.level.changed': Math.min(newLevel, maxLevel) }); } else { + const levelupAuto = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).levelupAuto; + const usedLevel = Math.max(newLevel, 1); if (newLevel < 1) { ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.tooLowLevel')); @@ -95,79 +97,90 @@ export default class DhpActor extends Actor { return acc; }, {}); - const features = []; - const domainCards = []; - const experiences = []; - const subclassFeatureState = { class: null, multiclass: null }; - let multiclass = null; - Object.keys(this.system.levelData.levelups) - .filter(x => x > usedLevel) - .forEach(levelKey => { - const level = this.system.levelData.levelups[levelKey]; - const achievementCards = level.achievements.domainCards.map(x => x.itemUuid); - const advancementCards = level.selections.filter(x => x.type === 'domainCard').map(x => x.itemUuid); - domainCards.push(...achievementCards, ...advancementCards); - experiences.push(...Object.keys(level.achievements.experiences)); - features.push(...level.selections.flatMap(x => x.features)); + if (levelupAuto) { + const features = []; + const domainCards = []; + const experiences = []; + const subclassFeatureState = { class: null, multiclass: null }; + let multiclass = null; + Object.keys(this.system.levelData.levelups) + .filter(x => x > usedLevel) + .forEach(levelKey => { + const level = this.system.levelData.levelups[levelKey]; + const achievementCards = level.achievements.domainCards.map(x => x.itemUuid); + const advancementCards = level.selections + .filter(x => x.type === 'domainCard') + .map(x => x.itemUuid); + domainCards.push(...achievementCards, ...advancementCards); + experiences.push(...Object.keys(level.achievements.experiences)); + features.push(...level.selections.flatMap(x => x.features)); - const subclass = level.selections.find(x => x.type === 'subclass'); - if (subclass) { - const path = subclass.secondaryData.isMulticlass === 'true' ? 'multiclass' : 'class'; - const subclassState = Number(subclass.secondaryData.featureState) - 1; - subclassFeatureState[path] = subclassFeatureState[path] - ? Math.min(subclassState, subclassFeatureState[path]) - : subclassState; - } + const subclass = level.selections.find(x => x.type === 'subclass'); + if (subclass) { + const path = subclass.secondaryData.isMulticlass === 'true' ? 'multiclass' : 'class'; + const subclassState = Number(subclass.secondaryData.featureState) - 1; + subclassFeatureState[path] = subclassFeatureState[path] + ? Math.min(subclassState, subclassFeatureState[path]) + : subclassState; + } - multiclass = level.selections.find(x => x.type === 'multiclass'); - }); + multiclass = level.selections.find(x => x.type === 'multiclass'); + }); - for (let feature of features) { - if (feature.onPartner && !this.system.partner) continue; + for (let feature of features) { + if (feature.onPartner && !this.system.partner) continue; - const document = feature.onPartner ? this.system.partner : this; - document.items.get(feature.id)?.delete(); - } - - if (experiences.length > 0) { - const getUpdate = () => ({ - 'system.experiences': experiences.reduce((acc, key) => { - acc[`-=${key}`] = null; - return acc; - }, {}) - }); - this.update(getUpdate()); - if (this.system.companion) { - this.system.companion.update(getUpdate()); + const document = feature.onPartner ? this.system.partner : this; + document.items.get(feature.id)?.delete(); } - } - if (subclassFeatureState.class) { - this.system.class.subclass.update({ 'system.featureState': subclassFeatureState.class }); - } - - if (subclassFeatureState.multiclass) { - this.system.multiclass.subclass.update({ 'system.featureState': subclassFeatureState.multiclass }); - } - - if (multiclass) { - const multiclassSubclass = this.items.find(x => x.type === 'subclass' && x.system.isMulticlass); - const multiclassItem = this.items.find(x => x.uuid === multiclass.itemUuid); - - multiclassSubclass.delete(); - multiclassItem.delete(); - - this.update({ - 'system.multiclass': { - value: null, - subclass: null + if (experiences.length > 0) { + const getUpdate = () => ({ + 'system.experiences': experiences.reduce((acc, key) => { + acc[`-=${key}`] = null; + return acc; + }, {}) + }); + this.update(getUpdate()); + if (this.system.companion) { + this.system.companion.update(getUpdate()); } - }); - } + } - for (let domainCard of domainCards) { - const itemCard = this.items.find(x => x.uuid === domainCard); - itemCard.delete(); + if (subclassFeatureState.class) { + this.system.class.subclass.update({ 'system.featureState': subclassFeatureState.class }); + } + + if (subclassFeatureState.multiclass) { + this.system.multiclass.subclass.update({ 'system.featureState': subclassFeatureState.multiclass }); + } + + if (multiclass) { + const multiclassItem = this.items.find(x => x.uuid === multiclass.itemUuid); + const multiclassFeatures = this.items.filter( + x => x.system.originItemType === 'class' && x.system.identifier === 'multiclass' + ); + const subclassFeatures = this.items.filter( + x => x.system.originItemType === 'subclass' && x.system.identifier === 'multiclass' + ); + + this.deleteEmbeddedDocuments( + 'Item', + [multiclassItem, ...multiclassFeatures, ...subclassFeatures].map(x => x.id) + ); + + this.update({ + 'system.multiclass': { + value: null, + subclass: null + } + }); + } + + for (let domainCard of domainCards) { + const itemCard = this.items.find(x => x.uuid === domainCard); + itemCard.delete(); + } } await this.update({ @@ -315,6 +328,7 @@ export default class DhpActor extends Actor { ...multiclassData, system: { ...multiclassData.system, + features: multiclassData.system.features.filter(x => x.type !== 'hope'), domains: [multiclass.secondaryData.domain], isMulticlass: true } diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 3dc1f7ed..63507782 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -85,13 +85,12 @@ export const chunkify = (array, chunkSize, mappingFunc) => { export const tagifyElement = (element, baseOptions, onChange, tagifyOptions = {}) => { const { maxTags } = tagifyOptions; - const options = - typeof baseOptions === 'object' - ? Object.keys(baseOptions).map(optionKey => ({ - ...baseOptions[optionKey], - id: optionKey - })) - : baseOptions; + const options = Array.isArray(baseOptions) + ? baseOptions + : Object.keys(baseOptions).map(optionKey => ({ + ...baseOptions[optionKey], + id: optionKey + })); const tagifyElement = new Tagify(element, { tagTextProp: 'name', diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index 7f598f55..9f2d56a1 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -87,7 +87,7 @@
    {{#if document.system.class.value}}
    - {{#each document.system.class.value.system.domainData as |data|}} + {{#each document.system.domainData as |data|}}
    From 10b871d4c348362a8e7b2041d9a15be281917bc9 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 10 Aug 2025 21:07:09 +0200 Subject: [PATCH 33/34] Changed so encounter countdowns is a button (#804) --- lang/en.json | 5 ++- .../ui/combat-sidebar/encounter-controls.less | 41 +++---------------- .../ui/combatTracker/combatTrackerHeader.hbs | 31 +++++--------- 3 files changed, 19 insertions(+), 58 deletions(-) diff --git a/lang/en.json b/lang/en.json index f35e7a66..d182a77c 100755 --- a/lang/en.json +++ b/lang/en.json @@ -272,7 +272,8 @@ "combatStarted": "Active", "giveSpotlight": "Give The Spotlight", "requestingSpotlight": "Requesting The Spotlight", - "requestSpotlight": "Request The Spotlight" + "requestSpotlight": "Request The Spotlight", + "openCountdowns": "Countdowns" }, "ContextMenu": { "disableEffect": "Disable Effect", @@ -1899,7 +1900,7 @@ "difficulty": "Difficulty", "downtime": "Downtime", "dropActorsHere": "Drop Actors here", - "dropFeaturesHere": "Drop Features here", + "dropFeaturesHere": "Drop Features here", "duality": "Duality", "dualityRoll": "Duality Roll", "enabled": "Enabled", diff --git a/styles/less/ui/combat-sidebar/encounter-controls.less b/styles/less/ui/combat-sidebar/encounter-controls.less index 58deaae8..fd0c1aee 100644 --- a/styles/less/ui/combat-sidebar/encounter-controls.less +++ b/styles/less/ui/combat-sidebar/encounter-controls.less @@ -2,43 +2,14 @@ .encounter-controls.combat { justify-content: space-between; - .encounter-fear-controls { + .encounter-title { + text-align: left; + } + + .inner-controls { display: flex; align-items: center; - gap: 8px; - - .encounter-fear-dice-container { - display: flex; - gap: 2px; - - .encounter-control-fear-container { - display: flex; - position: relative; - align-items: center; - justify-content: center; - color: black; - - .dice { - height: 22px; - width: 22px; - } - - .encounter-control-fear { - position: absolute; - font-size: 16px; - } - - .encounter-control-counter { - position: absolute; - right: -10px; - color: var(--color-text-secondary); - } - } - } - - .encounter-countdowns { - color: var(--content-link-icon-color); - } + gap: 4px; } .control-buttons { diff --git a/templates/ui/combatTracker/combatTrackerHeader.hbs b/templates/ui/combatTracker/combatTrackerHeader.hbs index de666168..9ecff9e6 100644 --- a/templates/ui/combatTracker/combatTrackerHeader.hbs +++ b/templates/ui/combatTracker/combatTrackerHeader.hbs @@ -51,19 +51,6 @@ {{/if}}
    - {{#if hasCombat}} -
    -
    -
    - - -
    -
    {{fear}}
    -
    - -
    - {{/if}} - {{!-- Combat Status --}} {{#if combats.length}} @@ -78,14 +65,16 @@ {{!-- Combat Controls --}} - {{#if hasCombat}} -
    -
    - -
    - {{/if}} - +
    + {{#if hasCombat}} + +
    +
    + +
    + {{/if}} +
    From 4603e7e40ce3d4aa955e883e0581af43471196f6 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 10 Aug 2025 21:08:08 +0200 Subject: [PATCH 34/34] Fixed so that dropping on class/subclass...creates the item on the character (#803) --- .../sheets/api/application-mixin.mjs | 1 + module/applications/sheets/api/base-item.mjs | 66 ++++++++++++++----- 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 315ae3cb..18a5ac91 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -129,6 +129,7 @@ export default function DHApplicationMixin(Base) { const docs = []; for (const docData of this.relatedDocs) { + if (!docData) continue; const doc = await foundry.utils.fromUuid(docData.uuid); docs.push(doc); } diff --git a/module/applications/sheets/api/base-item.mjs b/module/applications/sheets/api/base-item.mjs index 6bd91ae8..a9d3237d 100644 --- a/module/applications/sheets/api/base-item.mjs +++ b/module/applications/sheets/api/base-item.mjs @@ -181,12 +181,18 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { static async #deleteFeature(_, element) { const target = element.closest('[data-item-uuid]'); const feature = await getDocFromElement(target); - if (!feature) return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing')); - await this.document.update({ - 'system.features': this.document.system.features - .filter(x => target.dataset.type !== x.type || x.item.uuid !== feature.uuid) - .map(x => ({ ...x, item: x.item.uuid })) - }); + if (!feature) { + await this.document.update({ + 'system.features': this.document.system.features + .filter(x => x.item) + .map(x => ({ ...x, item: x.item.uuid })) + }); + } else + await this.document.update({ + 'system.features': this.document.system.features + .filter(x => target.dataset.type !== x.type || x.item.uuid !== feature.uuid) + .map(x => ({ ...x, item: x.item.uuid })) + }); } /** @@ -259,21 +265,45 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { if (data.fromInternal) return; const target = event.target.closest('fieldset.drop-section'); - const item = await fromUuid(data.uuid); + let item = await fromUuid(data.uuid); if (item?.type === 'feature') { + const cls = foundry.documents.Item.implementation; + + if (this.document.parent?.type === 'character') { + const itemData = item.toObject(); + item = await cls.create( + { + ...itemData, + system: { + ...itemData.system, + originItemType: this.document.type, + originId: this.document.id, + identifier: this.document.system.isMulticlass ? 'multiclass' : null + } + }, + { parent: this.document.parent } + ); + } + if (target.dataset.type) { - await this.document.update({ - 'system.features': [...this.document.system.features, { type: target.dataset.type, item }].map( - x => ({ - ...x, - item: x.item?.uuid - }) - ) - }); + await this.document.update( + { + 'system.features': [...this.document.system.features, { type: target.dataset.type, item }].map( + x => ({ + ...x, + item: x.item?.uuid + }) + ) + }, + { parent: this.document.parent?.type === 'character' ? this.document.parent : undefined } + ); } else { - await this.document.update({ - 'system.features': [...this.document.system.features, item].map(x => x.uuid) - }); + await this.document.update( + { + 'system.features': [...this.document.system.features, item].map(x => x.uuid) + }, + { parent: this.document.parent?.type === 'character' ? this.document.parent : undefined } + ); } } }