[Fix] TagTag/GroupRoll Resource Handling (#2022)

This commit is contained in:
WBHarry 2026-06-21 04:50:09 +02:00 committed by GitHub
parent 29be8c1395
commit 08b95e48d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 92 additions and 47 deletions

View file

@ -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 });

View file

@ -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,33 +768,38 @@ 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 }]);
}
}
}
if (fearUpdate.value) {
mainActor.modifyResource([fearUpdate]);
/* 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 }]);
}
for (const resourceMap of Object.values(actorResourceMaps))
resourceMap.updateResources();
/* Fin */
this.cancelRoll({ confirm: false });
}

View file

@ -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
)

View file

@ -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;
}

View file

@ -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 {

View file

@ -26,10 +26,15 @@
</select>
</div>
</div>
<div class="form-group">
<div class="initiator-cost-fields">
<label>{{localize "DAGGERHEART.APPLICATIONS.TagTeamSelect.FIELDS.initiator.cost.label"}}</label>
<div class="form-fields">
<input type="text" data-dtype="Number" value="{{initiator.cost}}" class="initiator-cost-field" {{#if initiatorDisabled}}disabled{{/if}} />
<div class="initiator-cost-inputs">
<input type="checkbox" class="initiator-cost-enabled-checkbox {{#if initiatorDisabled}}inactive{{/if}}" {{checked usesTagTeamHopeCost}} {{#if initiatorDisabled}}disabled{{/if}} />
<input
type="text" data-dtype="Number" value="{{initiator.cost}}"
class="initiator-cost-input {{#if (or initiatorDisabled (not usesTagTeamHopeCost))}}inactive{{/if}}"
{{#if (or initiatorDisabled (not usesTagTeamHopeCost))}}disabled{{/if}}
/>
</div>
</div>
</div>