From 08b95e48d6190b1c1853009104410f0cf13f2133 Mon Sep 17 00:00:00 2001 From: WBHarry <89362246+WBHarry@users.noreply.github.com> Date: Sun, 21 Jun 2026 04:50:09 +0200 Subject: [PATCH 1/3] [Fix] TagTag/GroupRoll Resource Handling (#2022) --- .../applications/dialogs/groupRollDialog.mjs | 28 ++++--- module/applications/dialogs/tagTeamDialog.mjs | 73 ++++++++++++------- module/dice/dualityRoll.mjs | 6 +- module/helpers/utils.mjs | 5 ++ .../tag-team-dialog/initialization.less | 14 +++- .../dialogs/tagTeamDialog/initialization.hbs | 13 +++- 6 files changed, 92 insertions(+), 47 deletions(-) diff --git a/module/applications/dialogs/groupRollDialog.mjs b/module/applications/dialogs/groupRollDialog.mjs index 58ed03b4..ebc80d39 100644 --- a/module/applications/dialogs/groupRollDialog.mjs +++ b/module/applications/dialogs/groupRollDialog.mjs @@ -1,4 +1,5 @@ import { ResourceUpdateMap } from '../../data/action/baseAction.mjs'; +import { shouldUseHopeFearAutomation } from '../../helpers/utils.mjs'; import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; import Party from '../sheets/actors/party.mjs'; @@ -480,19 +481,22 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat await cls.create(msgData); - const resourceMap = new ResourceUpdateMap(actor); - if (totalRoll.isCritical) { - resourceMap.addResources([ - { key: 'stress', value: -1 }, - { key: 'hope', value: 1 } - ]); - } else if (totalRoll.withHope) { - resourceMap.addResources([{ key: 'hope', value: 1 }]); - } else { - resourceMap.addResources([{ key: 'fear', value: 1 }]); - } + /* Handle resource updates for the finished GroupRoll */ + if (shouldUseHopeFearAutomation({ gmAsPlayer: true })) { + const resourceMap = new ResourceUpdateMap(actor); + if (totalRoll.isCritical) { + resourceMap.addResources([ + { key: 'stress', value: -1 }, + { key: 'hope', value: 1 } + ]); + } else if (totalRoll.withHope) { + resourceMap.addResources([{ key: 'hope', value: 1 }]); + } else { + resourceMap.addResources([{ key: 'fear', value: 1 }]); + } - resourceMap.updateResources(); + resourceMap.updateResources(); + } /* Fin */ this.cancelRoll({ confirm: false }); diff --git a/module/applications/dialogs/tagTeamDialog.mjs b/module/applications/dialogs/tagTeamDialog.mjs index 5c83f075..19869a00 100644 --- a/module/applications/dialogs/tagTeamDialog.mjs +++ b/module/applications/dialogs/tagTeamDialog.mjs @@ -1,5 +1,6 @@ +import { ResourceUpdateMap } from '../../data/action/baseAction.mjs'; import { MemberData } from '../../data/tagTeamData.mjs'; -import { getCritDamageBonus } from '../../helpers/utils.mjs'; +import { getCritDamageBonus, shouldUseHopeFearAutomation } from '../../helpers/utils.mjs'; import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; import Party from '../sheets/actors/party.mjs'; @@ -9,6 +10,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio constructor(party) { super({ id: `TagTeamDialog-${party.id}` }); + this.usesTagTeamHopeCost = true; this.party = party; this.partyMembers = party.system.partyMembers .filter(x => Party.DICE_ROLL_ACTOR_TYPES.includes(x.type)) @@ -20,7 +22,9 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio owned: member.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) })); - this.initiator = { cost: 3 }; + this.initiator = { cost: + this.party.system.schema.fields.tagTeam.fields.initiator.fields.cost.initial + }; this.openForAllPlayers = true; this.tabGroups.application = Object.keys(party.system.tagTeam.members).length @@ -94,9 +98,13 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio ?.addEventListener('input', this.updateInitiatorMemberField.bind(this)); htmlElement - .querySelector('.initiator-cost-field') + .querySelector('.initiator-cost-input') ?.addEventListener('input', this.updateInitiatorCostField.bind(this)); + htmlElement + .querySelector('.initiator-cost-enabled-checkbox') + ?.addEventListener('change', this.toggleInitiatorCostEnabled.bind(this)); + htmlElement .querySelector('.openforall-field') ?.addEventListener('change', this.updateOpenForAllField.bind(this)); @@ -156,6 +164,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio .map(x => ({ value: x.id, label: x.name })); partContext.initiatorDisabled = !selectedMembers.length; partContext.openForAllPlayers = this.openForAllPlayers; + partContext.usesTagTeamHopeCost = this.usesTagTeamHopeCost; break; case 'tagTeamRoll': @@ -397,6 +406,13 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio this.render(); } + toggleInitiatorCostEnabled(_event) { + this.usesTagTeamHopeCost = !this.usesTagTeamHopeCost; + this.initiator.cost = this.usesTagTeamHopeCost ? + this.party.system.schema.fields.tagTeam.fields.initiator.fields.cost.initial : 0; + this.render(); + } + updateOpenForAllField(event) { this.openForAllPlayers = event.target.checked; this.render(); @@ -752,32 +768,37 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio /* Handle resource updates from the finished TagTeamRoll */ const tagTeamData = this.party.system.tagTeam; - const fearUpdate = { key: 'fear', value: null, enabled: true }; - for (let memberId in tagTeamData.members) { - const resourceUpdates = []; - const rollGivesHope = finalRoll.isCritical || finalRoll.withHope; - if (memberId === tagTeamData.initiator.memberId) { - const value = tagTeamData.initiator.cost - ? rollGivesHope - ? 1 - tagTeamData.initiator.cost - : -tagTeamData.initiator.cost - : 1; - resourceUpdates.push({ key: 'hope', value: value, enabled: true }); - } else if (rollGivesHope) { - resourceUpdates.push({ key: 'hope', value: 1, enabled: true }); - } - if (finalRoll.isCritical) resourceUpdates.push({ key: 'stress', value: -1, enabled: true }); - if (finalRoll.withFear) { - fearUpdate.value = fearUpdate.value === null ? 1 : fearUpdate.value + 1; - fearUpdate.total = fearUpdate.total === null ? -1 : fearUpdate.total - 1; - } - game.actors.get(memberId).modifyResource(resourceUpdates); + const actorResourceMaps = Object.keys(tagTeamData.members).reduce((acc, key) => { + acc[key] = new ResourceUpdateMap(game.actors.get(key)); + return acc; + }, {}); + + if (shouldUseHopeFearAutomation({ gmAsPlayer: true })) { + const fearResourceMap = actorResourceMaps[tagTeamData.initiator.memberId]; + for (const memberId in tagTeamData.members) { + const resourceMap = actorResourceMaps[memberId]; + if (finalRoll.isCritical) { + resourceMap.addResources([ + { key: 'stress', value: -1, enabled: true }, + { key: 'hope', value: 1, enabled: true } + ]); + } else if (finalRoll.withHope) { + resourceMap.addResources([{ key: 'hope', value: 1, enabled: true }]); + } else if (finalRoll.withFear) { + fearResourceMap.addResources([{ key: 'fear', value: 1, enabled: true }]); + } + } + } + + /* Even with Hope/Fear automation off, the hope cost of performing the TagTeamRoll can still optionally be subtracted */ + if (tagTeamData.initiator.cost) { + const resourceMap = actorResourceMaps[tagTeamData.initiator.memberId]; + resourceMap.addResources([{ key: 'hope', value: -tagTeamData.initiator.cost, enabled: true }]); } - if (fearUpdate.value) { - mainActor.modifyResource([fearUpdate]); - } + for (const resourceMap of Object.values(actorResourceMaps)) + resourceMap.updateResources(); /* Fin */ this.cancelRoll({ confirm: false }); diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index 70e98242..e2d967a3 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -1,6 +1,6 @@ import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs'; import D20Roll from './d20Roll.mjs'; -import { parseRallyDice, setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs'; +import { parseRallyDice, setDiceSoNiceForDualityRoll, shouldUseHopeFearAutomation } from '../helpers/utils.mjs'; import { getDiceSoNicePresets } from '../config/generalConfig.mjs'; import { updateResourcesForDualityReroll } from './helpers.mjs'; @@ -312,11 +312,9 @@ export default class DualityRoll extends D20Roll { } static async addDualityResourceUpdates(config) { - const automationSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation); - const hopeFearAutomation = automationSettings.hopeFear; if ( !config.source?.actor || - (game.user.isGM ? !hopeFearAutomation.gm : !hopeFearAutomation.players) || + !shouldUseHopeFearAutomation() || config.actionType === 'reaction' || config.skips?.resources ) diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 3c5192be..6467edd7 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -885,3 +885,8 @@ export async function triggerChatRollFx(rolls, options = { whisper: false, blind foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice }); } } + +export function shouldUseHopeFearAutomation(options = { gmAsPlayer: true }) { + const { hopeFear } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation); + return (!game.user.isGM || options.gmAsPlayer) ? hopeFear.players : hopeFear.gm; +} \ No newline at end of file diff --git a/styles/less/dialog/tag-team-dialog/initialization.less b/styles/less/dialog/tag-team-dialog/initialization.less index d6f7ad29..e79ed65c 100644 --- a/styles/less/dialog/tag-team-dialog/initialization.less +++ b/styles/less/dialog/tag-team-dialog/initialization.less @@ -88,9 +88,21 @@ grid-template-columns: 1fr 1fr; gap: 8px; - &.inactive { + .inactive { opacity: 0.4; } + + .initiator-cost-fields { + display: flex; + flex-direction: column; + align-items: flex-start; + + .initiator-cost-inputs { + display: grid; + grid-template-columns: auto 1fr; + align-items: center; + } + } } footer { diff --git a/templates/dialogs/tagTeamDialog/initialization.hbs b/templates/dialogs/tagTeamDialog/initialization.hbs index 0b92e68e..40491e1b 100644 --- a/templates/dialogs/tagTeamDialog/initialization.hbs +++ b/templates/dialogs/tagTeamDialog/initialization.hbs @@ -26,11 +26,16 @@ -
+
-
- -
+
+ + +
From 7237a3651d168338c47f9acf9bd4c56e72cf7d5d Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Sun, 21 Jun 2026 05:29:07 -0400 Subject: [PATCH 2/3] Fix svg sizing on firefox (#2027) --- assets/icons/documents/actors/capybara.svg | 2 +- assets/icons/documents/actors/dark-squad.svg | 2 +- assets/icons/documents/actors/dragon-head.svg | 2 +- assets/icons/documents/actors/drama-masks.svg | 2 +- assets/icons/documents/actors/forest.svg | 2 +- assets/icons/documents/items/battered-axe.svg | 2 +- assets/icons/documents/items/card-play.svg | 2 +- assets/icons/documents/items/chest-armor.svg | 2 +- assets/icons/documents/items/family-tree.svg | 2 +- assets/icons/documents/items/laurel-crown.svg | 2 +- assets/icons/documents/items/laurels.svg | 2 +- assets/icons/documents/items/open-treasure-chest.svg | 2 +- assets/icons/documents/items/round-potion.svg | 2 +- assets/icons/documents/items/stars-stack.svg | 2 +- assets/icons/documents/items/village.svg | 2 +- assets/icons/documents/items/wolf-head.svg | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/assets/icons/documents/actors/capybara.svg b/assets/icons/documents/actors/capybara.svg index 90deb64a..8fbe2c06 100644 --- a/assets/icons/documents/actors/capybara.svg +++ b/assets/icons/documents/actors/capybara.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/actors/dark-squad.svg b/assets/icons/documents/actors/dark-squad.svg index f21b4c5b..39ca794a 100644 --- a/assets/icons/documents/actors/dark-squad.svg +++ b/assets/icons/documents/actors/dark-squad.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/actors/dragon-head.svg b/assets/icons/documents/actors/dragon-head.svg index d9e008f5..324ffbbb 100644 --- a/assets/icons/documents/actors/dragon-head.svg +++ b/assets/icons/documents/actors/dragon-head.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/actors/drama-masks.svg b/assets/icons/documents/actors/drama-masks.svg index 84307da0..98b9b68b 100644 --- a/assets/icons/documents/actors/drama-masks.svg +++ b/assets/icons/documents/actors/drama-masks.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/actors/forest.svg b/assets/icons/documents/actors/forest.svg index 8f7117e8..fcdb8a9d 100644 --- a/assets/icons/documents/actors/forest.svg +++ b/assets/icons/documents/actors/forest.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/battered-axe.svg b/assets/icons/documents/items/battered-axe.svg index 5d7be27d..436884bd 100644 --- a/assets/icons/documents/items/battered-axe.svg +++ b/assets/icons/documents/items/battered-axe.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/card-play.svg b/assets/icons/documents/items/card-play.svg index 587cb1c1..5c29fc43 100644 --- a/assets/icons/documents/items/card-play.svg +++ b/assets/icons/documents/items/card-play.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/chest-armor.svg b/assets/icons/documents/items/chest-armor.svg index 2cef80a6..03b82d48 100644 --- a/assets/icons/documents/items/chest-armor.svg +++ b/assets/icons/documents/items/chest-armor.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/family-tree.svg b/assets/icons/documents/items/family-tree.svg index d95c935d..031f23da 100644 --- a/assets/icons/documents/items/family-tree.svg +++ b/assets/icons/documents/items/family-tree.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/laurel-crown.svg b/assets/icons/documents/items/laurel-crown.svg index 34a54d2a..38dbcd04 100644 --- a/assets/icons/documents/items/laurel-crown.svg +++ b/assets/icons/documents/items/laurel-crown.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/laurels.svg b/assets/icons/documents/items/laurels.svg index 2c3cdf63..ae7398ec 100644 --- a/assets/icons/documents/items/laurels.svg +++ b/assets/icons/documents/items/laurels.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/open-treasure-chest.svg b/assets/icons/documents/items/open-treasure-chest.svg index 172a8003..d66399f4 100644 --- a/assets/icons/documents/items/open-treasure-chest.svg +++ b/assets/icons/documents/items/open-treasure-chest.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/round-potion.svg b/assets/icons/documents/items/round-potion.svg index 7f981914..0c235732 100644 --- a/assets/icons/documents/items/round-potion.svg +++ b/assets/icons/documents/items/round-potion.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/stars-stack.svg b/assets/icons/documents/items/stars-stack.svg index 19c197f6..2d446683 100644 --- a/assets/icons/documents/items/stars-stack.svg +++ b/assets/icons/documents/items/stars-stack.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/village.svg b/assets/icons/documents/items/village.svg index c28d742b..b7c776a1 100644 --- a/assets/icons/documents/items/village.svg +++ b/assets/icons/documents/items/village.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/documents/items/wolf-head.svg b/assets/icons/documents/items/wolf-head.svg index 2be500c1..7e57bbad 100644 --- a/assets/icons/documents/items/wolf-head.svg +++ b/assets/icons/documents/items/wolf-head.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From 3840f39c9791d441073a02e83a1252d7fc438460 Mon Sep 17 00:00:00 2001 From: Carlos Fernandez Date: Sun, 21 Jun 2026 05:34:49 -0400 Subject: [PATCH 3/3] Fade environment adversaries and notes tabs when empty (#2028) --- module/applications/sheets/actors/environment.mjs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/module/applications/sheets/actors/environment.mjs b/module/applications/sheets/actors/environment.mjs index 9a88dba6..60fb9071 100644 --- a/module/applications/sheets/actors/environment.mjs +++ b/module/applications/sheets/actors/environment.mjs @@ -72,6 +72,16 @@ export default class DhpEnvironment extends DHBaseActorSheet { return applicationOptions; } + /** @inheritdoc */ + _prepareTabs(group) { + const result = super._prepareTabs(group); + if (group === 'primary') { + result.potentialAdversaries.empty = foundry.utils.isEmpty(this.document.system.potentialAdversaries); + result.notes.empty = !this.document.system.notes?.trim(); + } + return result; + } + /**@inheritdoc */ async _preparePartContext(partId, context, options) { context = await super._preparePartContext(partId, context, options);