Task/415 action refactor part 2 (#1094)

This commit is contained in:
Dapoulp 2025-08-27 09:00:18 +02:00 committed by GitHub
parent 8e5dd22370
commit 1eb3ff11c0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
47 changed files with 1244 additions and 641 deletions

View file

@ -1,3 +1,5 @@
import { abilities } from "../../config/actorConfig.mjs";
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class D20RollDialog extends HandlebarsApplicationMixin(ApplicationV2) {
@ -7,7 +9,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
this.roll = roll;
this.config = config;
this.config.experiences = [];
this.reactionOverride = config.roll?.type === 'reaction';
this.reactionOverride = config.actionType === 'reaction';
if (config.source?.action) {
this.item = config.data.parent.items.get(config.source.item) ?? config.data.parent;
@ -20,7 +22,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
static DEFAULT_OPTIONS = {
tag: 'form',
id: 'roll-selection',
// id: 'roll-selection',
classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'roll-selection'],
position: {
width: 'auto'
@ -42,7 +44,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
};
get title() {
return this.config.title;
return `${this.config.title}${this.actor ? `: ${this.actor.name}` : ''}`;
}
get actor() {
@ -113,15 +115,21 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
context.isLite = this.config.roll?.lite;
context.extraFormula = this.config.extraFormula;
context.formula = this.roll.constructFormula(this.config);
if(this.actor.system.traits) context.abilities = this.getTraitModifiers();
context.showReaction = !context.rollConfig.type && context.rollType === 'DualityRoll';
context.showReaction = !this.config.roll?.type && context.rollType === 'DualityRoll';
context.reactionOverride = this.reactionOverride;
}
return context;
}
getTraitModifiers() {
return Object.values(abilities).map(a => ({ id: a.id, label: `${game.i18n.localize(a.label)} (${this.actor.system.traits[a.id]?.value.signedString() ?? 0})` }))
}
static updateRollConfiguration(event, _, formData) {
const { ...rest } = foundry.utils.expandObject(formData.object);
this.config.selectedRollMode = rest.selectedRollMode;
if (this.config.costs) {
@ -133,6 +141,12 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
this.roll[key] = value;
});
}
if(rest.hasOwnProperty("trait")) {
this.config.roll.trait = rest.trait;
this.config.title = game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
ability: game.i18n.localize(abilities[this.config.roll.trait]?.label)
});
}
this.config.extraFormula = rest.extraFormula;
this.render();
}
@ -151,31 +165,29 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
this.config.experiences.indexOf(button.dataset.key) > -1
? this.config.experiences.filter(x => x !== button.dataset.key)
: [...this.config.experiences, button.dataset.key];
if (this.config?.data?.parent?.type === 'character' || this.config?.data?.parent?.type === 'companion') {
this.config.costs =
this.config.costs.indexOf(this.config.costs.find(c => c.extKey === button.dataset.key)) > -1
? this.config.costs.filter(x => x.extKey !== button.dataset.key)
: [
...this.config.costs,
{
extKey: button.dataset.key,
key: 'hope',
value: 1,
name: this.config.data?.experiences?.[button.dataset.key]?.name
}
];
}
this.config.costs =
this.config.costs.indexOf(this.config.costs.find(c => c.extKey === button.dataset.key)) > -1
? this.config.costs.filter(x => x.extKey !== button.dataset.key)
: [
...this.config.costs,
{
extKey: button.dataset.key,
key: this.config?.data?.parent?.isNPC ? 'fear' : 'hope',
value: 1,
name: this.config.data?.experiences?.[button.dataset.key]?.name
}
];
this.render();
}
static toggleReaction() {
if (this.config.roll) {
this.reactionOverride = !this.reactionOverride;
this.config.roll.type = this.reactionOverride
this.config.actionType = this.reactionOverride
? CONFIG.DH.ITEM.actionTypes.reaction.id
: this.config.roll.type === CONFIG.DH.ITEM.actionTypes.reaction.id
: this.config.actionType === CONFIG.DH.ITEM.actionTypes.reaction.id
? null
: this.config.roll.type;
: this.config.actionType;
this.render();
}
}

View file

@ -35,13 +35,14 @@ export default class DhAutomationSettings extends HandlebarsApplicationMixin(App
header: { template: 'systems/daggerheart/templates/settings/automation-settings/header.hbs' },
general: { template: 'systems/daggerheart/templates/settings/automation-settings/general.hbs' },
rules: { template: 'systems/daggerheart/templates/settings/automation-settings/rules.hbs' },
roll: { template: 'systems/daggerheart/templates/settings/automation-settings/roll.hbs' },
footer: { template: 'systems/daggerheart/templates/settings/automation-settings/footer.hbs' }
};
/** @inheritdoc */
static TABS = {
main: {
tabs: [{ id: 'general' }, { id: 'rules' }],
tabs: [{ id: 'general' }, { id: 'rules' }, { id: 'roll' }],
initial: 'general',
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
}

View file

@ -146,9 +146,9 @@ export default class AdversarySheet extends DHBaseActorSheet {
title: `Reaction Roll: ${this.actor.name}`,
headerTitle: 'Adversary Reaction Roll',
roll: {
type: 'reaction'
type: 'trait'
},
type: 'trait',
actionType: 'reaction',
hasRoll: true,
data: this.actor.getRollData()
};

View file

@ -679,31 +679,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
})
});
this.consumeResource(result?.costs);
}
// Remove when Action Refactor part #2 done
async consumeResource(costs) {
if (!costs?.length) return;
const usefulResources = {
...foundry.utils.deepClone(this.actor.system.resources),
fear: {
value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear),
max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear,
reversed: false
}
};
const resources = game.system.api.fields.ActionFields.CostField.getRealCosts(costs).map(c => {
const resource = usefulResources[c.key];
return {
key: c.key,
value: (c.total ?? c.value) * (resource.isReversed ? 1 : -1),
target: resource.target,
keyIsID: resource.keyIsID
};
});
await this.actor.modifyResource(resources);
if(result) game.system.api.fields.ActionFields.CostField.execute.call(this, result);
}
//TODO: redo toggleEquipItem method

View file

@ -412,6 +412,16 @@ export default function DHApplicationMixin(Base) {
];
if (usable) {
options.unshift({
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.cancelBeastform',
icon: 'fa-solid fa-ban',
condition: target => {
const doc = getDocFromElementSync(target);
return doc && doc.system?.actions?.some(a => a.type === "beastform");
},
callback: async target => game.system.api.fields.ActionFields.BeastformField.handleActiveTransformations.call(await getDocFromElement(target))
});
options.unshift({
name: 'DAGGERHEART.GENERAL.damage',
icon: 'fa-solid fa-explosion',
@ -422,7 +432,9 @@ export default function DHApplicationMixin(Base) {
callback: async (target, event) => {
const doc = await getDocFromElement(target),
action = doc?.system?.attack ?? doc;
return action && action.use(event, { byPassRoll: true });
const config = action.prepareConfig(event);
config.hasRoll = false;
return action && action.workflow.get("damage").execute(config, null, true);
}
});

View file

@ -1,5 +1,3 @@
import { emitAsGM, GMUpdateEvent } from '../../systemRegistration/socket.mjs';
export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog {
constructor(options) {
super(options);
@ -55,21 +53,9 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
}
addChatListeners = async (app, html, data) => {
html.querySelectorAll('.duality-action-damage').forEach(element =>
element.addEventListener('click', event => this.onRollDamage(event, data.message))
);
html.querySelectorAll('.target-save').forEach(element =>
element.addEventListener('click', event => this.onRollSave(event, data.message))
);
html.querySelectorAll('.roll-all-save-button').forEach(element =>
element.addEventListener('click', event => this.onRollAllSave(event, data.message))
);
html.querySelectorAll('.simple-roll-button').forEach(element =>
element.addEventListener('click', event => this.onRollSimple(event, data.message))
);
html.querySelectorAll('.healing-button').forEach(element =>
element.addEventListener('click', event => this.onHealing(event, data.message))
);
html.querySelectorAll('.ability-use-button').forEach(element =>
element.addEventListener('click', event => this.abilityUseButton(event, data.message))
);
@ -90,80 +76,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
super.close(options);
}
async getActor(uuid) {
return await foundry.utils.fromUuid(uuid);
}
getAction(actor, itemId, actionId) {
const item = actor.items.get(itemId),
action =
actor.system.attack?._id === actionId
? actor.system.attack
: item.system.attack?._id === actionId
? item.system.attack
: item?.system?.actions?.get(actionId);
return action;
}
async onRollDamage(event, message) {
event.stopPropagation();
const actor = await this.getActor(message.system.source.actor);
if(!actor.isOwner) return true;
if (message.system.source.item && message.system.source.action) {
const action = this.getAction(actor, message.system.source.item, message.system.source.action);
if (!action || !action?.rollDamage) return;
await action.rollDamage(event, message);
}
}
async onRollSave(event, message) {
event.stopPropagation();
const actor = await this.getActor(message.system.source.actor),
tokenId = event.target.closest('[data-token]')?.dataset.token,
token = game.canvas.tokens.get(tokenId);
if (!token?.actor || !token.isOwner) return true;
if (message.system.source.item && message.system.source.action) {
const action = this.getAction(actor, message.system.source.item, message.system.source.action);
if (!action || !action?.hasSave) return;
action.rollSave(token.actor, event, message).then(result =>
emitAsGM(
GMUpdateEvent.UpdateSaveMessage,
action.updateSaveMessage.bind(action, result, message, token.id),
{
action: action.uuid,
message: message._id,
token: token.id,
result
}
)
);
}
}
async onRollAllSave(event, message) {
event.stopPropagation();
if (!game.user.isGM) return;
const targets = event.target.parentElement.querySelectorAll('[data-token] .target-save');
const actor = await this.getActor(message.system.source.actor),
action = this.getAction(actor, message.system.source.item, message.system.source.action);
targets.forEach(async el => {
const tokenId = el.closest('[data-token]')?.dataset.token,
token = game.canvas.tokens.get(tokenId);
if (!token.actor) return;
if (game.user === token.actor.owner) el.dispatchEvent(new PointerEvent('click', { shiftKey: true }));
else {
token.actor.owner
.query('reactionRoll', {
actionId: action.uuid,
actorId: token.actor.uuid,
event,
message
})
.then(result => action.updateSaveMessage(result, message, token.id));
}
});
}
async onRollSimple(event, message) {
const buttonType = event.target.dataset.type ?? 'damage',
total = message.rolls.reduce((a, c) => a + Roll.fromJSON(c).total, 0),
@ -197,8 +109,11 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
item.system.attack?.id === event.currentTarget.id
? item.system.attack
: item.system.actions.get(event.currentTarget.id);
if (event.currentTarget.dataset.directDamage) action.use(event, { byPassRoll: true });
else action.use(event);
if (event.currentTarget.dataset.directDamage) {
const config = action.prepareConfig(event);
config.hasRoll = false;
action.workflow.get("damage").execute(config, null, true);
} else action.use(event);
}
async actionUseButton(event, message) {

View file

@ -78,7 +78,7 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV
/** @override */
async _preRender(context, options) {
if (this.currentFear > this.maxFear)
if (this.currentFear > this.maxFear && game.user.isGM)
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear, this.maxFear);
}
@ -107,18 +107,5 @@ export default class FearTracker extends HandlebarsApplicationMixin(ApplicationV
async updateFear(value) {
return emitAsGM(GMUpdateEvent.UpdateFear, game.settings.set.bind(game.settings, CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), value);
/* if(!game.user.isGM)
await game.socket.emit(`system.${CONFIG.DH.id}`, {
action: socketEvent.GMUpdate,
data: {
action: GMUpdateEvent.UpdateFear,
update: value
}
});
else
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear, value); */
/* if (!game.user.isGM) return;
value = Math.max(0, Math.min(this.maxFear, value));
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear, value); */
}
}