mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-06-06 04:44:16 +02:00
Merged with main
This commit is contained in:
commit
d78927d0c2
275 changed files with 5076 additions and 4132 deletions
|
|
@ -50,7 +50,7 @@ export default class CompendiumBrowserSettings extends HandlebarsApplicationMixi
|
|||
const excludedSourceData = this.browserSettings.excludedSources;
|
||||
const excludedPackData = this.browserSettings.excludedPacks;
|
||||
context.typePackCollections = game.packs.reduce((acc, pack) => {
|
||||
const { type, label, packageType, packageName: basePackageName, id } = pack.metadata;
|
||||
const { type, label, packageType, packageName: basePackageName, name, id } = pack.metadata;
|
||||
if (!CompendiumBrowserSettings.#browserPackTypes.includes(type)) return acc;
|
||||
|
||||
const isWorldPack = packageType === 'world';
|
||||
|
|
@ -68,13 +68,15 @@ export default class CompendiumBrowserSettings extends HandlebarsApplicationMixi
|
|||
if (!acc[type].sources[packageName])
|
||||
acc[type].sources[packageName] = { label: sourceLabel, checked: sourceChecked, packs: [] };
|
||||
|
||||
const checked = !excludedPackData[id] || !excludedPackData[id].excludedDocumentTypes.includes(type);
|
||||
const included =
|
||||
!excludedPackData[packageName] ||
|
||||
!excludedPackData[packageName][name]?.excludedDocumentTypes.includes(type);
|
||||
|
||||
acc[type].sources[packageName].packs.push({
|
||||
pack: id,
|
||||
name,
|
||||
type,
|
||||
label: id === game.system.id ? game.system.title : game.i18n.localize(label),
|
||||
checked: checked
|
||||
checked: included
|
||||
});
|
||||
|
||||
return acc;
|
||||
|
|
@ -106,16 +108,16 @@ export default class CompendiumBrowserSettings extends HandlebarsApplicationMixi
|
|||
toggleTypedPack(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
const { type, pack } = event.target.dataset;
|
||||
const currentlyExcluded = this.browserSettings.excludedPacks[pack]
|
||||
? this.browserSettings.excludedPacks[pack].excludedDocumentTypes.includes(type)
|
||||
const { type, source, packName } = event.target.dataset;
|
||||
const currentlyExcluded = this.browserSettings.excludedPacks[source]?.[packName]
|
||||
? this.browserSettings.excludedPacks[source][packName].excludedDocumentTypes.includes(type)
|
||||
: false;
|
||||
|
||||
if (!this.browserSettings.excludedPacks[pack])
|
||||
this.browserSettings.excludedPacks[pack] = { excludedDocumentTypes: [] };
|
||||
this.browserSettings.excludedPacks[pack].excludedDocumentTypes = currentlyExcluded
|
||||
? this.browserSettings.excludedPacks[pack].excludedDocumentTypes.filter(x => x !== type)
|
||||
: [...(this.browserSettings.excludedPacks[pack]?.excludedDocumentTypes ?? []), type];
|
||||
this.browserSettings.excludedPacks[source] ??= {};
|
||||
this.browserSettings.excludedPacks[source][packName] ??= { excludedDocumentTypes: [] };
|
||||
this.browserSettings.excludedPacks[source][packName].excludedDocumentTypes = currentlyExcluded
|
||||
? this.browserSettings.excludedPacks[source][packName].excludedDocumentTypes.filter(x => x !== type)
|
||||
: [...(this.browserSettings.excludedPacks[source][packName]?.excludedDocumentTypes ?? []), type];
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ export { default as ImageSelectDialog } from './imageSelectDialog.mjs';
|
|||
export { default as ItemTransferDialog } from './itemTransfer.mjs';
|
||||
export { default as MulticlassChoiceDialog } from './multiclassChoiceDialog.mjs';
|
||||
export { default as OwnershipSelection } from './ownershipSelection.mjs';
|
||||
export { default as RerollDamageDialog } from './rerollDamageDialog.mjs';
|
||||
export { default as ResourceDiceDialog } from './resourceDiceDialog.mjs';
|
||||
export { default as ActionSelectionDialog } from './actionSelectionDialog.mjs';
|
||||
export { default as TagTeamDialog } from './tagTeamDialog.mjs';
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@ export default class ActionSelectionDialog extends HandlebarsApplicationMixin(Ap
|
|||
|
||||
static async #onChooseAction(event, button) {
|
||||
const { actionId } = button.dataset;
|
||||
this.action = this.item.system.actionsList.find(a => a._id === actionId);
|
||||
Object.defineProperty(this.event, 'shiftKey', {
|
||||
this.#action = this.item.system.actionsList.find(a => a._id === actionId);
|
||||
Object.defineProperty(this.#event, 'shiftKey', {
|
||||
get() {
|
||||
return event.shiftKey;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,14 +175,14 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
this.disadvantage = advantage === -1;
|
||||
|
||||
this.config.roll.advantage = this.config.roll.advantage === advantage ? 0 : advantage;
|
||||
if (this.config.roll.advantage === 0) return this.render();
|
||||
|
||||
if (this.config.roll.advantage === 1 && this.config.data.rules.roll.advantageFaces) {
|
||||
const faces = Number.parseInt(this.config.data.rules.roll.advantageFaces);
|
||||
this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces;
|
||||
} else if (this.config.roll.advantage === -1 && this.config.data.rules.roll.disadvantageFaces) {
|
||||
const faces = Number.parseInt(this.config.data.rules.roll.disadvantageFaces);
|
||||
this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces;
|
||||
}
|
||||
const defaultFaces =
|
||||
this.config.roll.advantage === 1
|
||||
? this.config.data.rules.roll.defaultAdvantageDice
|
||||
: this.config.data.rules.roll.defaultDisadvantageDice;
|
||||
const faces = Number.parseInt(defaultFaces);
|
||||
this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,10 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
);
|
||||
|
||||
const orderedArmorSources = getArmorSources(actor).filter(s => !s.disabled);
|
||||
const armor = orderedArmorSources.reduce((acc, { document }) => {
|
||||
const armor = orderedArmorSources.reduce((acc, { name, document }) => {
|
||||
const { current, max } = document.type === 'armor' ? document.system.armor : document.system.armorData;
|
||||
acc.push({
|
||||
name,
|
||||
effect: document,
|
||||
marks: [...Array(max).keys()].reduce((acc, _, index) => {
|
||||
const spent = index < current;
|
||||
|
|
@ -152,14 +153,8 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
|
||||
const armorSources = [];
|
||||
for (const source of this.marks.armor) {
|
||||
const parent = source.effect.origin
|
||||
? await foundry.utils.fromUuid(source.effect.origin)
|
||||
: source.effect.parent;
|
||||
|
||||
const useEffectName = parent.type === 'armor' || parent instanceof Actor;
|
||||
const label = useEffectName ? source.effect.name : parent.name;
|
||||
armorSources.push({
|
||||
label: label,
|
||||
label: source.name,
|
||||
uuid: source.effect.uuid,
|
||||
marks: source.marks
|
||||
});
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV
|
|||
|
||||
let returnMessage = game.i18n.localize('DAGGERHEART.UI.Chat.deathMove.avoidScar');
|
||||
if (config.roll.fate.value <= this.actor.system.levelData.level.current) {
|
||||
const maxHope = this.actor.system.resources.hope.max + this.actor.system.scars;
|
||||
const newScarAmount = this.actor.system.scars + 1;
|
||||
await this.actor.update({
|
||||
system: {
|
||||
|
|
@ -64,7 +65,7 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV
|
|||
}
|
||||
});
|
||||
|
||||
if (newScarAmount >= this.actor.system.resources.hope.max) {
|
||||
if (newScarAmount >= maxHope) {
|
||||
await this.actor.setDeathMoveDefeated(CONFIG.DH.GENERAL.defeatedConditionChoices.dead.id);
|
||||
return game.i18n.format('DAGGERHEART.UI.Chat.deathMove.journeysEnd', { scars: newScarAmount });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,7 +259,9 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
|
|||
const resetValue = increasing
|
||||
? 0
|
||||
: feature.system.resource.max
|
||||
? new Roll(Roll.replaceFormulaData(feature.system.resource.max, this.actor)).evaluateSync().total
|
||||
? new Roll(
|
||||
Roll.replaceFormulaData(feature.system.resource.max, this.actor.getRollData())
|
||||
).evaluateSync().total
|
||||
: 0;
|
||||
|
||||
await feature.update({ 'system.resource.value': resetValue });
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { ResourceUpdateMap } from '../../data/action/baseAction.mjs';
|
||||
import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
||||
import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
||||
import Party from '../sheets/actors/party.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class GroupRollDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(party) {
|
||||
super();
|
||||
super({ id: `GroupRollDialog-${party.id}` });
|
||||
|
||||
this.party = party;
|
||||
this.partyMembers = party.system.partyMembers
|
||||
|
|
@ -35,19 +35,18 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
id: 'GroupRollDialog',
|
||||
classes: ['daggerheart', 'views', 'dh-style', 'dialog', 'group-roll-dialog'],
|
||||
position: { width: 550, height: 'auto' },
|
||||
position: { width: 390, height: 'auto' },
|
||||
window: {
|
||||
icon: 'fa-solid fa-users'
|
||||
},
|
||||
actions: {
|
||||
toggleSelectMember: this.#toggleSelectMember,
|
||||
startGroupRoll: this.#startGroupRoll,
|
||||
makeRoll: this.#makeRoll,
|
||||
removeRoll: this.#removeRoll,
|
||||
rerollDice: this.#rerollDice,
|
||||
makeLeaderRoll: this.#makeLeaderRoll,
|
||||
removeLeaderRoll: this.#removeLeaderRoll,
|
||||
rerollLeaderDice: this.#rerollLeaderDice,
|
||||
markSuccessfull: this.#markSuccessfull,
|
||||
markSuccessful: this.#markSuccessful,
|
||||
cancelRoll: this.#onCancelRoll,
|
||||
finishRoll: this.#finishRoll
|
||||
},
|
||||
|
|
@ -59,17 +58,21 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
id: 'initialization',
|
||||
template: 'systems/daggerheart/templates/dialogs/groupRollDialog/initialization.hbs'
|
||||
},
|
||||
main: {
|
||||
id: 'main',
|
||||
template: 'systems/daggerheart/templates/dialogs/groupRollDialog/main.hbs'
|
||||
},
|
||||
leader: {
|
||||
id: 'leader',
|
||||
template: 'systems/daggerheart/templates/dialogs/groupRollDialog/leader.hbs'
|
||||
template: 'systems/daggerheart/templates/dialogs/groupRollDialog/parts/member.hbs'
|
||||
},
|
||||
groupRoll: {
|
||||
id: 'groupRoll',
|
||||
template: 'systems/daggerheart/templates/dialogs/groupRollDialog/groupRoll.hbs'
|
||||
result: {
|
||||
id: 'result',
|
||||
template: 'systems/daggerheart/templates/dialogs/groupRollDialog/parts/result.hbs'
|
||||
},
|
||||
footer: {
|
||||
id: 'footer',
|
||||
template: 'systems/daggerheart/templates/dialogs/groupRollDialog/footer.hbs'
|
||||
template: 'systems/daggerheart/templates/dialogs/groupRollDialog/parts/footer.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -89,51 +92,31 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
}
|
||||
|
||||
_configureRenderParts(options) {
|
||||
const { initialization, leader, groupRoll, footer } = super._configureRenderParts(options);
|
||||
const augmentedParts = { initialization };
|
||||
const parts = super._configureRenderParts(options);
|
||||
for (const memberKey of Object.keys(this.party.system.groupRoll.aidingCharacters)) {
|
||||
augmentedParts[memberKey] = {
|
||||
parts[memberKey] = {
|
||||
id: memberKey,
|
||||
template: 'systems/daggerheart/templates/dialogs/groupRollDialog/groupRollMember.hbs'
|
||||
template: 'systems/daggerheart/templates/dialogs/groupRollDialog/parts/member.hbs'
|
||||
};
|
||||
}
|
||||
|
||||
augmentedParts.leader = leader;
|
||||
augmentedParts.groupRoll = groupRoll;
|
||||
augmentedParts.footer = footer;
|
||||
|
||||
return augmentedParts;
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options);
|
||||
|
||||
if (this.element.querySelector('.team-container')) return;
|
||||
|
||||
if (this.tabGroups.application !== this.constructor.PARTS.initialization.id) {
|
||||
const initializationPart = this.element.querySelector('.initialization-container');
|
||||
initializationPart.insertAdjacentHTML('afterend', '<div class="team-container"></div>');
|
||||
initializationPart.insertAdjacentHTML(
|
||||
'afterend',
|
||||
`<div class="section-title">${game.i18n.localize('DAGGERHEART.APPLICATIONS.GroupRollSelect.aidingCharacters')}</div>`
|
||||
);
|
||||
|
||||
const teamContainer = this.element.querySelector('.team-container');
|
||||
for (const memberContainer of this.element.querySelectorAll('.team-member-container'))
|
||||
teamContainer.appendChild(memberContainer);
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
|
||||
context.isGM = game.user.isGM;
|
||||
context.isEditable = this.getIsEditable();
|
||||
context.isEditable =
|
||||
game.user.isGM ||
|
||||
this.party.system.partyMembers.some(actor => {
|
||||
const selected = Boolean(this.party.system.groupRoll.participants[actor.id]);
|
||||
return selected && actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER);
|
||||
});
|
||||
context.fields = this.party.system.schema.fields.groupRoll.fields;
|
||||
context.data = this.party.system.groupRoll;
|
||||
context.traitOptions = CONFIG.DH.ACTOR.abilities;
|
||||
context.members = {};
|
||||
context.aidKeys = Object.keys(this.party.system.groupRoll.aidingCharacters);
|
||||
context.allHaveRolled = Object.keys(context.data.participants).every(key => {
|
||||
const data = context.data.participants[key];
|
||||
return Boolean(data.rollData);
|
||||
|
|
@ -145,6 +128,7 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
async _preparePartContext(partId, context, options) {
|
||||
const partContext = await super._preparePartContext(partId, context, options);
|
||||
partContext.partId = partId;
|
||||
partContext.leader = this.getRollCharacterData(this.party.system.groupRoll.leader);
|
||||
|
||||
switch (partId) {
|
||||
case 'initialization':
|
||||
|
|
@ -162,19 +146,14 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
partContext.canStartGroupRoll = selectedMembers.length > 1 && this.leader?.memberId;
|
||||
partContext.openForAllPlayers = this.openForAllPlayers;
|
||||
break;
|
||||
case 'leader':
|
||||
partContext.leader = this.getRollCharacterData(this.party.system.groupRoll.leader);
|
||||
break;
|
||||
case 'groupRoll':
|
||||
case 'result':
|
||||
const leader = this.party.system.groupRoll.leader;
|
||||
partContext.hasRolled =
|
||||
leader?.rollData ||
|
||||
Object.values(this.party.system.groupRoll?.aidingCharacters ?? {}).some(
|
||||
x => x.successfull !== null
|
||||
);
|
||||
Object.values(this.party.system.groupRoll?.aidingCharacters ?? {}).some(x => x.successful !== null);
|
||||
const { modifierTotal, modifiers } = Object.values(this.party.system.groupRoll.aidingCharacters).reduce(
|
||||
(acc, curr) => {
|
||||
const modifier = curr.successfull === true ? 1 : curr.successfull === false ? -1 : null;
|
||||
const modifier = curr.successful === true ? 1 : curr.successful === false ? -1 : null;
|
||||
if (modifier) {
|
||||
acc.modifierTotal += modifier;
|
||||
acc.modifiers.push(modifier);
|
||||
|
|
@ -200,7 +179,7 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
case 'footer':
|
||||
partContext.canFinishRoll =
|
||||
Boolean(this.party.system.groupRoll.leader?.rollData) &&
|
||||
Object.values(this.party.system.groupRoll.aidingCharacters).every(x => x.successfull !== null);
|
||||
Object.values(this.party.system.groupRoll.aidingCharacters).every(x => x.successful !== null);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -216,20 +195,42 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
if (!data) return {};
|
||||
|
||||
const actor = game.actors.get(data.id);
|
||||
const isLeader = data === this.party.system.groupRoll.leader;
|
||||
|
||||
const roll = data.roll;
|
||||
const withTypeSuffix = !roll ? null : roll.isCritical ? 'criticalShort' : roll.withHope ? 'hope' : 'fear';
|
||||
const thing = withTypeSuffix ? _loc(`DAGGERHEART.GENERAL.${withTypeSuffix}`) : null;
|
||||
|
||||
return {
|
||||
...data,
|
||||
type: isLeader ? 'leader' : 'aid',
|
||||
basePath: isLeader ? 'system.groupRoll.leader' : `system.groupRoll.aidingCharacters.${data.id}`,
|
||||
rollChoiceLabel: _loc(CONFIG.DH.ACTOR.abilities[data.rollChoice]?.label),
|
||||
roll: data.roll,
|
||||
isEditable: actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER),
|
||||
isEditable: actor?.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER),
|
||||
key: partId,
|
||||
readyToRoll: Boolean(data.rollChoice),
|
||||
hasRolled: Boolean(data.rollData)
|
||||
hasRolled: Boolean(data.rollData),
|
||||
modifier: data.successful ? 1 : data.successful === false ? -1 : 0,
|
||||
withLabelShort: thing ? _loc('DAGGERHEART.GENERAL.withThing', { thing }) : null
|
||||
};
|
||||
}
|
||||
|
||||
#getCharacterDataById(id) {
|
||||
if (!id) return null;
|
||||
|
||||
const groupRoll = this.party.system.groupRoll;
|
||||
if (id === 'leader' || id === groupRoll.leader?.id) {
|
||||
return { data: groupRoll.leader, basePath: 'system.groupRoll.leader' };
|
||||
} else if (id in groupRoll.aidingCharacters) {
|
||||
return { data: groupRoll.aidingCharacters[id], basePath: `system.groupRoll.aidingCharacters.${id}` };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static async updateData(event, _, formData) {
|
||||
const partyData = foundry.utils.expandObject(formData.object);
|
||||
|
||||
this.updatePartyData(partyData, this.getUpdatingParts(event.target));
|
||||
}
|
||||
|
||||
|
|
@ -246,7 +247,7 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
});
|
||||
};
|
||||
|
||||
await emitAsGM(
|
||||
await emitGMUpdate(
|
||||
GMUpdateEvent.UpdateDocument,
|
||||
gmUpdate,
|
||||
update,
|
||||
|
|
@ -256,26 +257,19 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
}
|
||||
|
||||
getUpdatingParts(target) {
|
||||
const { initialization, leader, groupRoll, footer } = this.constructor.PARTS;
|
||||
const { initialization, leader, result, footer } = this.constructor.PARTS;
|
||||
const isInitialization = this.tabGroups.application === initialization.id;
|
||||
const updatingMember = target.closest('.team-member-container')?.dataset?.memberKey;
|
||||
const updatingLeader = target.closest('.main-character-outer-container');
|
||||
const updatingMember = target.closest('.member-roll-container.aid')?.dataset?.memberKey;
|
||||
const updatingLeader = target.closest('.member-roll-container.leader');
|
||||
|
||||
return [
|
||||
...(isInitialization ? [initialization.id] : []),
|
||||
...(updatingMember ? [updatingMember] : []),
|
||||
...(updatingLeader ? [leader.id] : []),
|
||||
...(!isInitialization ? [groupRoll.id, footer.id] : [])
|
||||
...(!isInitialization ? [result.id, footer.id] : [])
|
||||
];
|
||||
}
|
||||
|
||||
getIsEditable() {
|
||||
return this.party.system.partyMembers.some(actor => {
|
||||
const selected = Boolean(this.party.system.groupRoll.participants[actor.id]);
|
||||
return selected && actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER);
|
||||
});
|
||||
}
|
||||
|
||||
groupRollRefresh = ({ refreshType, action, parts }) => {
|
||||
if (refreshType !== RefreshType.GroupRoll) return;
|
||||
|
||||
|
|
@ -304,6 +298,9 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
static #toggleSelectMember(_, button) {
|
||||
const member = this.partyMembers.find(x => x.id === button.dataset.id);
|
||||
member.selected = !member.selected;
|
||||
if (this.leader?.memberId === member.id) {
|
||||
this.leader = null;
|
||||
}
|
||||
this.render();
|
||||
}
|
||||
|
||||
|
|
@ -343,11 +340,14 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
}
|
||||
//#endregion
|
||||
|
||||
async makeRoll(button, characterData, path) {
|
||||
const actor = game.actors.find(x => x.id === characterData.id);
|
||||
/** @this GroupRollDialog */
|
||||
static async #makeRoll(_event, button) {
|
||||
const member = button.closest('[data-member-key]').dataset.memberKey;
|
||||
const { data, basePath } = this.#getCharacterDataById(member);
|
||||
const actor = game.actors.find(x => x.id === data.id);
|
||||
if (!actor) return;
|
||||
|
||||
const result = await actor.rollTrait(characterData.rollChoice, {
|
||||
const result = await actor.rollTrait(data.rollChoice, {
|
||||
skips: {
|
||||
createMessage: true,
|
||||
resources: true,
|
||||
|
|
@ -356,53 +356,38 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
});
|
||||
|
||||
if (!result) return;
|
||||
if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
|
||||
|
||||
const rollData = result.messageRoll.toJSON();
|
||||
delete rollData.options.messageRoll;
|
||||
this.updatePartyData(
|
||||
{
|
||||
[path]: rollData
|
||||
[basePath]: { rollData, successful: null }
|
||||
},
|
||||
this.getUpdatingParts(button)
|
||||
);
|
||||
}
|
||||
|
||||
static async #makeRoll(_event, button) {
|
||||
const { member } = button.dataset;
|
||||
const character = this.party.system.groupRoll.aidingCharacters[member];
|
||||
this.makeRoll(button, character, `system.groupRoll.aidingCharacters.${member}.rollData`);
|
||||
}
|
||||
|
||||
static async #makeLeaderRoll(_event, button) {
|
||||
const character = this.party.system.groupRoll.leader;
|
||||
this.makeRoll(button, character, 'system.groupRoll.leader.rollData');
|
||||
}
|
||||
|
||||
async removeRoll(button, path) {
|
||||
/** @this GroupRollDialog */
|
||||
static async #removeRoll(_event, button) {
|
||||
const member = button.closest('[data-member-key]').dataset.memberKey;
|
||||
const { basePath } = this.#getCharacterDataById(member);
|
||||
this.updatePartyData(
|
||||
{
|
||||
[path]: {
|
||||
[basePath]: {
|
||||
rollData: null,
|
||||
rollChoice: null,
|
||||
selected: false,
|
||||
successfull: null
|
||||
successful: null
|
||||
}
|
||||
},
|
||||
this.getUpdatingParts(button)
|
||||
);
|
||||
}
|
||||
|
||||
static async #removeRoll(_event, button) {
|
||||
this.removeRoll(button, `system.groupRoll.aidingCharacters.${button.dataset.member}`);
|
||||
}
|
||||
|
||||
static async #removeLeaderRoll(_event, button) {
|
||||
this.removeRoll(button, 'system.groupRoll.leader');
|
||||
}
|
||||
|
||||
async rerollDice(button, data, path) {
|
||||
/** @this GroupRollDialog */
|
||||
static async #rerollDice(_, button) {
|
||||
const { diceType } = button.dataset;
|
||||
const { data, basePath } = this.#getCharacterDataById(button.dataset.member);
|
||||
|
||||
const dieIndex = diceType === 'hope' ? 0 : diceType === 'fear' ? 1 : 2;
|
||||
const newRoll = game.system.api.dice.DualityRoll.fromData(data.rollData);
|
||||
|
|
@ -416,31 +401,19 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
const rollData = newRoll.toJSON();
|
||||
this.updatePartyData(
|
||||
{
|
||||
[path]: rollData
|
||||
[`${basePath}.rollData`]: rollData
|
||||
},
|
||||
this.getUpdatingParts(button)
|
||||
);
|
||||
}
|
||||
|
||||
static async #rerollDice(_, button) {
|
||||
const { member } = button.dataset;
|
||||
this.rerollDice(
|
||||
button,
|
||||
this.party.system.groupRoll.aidingCharacters[member],
|
||||
`system.groupRoll.aidingCharacters.${member}.rollData`
|
||||
);
|
||||
}
|
||||
|
||||
static async #rerollLeaderDice(_, button) {
|
||||
this.rerollDice(button, this.party.system.groupRoll.leader, `system.groupRoll.leader.rollData`);
|
||||
}
|
||||
|
||||
static #markSuccessfull(_event, button) {
|
||||
const previousValue = this.party.system.groupRoll.aidingCharacters[button.dataset.member].successfull;
|
||||
const newValue = Boolean(button.dataset.successfull === 'true');
|
||||
static #markSuccessful(_event, button) {
|
||||
const memberKey = button.closest('[data-member-key]').dataset.memberKey;
|
||||
const previousValue = this.party.system.groupRoll.aidingCharacters[memberKey].successful;
|
||||
const newValue = Boolean(button.dataset.success === 'true');
|
||||
this.updatePartyData(
|
||||
{
|
||||
[`system.groupRoll.aidingCharacters.${button.dataset.member}.successfull`]:
|
||||
[`system.groupRoll.aidingCharacters.${memberKey}.successful`]:
|
||||
previousValue === newValue ? null : newValue
|
||||
},
|
||||
this.getUpdatingParts(button)
|
||||
|
|
@ -484,7 +457,7 @@ export default class GroupRollDialog extends HandlebarsApplicationMixin(Applicat
|
|||
static async #finishRoll() {
|
||||
const totalRoll = this.party.system.groupRoll.leader.roll;
|
||||
for (const character of Object.values(this.party.system.groupRoll.aidingCharacters)) {
|
||||
totalRoll.terms.push(new foundry.dice.terms.OperatorTerm({ operator: character.successfull ? '+' : '-' }));
|
||||
totalRoll.terms.push(new foundry.dice.terms.OperatorTerm({ operator: character.successful ? '+' : '-' }));
|
||||
totalRoll.terms.push(new foundry.dice.terms.NumericTerm({ number: 1 }));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,280 +0,0 @@
|
|||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
export default class RerollDamageDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(message, options = {}) {
|
||||
super(options);
|
||||
|
||||
this.message = message;
|
||||
this.damage = Object.keys(message.system.damage).reduce((acc, typeKey) => {
|
||||
const type = message.system.damage[typeKey];
|
||||
acc[typeKey] = Object.keys(type.parts).reduce((acc, partKey) => {
|
||||
const part = type.parts[partKey];
|
||||
acc[partKey] = Object.keys(part.dice).reduce((acc, diceKey) => {
|
||||
const dice = part.dice[diceKey];
|
||||
const activeResults = dice.results.filter(x => x.active);
|
||||
acc[diceKey] = {
|
||||
dice: dice.dice,
|
||||
selectedResults: activeResults.length,
|
||||
maxSelected: activeResults.length,
|
||||
results: activeResults.map(x => ({ ...x, selected: true }))
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
id: 'reroll-dialog',
|
||||
classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'reroll-dialog'],
|
||||
window: {
|
||||
icon: 'fa-solid fa-dice'
|
||||
},
|
||||
actions: {
|
||||
toggleResult: RerollDamageDialog.#toggleResult,
|
||||
selectRoll: RerollDamageDialog.#selectRoll,
|
||||
doReroll: RerollDamageDialog.#doReroll,
|
||||
save: RerollDamageDialog.#save
|
||||
}
|
||||
};
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
id: 'main',
|
||||
template: 'systems/daggerheart/templates/dialogs/rerollDialog/damage/main.hbs'
|
||||
},
|
||||
footer: {
|
||||
id: 'footer',
|
||||
template: 'systems/daggerheart/templates/dialogs/rerollDialog/footer.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.damageTitle');
|
||||
}
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
htmlElement.querySelectorAll('.to-reroll-input').forEach(element => {
|
||||
element.addEventListener('change', this.toggleDice.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.damage = this.damage;
|
||||
context.disabledReroll = !this.getRerollDice().length;
|
||||
context.saveDisabled = !this.isSelectionDone();
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async #save() {
|
||||
const update = {
|
||||
'system.damage': Object.keys(this.damage).reduce((acc, typeKey) => {
|
||||
const type = this.damage[typeKey];
|
||||
let typeTotal = 0;
|
||||
const messageType = this.message.system.damage[typeKey];
|
||||
const parts = Object.keys(type).map(partKey => {
|
||||
const part = type[partKey];
|
||||
const messagePart = messageType.parts[partKey];
|
||||
let partTotal = messagePart.modifierTotal;
|
||||
const dice = Object.keys(part).map(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
const total = dice.results.reduce((acc, result) => {
|
||||
if (result.active) acc += result.result;
|
||||
return acc;
|
||||
}, 0);
|
||||
partTotal += total;
|
||||
const messageDice = messagePart.dice[diceKey];
|
||||
return {
|
||||
...messageDice,
|
||||
total: total,
|
||||
results: dice.results.map(x => ({
|
||||
...x,
|
||||
hasRerolls: dice.results.length > 1
|
||||
}))
|
||||
};
|
||||
});
|
||||
|
||||
typeTotal += partTotal;
|
||||
return {
|
||||
...messagePart,
|
||||
total: partTotal,
|
||||
dice: dice
|
||||
};
|
||||
});
|
||||
|
||||
acc[typeKey] = {
|
||||
...messageType,
|
||||
total: typeTotal,
|
||||
parts: parts
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {})
|
||||
};
|
||||
|
||||
await this.message.update(update);
|
||||
await this.close();
|
||||
}
|
||||
|
||||
getRerollDice() {
|
||||
const rerollDice = [];
|
||||
Object.keys(this.damage).forEach(typeKey => {
|
||||
const type = this.damage[typeKey];
|
||||
Object.keys(type).forEach(partKey => {
|
||||
const part = type[partKey];
|
||||
Object.keys(part).forEach(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
Object.keys(dice.results).forEach(resultKey => {
|
||||
const result = dice.results[resultKey];
|
||||
if (result.toReroll) {
|
||||
rerollDice.push({
|
||||
...result,
|
||||
dice: dice.dice,
|
||||
type: typeKey,
|
||||
part: partKey,
|
||||
dice: diceKey,
|
||||
result: resultKey
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return rerollDice;
|
||||
}
|
||||
|
||||
isSelectionDone() {
|
||||
const diceFinishedData = [];
|
||||
Object.keys(this.damage).forEach(typeKey => {
|
||||
const type = this.damage[typeKey];
|
||||
Object.keys(type).forEach(partKey => {
|
||||
const part = type[partKey];
|
||||
Object.keys(part).forEach(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
const selected = dice.results.reduce((acc, result) => acc + (result.active ? 1 : 0), 0);
|
||||
diceFinishedData.push(selected === dice.maxSelected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return diceFinishedData.every(x => x);
|
||||
}
|
||||
|
||||
toggleDice(event) {
|
||||
const target = event.target;
|
||||
const { type, part, dice } = target.dataset;
|
||||
const toggleDice = this.damage[type][part][dice];
|
||||
|
||||
const existingDiceRerolls = this.getRerollDice().filter(
|
||||
x => x.type === type && x.part === part && x.dice === dice
|
||||
);
|
||||
|
||||
const allRerolled = existingDiceRerolls.length === toggleDice.results.filter(x => x.active).length;
|
||||
|
||||
toggleDice.toReroll = !allRerolled;
|
||||
toggleDice.results.forEach(result => {
|
||||
if (result.active) {
|
||||
result.toReroll = !allRerolled;
|
||||
}
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static #toggleResult(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
const target = event.target.closest('.to-reroll-result');
|
||||
const { type, part, dice, result } = target.dataset;
|
||||
const toggleDice = this.damage[type][part][dice];
|
||||
const toggleResult = toggleDice.results[result];
|
||||
toggleResult.toReroll = !toggleResult.toReroll;
|
||||
|
||||
const existingDiceRerolls = this.getRerollDice().filter(
|
||||
x => x.type === type && x.part === part && x.dice === dice
|
||||
);
|
||||
|
||||
const allToReroll = existingDiceRerolls.length === toggleDice.results.filter(x => x.active).length;
|
||||
toggleDice.toReroll = allToReroll;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #selectRoll(_, button) {
|
||||
const { type, part, dice, result } = button.dataset;
|
||||
|
||||
const diceVal = this.damage[type][part][dice];
|
||||
const diceResult = diceVal.results[result];
|
||||
if (!diceResult.active && diceVal.results.filter(x => x.active).length === diceVal.maxSelected) {
|
||||
return ui.notifications.warn(
|
||||
game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.deselectDiceNotification')
|
||||
);
|
||||
}
|
||||
|
||||
if (diceResult.active) {
|
||||
diceVal.toReroll = false;
|
||||
diceResult.toReroll = false;
|
||||
}
|
||||
|
||||
diceVal.selectedResults += diceResult.active ? -1 : 1;
|
||||
diceResult.active = !diceResult.active;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #doReroll() {
|
||||
const toReroll = this.getRerollDice().map(x => {
|
||||
const { type, part, dice, result } = x;
|
||||
const diceData = this.damage[type][part][dice].results[result];
|
||||
return {
|
||||
...diceData,
|
||||
dice: this.damage[type][part][dice].dice,
|
||||
typeKey: type,
|
||||
partKey: part,
|
||||
diceKey: dice,
|
||||
resultsIndex: result
|
||||
};
|
||||
});
|
||||
|
||||
const roll = await new Roll(toReroll.map(x => `1${x.dice}`).join(' + ')).evaluate();
|
||||
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
const diceSoNiceRoll = {
|
||||
_evaluated: true,
|
||||
dice: roll.dice,
|
||||
options: { appearance: {} }
|
||||
};
|
||||
|
||||
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
|
||||
}
|
||||
|
||||
toReroll.forEach((data, index) => {
|
||||
const { typeKey, partKey, diceKey, resultsIndex } = data;
|
||||
const rerolledDice = roll.dice[index];
|
||||
|
||||
const dice = this.damage[typeKey][partKey][diceKey];
|
||||
dice.toReroll = false;
|
||||
dice.results[resultsIndex].active = false;
|
||||
dice.results[resultsIndex].discarded = true;
|
||||
dice.results[resultsIndex].toReroll = false;
|
||||
dice.results.splice(dice.results.length, 0, {
|
||||
...rerolledDice.results[0],
|
||||
toReroll: false,
|
||||
selected: true
|
||||
});
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,279 +0,0 @@
|
|||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
export default class RerollDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(message, options = {}) {
|
||||
super(options);
|
||||
|
||||
this.message = message;
|
||||
this.damage = Object.keys(message.system.damage).reduce((acc, typeKey) => {
|
||||
const type = message.system.damage[typeKey];
|
||||
acc[typeKey] = Object.keys(type.parts).reduce((acc, partKey) => {
|
||||
const part = type.parts[partKey];
|
||||
acc[partKey] = Object.keys(part.dice).reduce((acc, diceKey) => {
|
||||
const dice = part.dice[diceKey];
|
||||
const activeResults = dice.results.filter(x => x.active);
|
||||
acc[diceKey] = {
|
||||
dice: dice.dice,
|
||||
selectedResults: activeResults.length,
|
||||
maxSelected: activeResults.length,
|
||||
results: activeResults.map(x => ({ ...x, selected: true }))
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS = {
|
||||
id: 'reroll-dialog',
|
||||
classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'reroll-dialog'],
|
||||
window: {
|
||||
icon: 'fa-solid fa-dice'
|
||||
},
|
||||
actions: {
|
||||
toggleResult: RerollDialog.#toggleResult,
|
||||
selectRoll: RerollDialog.#selectRoll,
|
||||
doReroll: RerollDialog.#doReroll,
|
||||
save: RerollDialog.#save
|
||||
}
|
||||
};
|
||||
|
||||
/** @override */
|
||||
static PARTS = {
|
||||
main: {
|
||||
id: 'main',
|
||||
template: 'systems/daggerheart/templates/dialogs/rerollDialog/main.hbs'
|
||||
},
|
||||
footer: {
|
||||
id: 'footer',
|
||||
template: 'systems/daggerheart/templates/dialogs/rerollDialog/footer.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.title');
|
||||
}
|
||||
|
||||
_attachPartListeners(partId, htmlElement, options) {
|
||||
super._attachPartListeners(partId, htmlElement, options);
|
||||
|
||||
htmlElement.querySelectorAll('.to-reroll-input').forEach(element => {
|
||||
element.addEventListener('change', this.toggleDice.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.damage = this.damage;
|
||||
context.disabledReroll = !this.getRerollDice().length;
|
||||
context.saveDisabled = !this.isSelectionDone();
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
static async #save() {
|
||||
const update = {
|
||||
'system.damage': Object.keys(this.damage).reduce((acc, typeKey) => {
|
||||
const type = this.damage[typeKey];
|
||||
let typeTotal = 0;
|
||||
const messageType = this.message.system.damage[typeKey];
|
||||
const parts = Object.keys(type).map(partKey => {
|
||||
const part = type[partKey];
|
||||
const messagePart = messageType.parts[partKey];
|
||||
let partTotal = messagePart.modifierTotal;
|
||||
const dice = Object.keys(part).map(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
const total = dice.results.reduce((acc, result) => {
|
||||
if (result.active) acc += result.result;
|
||||
return acc;
|
||||
}, 0);
|
||||
partTotal += total;
|
||||
const messageDice = messagePart.dice[diceKey];
|
||||
return {
|
||||
...messageDice,
|
||||
total: total,
|
||||
results: dice.results.map(x => ({
|
||||
...x,
|
||||
hasRerolls: dice.results.length > 1
|
||||
}))
|
||||
};
|
||||
});
|
||||
|
||||
typeTotal += partTotal;
|
||||
return {
|
||||
...messagePart,
|
||||
total: partTotal,
|
||||
dice: dice
|
||||
};
|
||||
});
|
||||
|
||||
acc[typeKey] = {
|
||||
...messageType,
|
||||
total: typeTotal,
|
||||
parts: parts
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {})
|
||||
};
|
||||
await this.message.update(update);
|
||||
await this.close();
|
||||
}
|
||||
|
||||
getRerollDice() {
|
||||
const rerollDice = [];
|
||||
Object.keys(this.damage).forEach(typeKey => {
|
||||
const type = this.damage[typeKey];
|
||||
Object.keys(type).forEach(partKey => {
|
||||
const part = type[partKey];
|
||||
Object.keys(part).forEach(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
Object.keys(dice.results).forEach(resultKey => {
|
||||
const result = dice.results[resultKey];
|
||||
if (result.toReroll) {
|
||||
rerollDice.push({
|
||||
...result,
|
||||
dice: dice.dice,
|
||||
type: typeKey,
|
||||
part: partKey,
|
||||
dice: diceKey,
|
||||
result: resultKey
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return rerollDice;
|
||||
}
|
||||
|
||||
isSelectionDone() {
|
||||
const diceFinishedData = [];
|
||||
Object.keys(this.damage).forEach(typeKey => {
|
||||
const type = this.damage[typeKey];
|
||||
Object.keys(type).forEach(partKey => {
|
||||
const part = type[partKey];
|
||||
Object.keys(part).forEach(diceKey => {
|
||||
const dice = part[diceKey];
|
||||
const selected = dice.results.reduce((acc, result) => acc + (result.active ? 1 : 0), 0);
|
||||
diceFinishedData.push(selected === dice.maxSelected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return diceFinishedData.every(x => x);
|
||||
}
|
||||
|
||||
toggleDice(event) {
|
||||
const target = event.target;
|
||||
const { type, part, dice } = target.dataset;
|
||||
const toggleDice = this.damage[type][part][dice];
|
||||
|
||||
const existingDiceRerolls = this.getRerollDice().filter(
|
||||
x => x.type === type && x.part === part && x.dice === dice
|
||||
);
|
||||
|
||||
const allRerolled = existingDiceRerolls.length === toggleDice.results.filter(x => x.active).length;
|
||||
|
||||
toggleDice.toReroll = !allRerolled;
|
||||
toggleDice.results.forEach(result => {
|
||||
if (result.active) {
|
||||
result.toReroll = !allRerolled;
|
||||
}
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static #toggleResult(event) {
|
||||
event.stopPropagation();
|
||||
|
||||
const target = event.target.closest('.to-reroll-result');
|
||||
const { type, part, dice, result } = target.dataset;
|
||||
const toggleDice = this.damage[type][part][dice];
|
||||
const toggleResult = toggleDice.results[result];
|
||||
toggleResult.toReroll = !toggleResult.toReroll;
|
||||
|
||||
const existingDiceRerolls = this.getRerollDice().filter(
|
||||
x => x.type === type && x.part === part && x.dice === dice
|
||||
);
|
||||
|
||||
const allToReroll = existingDiceRerolls.length === toggleDice.results.length;
|
||||
toggleDice.toReroll = allToReroll;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #selectRoll(_, button) {
|
||||
const { type, part, dice, result } = button.dataset;
|
||||
|
||||
const diceVal = this.damage[type][part][dice];
|
||||
const diceResult = diceVal.results[result];
|
||||
if (!diceResult.active && diceVal.results.filter(x => x.active).length === diceVal.maxSelected) {
|
||||
return ui.notifications.warn(
|
||||
game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.deselectDiceNotification')
|
||||
);
|
||||
}
|
||||
|
||||
if (diceResult.active) {
|
||||
diceVal.toReroll = false;
|
||||
diceResult.toReroll = false;
|
||||
}
|
||||
|
||||
diceVal.selectedResults += diceResult.active ? -1 : 1;
|
||||
diceResult.active = !diceResult.active;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static async #doReroll() {
|
||||
const toReroll = this.getRerollDice().map(x => {
|
||||
const { type, part, dice, result } = x;
|
||||
const diceData = this.damage[type][part][dice].results[result];
|
||||
return {
|
||||
...diceData,
|
||||
dice: this.damage[type][part][dice].dice,
|
||||
typeKey: type,
|
||||
partKey: part,
|
||||
diceKey: dice,
|
||||
resultsIndex: result
|
||||
};
|
||||
});
|
||||
|
||||
const roll = await new Roll(toReroll.map(x => `1${x.dice}`).join(' + ')).evaluate();
|
||||
|
||||
if (game.modules.get('dice-so-nice')?.active) {
|
||||
const diceSoNiceRoll = {
|
||||
_evaluated: true,
|
||||
dice: roll.dice,
|
||||
options: { appearance: {} }
|
||||
};
|
||||
|
||||
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
|
||||
}
|
||||
|
||||
toReroll.forEach((data, index) => {
|
||||
const { typeKey, partKey, diceKey, resultsIndex } = data;
|
||||
const rerolledDice = roll.dice[index];
|
||||
|
||||
const dice = this.damage[typeKey][partKey][diceKey];
|
||||
dice.toReroll = false;
|
||||
dice.results[resultsIndex].active = false;
|
||||
dice.results[resultsIndex].discarded = true;
|
||||
dice.results[resultsIndex].toReroll = false;
|
||||
dice.results.splice(dice.results.length, 0, {
|
||||
...rerolledDice.results[0],
|
||||
toReroll: false,
|
||||
selected: true
|
||||
});
|
||||
});
|
||||
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { itemAbleRollParse } from '../../helpers/utils.mjs';
|
||||
import { itemAbleRollParse, triggerChatRollFx } from '../../helpers/utils.mjs';
|
||||
|
||||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ export default class ResourceDiceDialog extends HandlebarsApplicationMixin(Appli
|
|||
const max = itemAbleRollParse(this.item.system.resource.max, this.actor, this.item);
|
||||
const diceFormula = `${max}${this.item.system.resource.dieFaces}`;
|
||||
const roll = await new Roll(diceFormula).evaluate();
|
||||
if (game.modules.get('dice-so-nice')?.active) await game.dice3d.showForRoll(roll, game.user, true);
|
||||
await triggerChatRollFx([roll]);
|
||||
this.rollValues = roll.terms[0].results.map(x => ({ value: x.result, used: false }));
|
||||
this.resetUsed = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { MemberData } from '../../data/tagTeamData.mjs';
|
||||
import { getCritDamageBonus } from '../../helpers/utils.mjs';
|
||||
import { emitAsGM, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
||||
import { emitGMUpdate, GMUpdateEvent, RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';
|
||||
import Party from '../sheets/actors/party.mjs';
|
||||
|
||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||
|
||||
export default class TagTeamDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||
constructor(party) {
|
||||
super();
|
||||
super({ id: `TagTeamDialog-${party.id}` });
|
||||
|
||||
this.party = party;
|
||||
this.partyMembers = party.system.partyMembers
|
||||
|
|
@ -36,9 +36,11 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
id: 'TagTeamDialog',
|
||||
classes: ['daggerheart', 'views', 'dh-style', 'dialog', 'tag-team-dialog'],
|
||||
position: { width: 550, height: 'auto' },
|
||||
window: {
|
||||
icon: 'fa-solid fa-user-group'
|
||||
},
|
||||
actions: {
|
||||
toggleSelectMember: TagTeamDialog.#toggleSelectMember,
|
||||
startTagTeamRoll: TagTeamDialog.#startTagTeamRoll,
|
||||
|
|
@ -60,13 +62,17 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
id: 'initialization',
|
||||
template: 'systems/daggerheart/templates/dialogs/tagTeamDialog/initialization.hbs'
|
||||
},
|
||||
tagTeamRoll: {
|
||||
id: 'tagTeamRoll',
|
||||
template: 'systems/daggerheart/templates/dialogs/tagTeamDialog/tagTeamRoll.hbs'
|
||||
},
|
||||
rollSelection: {
|
||||
id: 'rollSelection',
|
||||
template: 'systems/daggerheart/templates/dialogs/tagTeamDialog/rollSelection.hbs'
|
||||
},
|
||||
tagTeamRoll: {
|
||||
id: 'tagTeamRoll',
|
||||
template: 'systems/daggerheart/templates/dialogs/tagTeamDialog/tagTeamRoll.hbs'
|
||||
result: {
|
||||
id: 'result',
|
||||
template: 'systems/daggerheart/templates/dialogs/tagTeamDialog/result.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -97,41 +103,25 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
}
|
||||
|
||||
_configureRenderParts(options) {
|
||||
const { initialization, rollSelection, tagTeamRoll } = super._configureRenderParts(options);
|
||||
const augmentedParts = { initialization };
|
||||
const parts = super._configureRenderParts(options);
|
||||
for (const memberKey of Object.keys(this.party.system.tagTeam.members)) {
|
||||
augmentedParts[memberKey] = {
|
||||
parts[memberKey] = {
|
||||
id: memberKey,
|
||||
template: 'systems/daggerheart/templates/dialogs/tagTeamDialog/tagTeamMember.hbs'
|
||||
};
|
||||
}
|
||||
augmentedParts.rollSelection = rollSelection;
|
||||
augmentedParts.tagTeamRoll = tagTeamRoll;
|
||||
|
||||
return augmentedParts;
|
||||
}
|
||||
|
||||
/**@inheritdoc */
|
||||
async _onRender(context, options) {
|
||||
await super._onRender(context, options);
|
||||
|
||||
// if (this.element.querySelector('.roll-selection')) {
|
||||
// for (const element of this.element.querySelectorAll('.team-member-container')) {
|
||||
// element.classList.add('select-padding');
|
||||
// }
|
||||
// }
|
||||
|
||||
if (this.element.querySelector('.team-container')) return;
|
||||
const initializationPart = this.element.querySelector('.initialization-container');
|
||||
initializationPart.insertAdjacentHTML('afterend', '<div class="team-container"></div>');
|
||||
const teamContainer = this.element.querySelector('.team-container');
|
||||
for (const memberContainer of this.element.querySelectorAll('.team-member-container'))
|
||||
teamContainer.appendChild(memberContainer);
|
||||
return parts;
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.isEditable = this.getIsEditable();
|
||||
context.isEditable =
|
||||
game.user.isGM ||
|
||||
this.party.system.partyMembers.some(actor => {
|
||||
const selected = Boolean(this.party.system.tagTeam.members[actor.id]);
|
||||
return selected && actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER);
|
||||
});
|
||||
context.fields = this.party.system.schema.fields.tagTeam.fields;
|
||||
context.data = this.party.system.tagTeam;
|
||||
context.rollTypes = CONFIG.DH.GENERAL.tagTeamRollTypes;
|
||||
|
|
@ -167,6 +157,9 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
partContext.initiatorDisabled = !selectedMembers.length;
|
||||
partContext.openForAllPlayers = this.openForAllPlayers;
|
||||
|
||||
break;
|
||||
case 'tagTeamRoll':
|
||||
partContext.memberKeys = Object.keys(this.party.system.tagTeam.members);
|
||||
break;
|
||||
case 'rollSelection':
|
||||
partContext.members = Object.keys(this.party.system.tagTeam.members).reduce((acc, key) => {
|
||||
|
|
@ -175,7 +168,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
return acc;
|
||||
}, {});
|
||||
break;
|
||||
case 'tagTeamRoll':
|
||||
case 'result':
|
||||
const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected);
|
||||
const critSelected = !selectedRoll
|
||||
? undefined
|
||||
|
|
@ -191,59 +184,58 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
}
|
||||
|
||||
if (Object.keys(this.party.system.tagTeam.members).includes(partId)) {
|
||||
const data = this.party.system.tagTeam.members[partId];
|
||||
const actor = game.actors.get(partId);
|
||||
|
||||
const rollOptions = [];
|
||||
const damageRollOptions = [];
|
||||
for (const item of actor.items) {
|
||||
if (item.system.metadata.hasActions) {
|
||||
const actions = [...item.system.actions, ...(item.system.attack ? [item.system.attack] : [])];
|
||||
for (const action of actions) {
|
||||
if (action.hasRoll) {
|
||||
const actionItem = {
|
||||
value: action.uuid,
|
||||
label: action.name,
|
||||
group: item.name,
|
||||
baseAction: action.baseAction
|
||||
};
|
||||
|
||||
if (action.hasDamage) damageRollOptions.push(actionItem);
|
||||
else rollOptions.push(actionItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected);
|
||||
const critSelected = !selectedRoll
|
||||
? undefined
|
||||
: (selectedRoll?.rollData?.options?.roll?.isCritical ?? false);
|
||||
|
||||
const damage = data.rollData?.options?.damage;
|
||||
partContext.hasDamage |= Boolean(damage);
|
||||
const critHitPointsDamage = await this.getCriticalDamage(damage);
|
||||
|
||||
partContext.members[partId] = {
|
||||
...data,
|
||||
roll: data.roll,
|
||||
isEditable: actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER),
|
||||
key: partId,
|
||||
readyToRoll: Boolean(data.rollChoice),
|
||||
hasRolled: Boolean(data.rollData),
|
||||
rollOptions,
|
||||
damageRollOptions,
|
||||
damage: damage,
|
||||
critDamage: critHitPointsDamage,
|
||||
useCritDamage: critSelected || (critSelected === undefined && data.rollData?.options?.roll?.isCritical)
|
||||
};
|
||||
const data = await this.#prepareMemberContext(partId);
|
||||
partContext.hasDamage |= Boolean(data?.damage);
|
||||
partContext.members[partId] = data;
|
||||
}
|
||||
|
||||
return partContext;
|
||||
}
|
||||
|
||||
async #prepareMemberContext(partId) {
|
||||
const data = this.party.system.tagTeam.members[partId] ?? {};
|
||||
const actor = game.actors.get(partId);
|
||||
if (!actor) console.error(`Failed to get actor ${partId}`);
|
||||
|
||||
const rollOptions = [];
|
||||
const damageRollOptions = [];
|
||||
for (const item of actor?.items ?? []) {
|
||||
if (!item.system.metadata.hasActions) continue;
|
||||
const actions = [...item.system.actions, ...(item.system.attack ? [item.system.attack] : [])];
|
||||
for (const action of actions) {
|
||||
if (action.hasRoll) {
|
||||
const collection = action.hasDamage ? damageRollOptions : rollOptions;
|
||||
collection.push({
|
||||
value: action.uuid,
|
||||
label: action.name,
|
||||
group: item.name,
|
||||
baseAction: action.baseAction
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const selectedRoll = Object.values(this.party.system.tagTeam.members).find(member => member.selected);
|
||||
const critSelected = !selectedRoll ? undefined : (selectedRoll?.rollData?.options?.roll?.isCritical ?? false);
|
||||
const damage = data.rollData?.options?.damage;
|
||||
|
||||
return {
|
||||
...data,
|
||||
roll: data.roll,
|
||||
isEditable: actor?.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER),
|
||||
key: partId,
|
||||
readyToRoll: Boolean(data.rollChoice),
|
||||
hasRolled: Boolean(data.rollData),
|
||||
rollOptions,
|
||||
damageRollOptions,
|
||||
damage: damage,
|
||||
critDamage: await this.getCriticalDamage(damage),
|
||||
useCritDamage: critSelected || (critSelected === undefined && data.rollData?.options?.roll?.isCritical)
|
||||
};
|
||||
}
|
||||
|
||||
getUpdatingParts(target) {
|
||||
const { initialization, rollSelection, tagTeamRoll } = this.constructor.PARTS;
|
||||
const { initialization, rollSelection, result } = this.constructor.PARTS;
|
||||
const isInitialization = this.tabGroups.application === initialization.id;
|
||||
const updatingMember = target.closest('.team-member-container')?.dataset?.memberKey;
|
||||
|
||||
|
|
@ -251,7 +243,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
...(isInitialization ? [initialization.id] : []),
|
||||
...(updatingMember ? [updatingMember] : []),
|
||||
...(!isInitialization ? [rollSelection.id] : []),
|
||||
...(!isInitialization ? [tagTeamRoll.id] : [])
|
||||
...(!isInitialization ? [result.id] : [])
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -274,7 +266,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
});
|
||||
};
|
||||
|
||||
await emitAsGM(
|
||||
await emitGMUpdate(
|
||||
GMUpdateEvent.UpdateDocument,
|
||||
gmUpdate,
|
||||
update,
|
||||
|
|
@ -285,13 +277,6 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
);
|
||||
}
|
||||
|
||||
getIsEditable() {
|
||||
return this.party.system.partyMembers.some(actor => {
|
||||
const selected = Boolean(this.party.system.tagTeam.members[actor.id]);
|
||||
return selected && actor.testUserPermission(game.user, CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER);
|
||||
});
|
||||
}
|
||||
|
||||
tagTeamRefresh = ({ refreshType, action, parts }) => {
|
||||
if (refreshType !== RefreshType.TagTeamRoll) return;
|
||||
|
||||
|
|
@ -446,8 +431,6 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
|
||||
if (!result) return;
|
||||
|
||||
if (!game.modules.get('dice-so-nice')?.active) foundry.audio.AudioHelper.play({ src: CONFIG.sounds.dice });
|
||||
|
||||
const rollData = result.messageRoll.toJSON();
|
||||
delete rollData.options.messageRoll;
|
||||
this.updatePartyData(
|
||||
|
|
@ -663,42 +646,50 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
|
|||
}
|
||||
|
||||
async getJoinedRoll({ overrideIsCritical, displayVersion } = {}) {
|
||||
const memberValues = Object.values(this.party.system.tagTeam.members);
|
||||
const selectedRoll = memberValues.find(x => x.selected);
|
||||
let baseMainRoll = selectedRoll ?? memberValues[0];
|
||||
let baseSecondaryRoll = selectedRoll
|
||||
? memberValues.find(x => !x.selected)
|
||||
: memberValues.length > 1
|
||||
? memberValues[1]
|
||||
: null;
|
||||
try {
|
||||
const memberValues = Object.values(this.party.system.tagTeam.members);
|
||||
const selectedRoll = memberValues.find(x => x.selected);
|
||||
const baseMainRoll = selectedRoll ?? memberValues[0];
|
||||
const baseSecondaryRoll = selectedRoll
|
||||
? memberValues.find(x => !x.selected)
|
||||
: memberValues.length > 1
|
||||
? memberValues[1]
|
||||
: null;
|
||||
|
||||
if (!baseMainRoll?.rollData || !baseSecondaryRoll) return null;
|
||||
if (!baseMainRoll?.rollData || !baseSecondaryRoll) return null;
|
||||
|
||||
const mainRoll = new MemberData(baseMainRoll.toObject());
|
||||
const secondaryRollData = new MemberData(baseSecondaryRoll.toObject()).rollData;
|
||||
const systemData = mainRoll.rollData.options;
|
||||
const isCritical = overrideIsCritical ?? systemData.roll.isCritical;
|
||||
if (isCritical) systemData.damage = await this.getCriticalDamage(systemData.damage);
|
||||
const mainRoll = new MemberData(baseMainRoll.toObject());
|
||||
const secondaryRollData = new MemberData(baseSecondaryRoll.toObject()).rollData;
|
||||
const systemData = mainRoll.rollData.options;
|
||||
const isCritical = overrideIsCritical ?? systemData.roll.isCritical;
|
||||
if (isCritical) systemData.damage = await this.getCriticalDamage(systemData.damage);
|
||||
|
||||
if (secondaryRollData?.options.hasDamage) {
|
||||
const secondaryDamage = (displayVersion ? overrideIsCritical : isCritical)
|
||||
? await this.getCriticalDamage(secondaryRollData.options.damage)
|
||||
: secondaryRollData.options.damage;
|
||||
if (systemData.damage) {
|
||||
for (const key in secondaryDamage) {
|
||||
const damage = secondaryDamage[key];
|
||||
systemData.damage[key].formula = [systemData.damage[key].formula, damage.formula]
|
||||
.filter(x => x)
|
||||
.join(' + ');
|
||||
systemData.damage[key].total += damage.total;
|
||||
systemData.damage[key].parts.push(...damage.parts);
|
||||
if (secondaryRollData?.options.hasDamage) {
|
||||
const secondaryDamage = (displayVersion ? overrideIsCritical : isCritical)
|
||||
? await this.getCriticalDamage(secondaryRollData.options.damage)
|
||||
: secondaryRollData.options.damage;
|
||||
if (systemData.damage) {
|
||||
for (const [key, damage] of Object.entries(secondaryDamage ?? {})) {
|
||||
if (key in systemData.damage) {
|
||||
systemData.damage[key].formula = [systemData.damage[key]?.formula, damage.formula]
|
||||
.filter(x => x)
|
||||
.join(' + ');
|
||||
systemData.damage[key].total += damage.total;
|
||||
systemData.damage[key].parts.push(...damage.parts);
|
||||
} else {
|
||||
systemData.damage[key] = damage;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
systemData.damage = secondaryDamage;
|
||||
}
|
||||
} else {
|
||||
systemData.damage = secondaryDamage;
|
||||
}
|
||||
}
|
||||
|
||||
return mainRoll;
|
||||
return mainRoll;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static async #onCancelRoll(_event, _button, options = { confirm: true }) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue