This commit is contained in:
WBHarry 2026-03-12 15:08:39 +01:00
parent 43114187b9
commit f78b4d1789
5 changed files with 370 additions and 57 deletions

View file

@ -1,3 +1,4 @@
import { getCritDamageBonus } from '../../helpers/utils.mjs';
import Party from '../sheets/actors/party.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
@ -34,7 +35,11 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
startTagTeamRoll: TagTeamDialog.#startTagTeamRoll,
makeRoll: TagTeamDialog.#makeRoll,
removeRoll: TagTeamDialog.#removeRoll,
rerollDice: TagTeamDialog.#rerollDice
rerollDice: TagTeamDialog.#rerollDice,
makeDamageRoll: TagTeamDialog.#makeDamageRoll,
removeDamageRoll: TagTeamDialog.#removeDamageRoll,
selectRoll: TagTeamDialog.#selectRoll,
finishRoll: TagTeamDialog.#finishRoll
},
form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false }
};
@ -83,6 +88,8 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
partContext.rollTypes = CONFIG.DH.GENERAL.tagTeamRollTypes;
partContext.traitOptions = CONFIG.DH.ACTOR.abilities;
const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected);
const critSelected = !selectedRoll ? undefined : (selectedRoll?.rollData?.options?.isCritical ?? false);
partContext.members = Object.keys(this.party.system.tagTeam.members).reduce((acc, actorId) => {
const data = this.party.system.tagTeam.members[actorId];
const actor = game.actors.get(actorId);
@ -104,17 +111,27 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
return acc;
}, []);
const hitPointsDamage = data.rollData?.options?.damage?.hitPoints;
const preCritHitPointsDamage = data.rollData?.options?.damage?.hitPoints?.preCritData?.hitPoints;
acc[actorId] = {
...data,
key: actorId,
readyToRoll: Boolean(data.rollChoice),
hasRolled: Boolean(data.rollData),
rollOptions
rollOptions,
damage: hitPointsDamage,
preCritDamage: preCritHitPointsDamage,
useCritFallback: selectedRoll !== data && critSelected === false
};
return acc;
}, {});
const { hint, totalDamage } = await this.getInfoTexts(this.party.system.tagTeam.members, critSelected);
partContext.hintText = hint;
partContext.totalDamage = totalDamage;
break;
}
@ -157,6 +174,38 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
//#endregion
//#region Tag Team Roll
async getInfoTexts(members, critSelected) {
let rollsAreFinished = true;
let rollIsSelected = false;
let totalDamage = null;
for (const member of Object.values(members)) {
const rollFinished = Boolean(member.rollData);
const hasDamage = member.rollData?.options?.hasDamage;
const damageFinished =
member.rollData?.options?.hasDamage !== undefined ? member.rollData.options.damage : true;
let hitPointDamage =
critSelected === false && member.rollData?.options?.isCritical
? member.rollData.options.damage?.hitPoints?.preCritData?.hitPoints?.total
: member.rollData?.options?.damage?.hitPoints?.total;
if (critSelected && member.rollData?.options?.isCritical === false) {
hitPointDamage += await getCritDamageBonus(member.rollData.options.damage?.hitPoints?.formula ?? '');
}
if (hasDamage) totalDamage = (totalDamage ?? 0) + (hitPointDamage ?? 0);
rollsAreFinished = rollsAreFinished && rollFinished && damageFinished;
rollIsSelected = rollIsSelected || member.selected;
}
let hint = null;
if (!rollsAreFinished) hint = game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.hints.completeRolls');
else if (!rollIsSelected) hint = game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.hints.selectRoll');
return { hint, totalDamage };
}
async updateRollType(event) {
await this.party.update({
[`system.tagTeam.members.${event.target.dataset.member}`]: {
@ -258,5 +307,187 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
this.render();
}
static async #makeDamageRoll(_, button) {
const { memberKey } = button.dataset;
const actor = game.actors.find(x => x.id === memberKey);
if (!actor) return;
const memberData = this.party.system.tagTeam.members[memberKey];
const action = await foundry.utils.fromUuid(memberData.rollChoice);
const config = {
source: {},
skips: {
createMessage: true,
resources: true,
triggers: true
}
};
await action.workflow.get('damage').execute(config, null, true);
if (!config.damage) return;
if (memberData.rollData.options.isCritical && config.damage.hitPoints) {
const critBonus = await getCritDamageBonus(config.damage.hitPoints.formula);
if (critBonus) {
config.damage.hitPoints.preCritData = foundry.utils.deepClone(config.damage);
config.damage.hitPoints.total += critBonus;
config.damage.hitPoints.formula = `${config.damage.hitPoints.formula} + ${critBonus}`;
config.damage.hitPoints.parts[0].total += critBonus;
config.damage.hitPoints.parts[0].formula = `${config.damage.hitPoints.parts[0].formula} + ${critBonus}`;
}
}
const current = this.party.system.tagTeam.members[memberKey].rollData;
await this.party.update({
[`system.tagTeam.members.${memberKey}.rollData`]: {
...current,
options: {
...current.options,
damage: config.damage
}
}
});
this.render();
}
static async #removeDamageRoll(_, button) {
const { memberKey } = button.dataset;
const current = this.party.system.tagTeam.members[memberKey].rollData;
await this.party.update({
[`system.tagTeam.members.${memberKey}.rollData`]: {
...current,
options: {
...current.options,
damage: null
}
}
});
this.render();
}
static async #selectRoll(_, button) {
const { memberKey } = button.dataset;
await this.party.update({
[`system.tagTeam.members`]: Object.entries(this.party.system.tagTeam.members).reduce(
(acc, [key, member]) => {
acc[key] = { selected: key === memberKey ? !member.selected : false };
return acc;
},
{}
)
});
this.render();
}
static async #finishRoll() {
// const mainRollId = Object.keys(this.data.members).find(key => this.data.members[key].selected);
// const mainRoll = game.messages.get(this.data.members[mainRollId].messageId);
// if (this.data.initiator.cost) {
// const initiator = this.party.find(x => x.id === this.data.initiator.id);
// if (initiator.system.resources.hope.value < this.data.initiator.cost) {
// return ui.notifications.warn(
// game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.insufficientHope')
// );
// }
// }
let mainRoll = null;
let secondaryRoll = null;
for (const member of Object.values(this.party.system.tagTeam.members)) {
if (member.selected) mainRoll = foundry.utils.deepClone(member.rollData);
else secondaryRoll = foundry.utils.deepClone(member.rollData);
}
if (!mainRoll || !secondaryRoll) return;
const systemData = mainRoll.options;
const criticalRoll = systemData.roll.isCritical;
if (secondaryRoll.options.hasDamage && systemData.hasDamage) {
for (let key in secondaryRoll.options.damage) {
var damage = secondaryRoll.options.damage[key];
const damageTotal =
!secondaryRoll.options.isCritical && criticalRoll
? (await getCritDamageBonus(damage.formula)) + damage.total
: damage.total;
const updatedDamageParts = damage.parts;
if (systemData.damage[key]) {
if (!secondaryRoll.options.isCritical && criticalRoll) {
for (let part of updatedDamageParts) {
const criticalDamage = await getCritDamageBonus(part.formula);
if (criticalDamage) {
damage.formula = `${damage.formula} + ${criticalDamage}`;
part.formula = `${part.formula} + ${criticalDamage}`;
part.modifierTotal = part.modifierTotal + criticalDamage;
part.total += criticalDamage;
part.roll = new Roll(part.formula);
}
}
} else if (
secondaryRoll.options.isCritical &&
!criticalRoll &&
secondaryRoll.options.damage.hitPoints.preCritData
) {
damage = secondaryRoll.options.damage.hitPoints.preCritData[key];
}
systemData.damage[key].formula = `${systemData.damage[key].formula} + ${damage.formula}`;
systemData.damage[key].total += damageTotal;
systemData.damage[key].parts = [...systemData.damage[key].parts, ...updatedDamageParts];
} else {
systemData.damage[key] = { ...damage, total: damageTotal, parts: updatedDamageParts };
}
}
}
const mainActor = this.party.system.partyMembers.find(x => x.uuid === mainRoll.options.source.actor);
systemData.title = game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.chatMessageRollTitle');
const cls = getDocumentClass('ChatMessage'),
msgData = {
type: 'dualityRoll',
user: game.user.id,
title: game.i18n.localize('DAGGERHEART.APPLICATIONS.TagTeamSelect.title'),
speaker: cls.getSpeaker({ actor: mainActor }),
system: systemData,
rolls: [mainRoll],
sound: null,
flags: { core: { RollTable: true } }
};
await cls.create(msgData);
// const fearUpdate = { key: 'fear', value: null, total: null, enabled: true };
// for (let memberId of Object.keys(this.data.members)) {
// const resourceUpdates = [];
// const rollGivesHope = systemData.roll.isCritical || systemData.roll.result.duality === 1;
// if (memberId === this.data.initiator.id) {
// const value = this.data.initiator.cost
// ? rollGivesHope
// ? 1 - this.data.initiator.cost
// : -this.data.initiator.cost
// : 1;
// resourceUpdates.push({ key: 'hope', value: value, total: -value, enabled: true });
// } else if (rollGivesHope) {
// resourceUpdates.push({ key: 'hope', value: 1, total: -1, enabled: true });
// }
// if (systemData.roll.isCritical) resourceUpdates.push({ key: 'stress', value: -1, total: 1, enabled: true });
// if (systemData.roll.result.duality === -1) {
// fearUpdate.value = fearUpdate.value === null ? 1 : fearUpdate.value + 1;
// fearUpdate.total = fearUpdate.total === null ? -1 : fearUpdate.total - 1;
// }
// this.party.find(x => x.id === memberId).modifyResource(resourceUpdates);
// }
// if (fearUpdate.value) {
// this.party.find(x => x.id === mainRollId).modifyResource([fearUpdate]);
// }
/* Clear Party tag Team Data here */
}
//#endregion
}

View file

@ -22,7 +22,8 @@ class MemberData extends foundry.abstract.DataModel {
label: 'Roll Type'
}),
rollChoice: new fields.StringField({ nullable: true, initial: null }),
rollData: new fields.JSONField({ nullable: true, initial: null })
rollData: new fields.JSONField({ nullable: true, initial: null }),
selected: new fields.BooleanField({ initial: false })
};
}