From 6018763d47bcbee4669541da029d2e3358afd636 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Tue, 1 Jul 2025 12:49:00 +0200 Subject: [PATCH] Fixed UseItem and ToChat --- .../applications/sheets/actors/adversary.mjs | 41 +++- .../applications/sheets/actors/character.mjs | 4 +- module/data/action/action.mjs | 179 +++++++++++------- 3 files changed, 150 insertions(+), 74 deletions(-) diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs index 98b3bb12..a588ba0f 100644 --- a/module/applications/sheets/actors/adversary.mjs +++ b/module/applications/sheets/actors/adversary.mjs @@ -10,7 +10,8 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) { position: { width: 660, height: 766 }, actions: { reactionRoll: this.reactionRoll, - attackRoll: this.attackRoll, + useItem: this.useItem, + toChat: this.toChat, attackConfigure: this.attackConfigure, addExperience: this.addExperience, removeExperience: this.removeExperience, @@ -70,6 +71,12 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) { return context; } + getAction(element) { + const itemId = (element.target ?? element).closest('[data-item-id]').dataset.itemId, + item = this.document.system.actions.find(x => x.id === itemId); + return item; + } + static async updateForm(event, _, formData) { await this.document.update(formData.object); this.render(); @@ -100,8 +107,36 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) { await new DHAdversarySettings(this.document).render(true); } - static async attackRoll(event) { - this.actor.system.attack.use(event); + static async useItem(event) { + const action = this.getAction(event) ?? this.actor.system.attack; + action.use(event); + } + + static async toChat(event, button) { + if (button?.dataset?.type === 'experience') { + const experience = this.document.system.experiences[button.dataset.uuid]; + const cls = getDocumentClass('ChatMessage'); + const systemData = { + name: game.i18n.localize('DAGGERHEART.General.Experience.Single'), + description: `${experience.name} ${ + experience.modifier < 0 ? experience.modifier : `+${experience.modifier}` + }` + }; + const msg = new cls({ + type: 'abilityUse', + user: game.user.id, + system: systemData, + content: await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/chat/ability-use.hbs', + systemData + ) + }); + + cls.create(msg.toObject()); + } else { + const item = this.getAction(event) ?? this.document.system.attack; + item.toChat(this.document.id); + } } static async attackConfigure(event) { diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index a5e3564b..62e42604 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -815,9 +815,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) { const cls = getDocumentClass('ChatMessage'); const systemData = { name: game.i18n.localize('DAGGERHEART.General.Experience.Single'), - description: `${experience.description} ${ - experience.total < 0 ? experience.total : `+${experience.total}` - }` + description: `${experience.name} ${experience.total < 0 ? experience.total : `+${experience.total}`}` }; const msg = new cls({ type: 'abilityUse', diff --git a/module/data/action/action.mjs b/module/data/action/action.mjs index ee9bbabe..5f543b47 100644 --- a/module/data/action/action.mjs +++ b/module/data/action/action.mjs @@ -73,7 +73,10 @@ export class DHBaseAction extends foundry.abstract.DataModel { save: new fields.SchemaField({ trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }), difficulty: new fields.NumberField({ nullable: true, initial: 10, integer: true, min: 0 }), - damageMod: new fields.StringField({ initial: SYSTEM.ACTIONS.damageOnSave.none.id, choices: SYSTEM.ACTIONS.damageOnSave }) + damageMod: new fields.StringField({ + initial: SYSTEM.ACTIONS.damageOnSave.none.id, + choices: SYSTEM.ACTIONS.damageOnSave + }) }), target: new fields.SchemaField({ type: new fields.StringField({ @@ -98,9 +101,12 @@ export class DHBaseAction extends foundry.abstract.DataModel { initial: SYSTEM.GENERAL.healingTypes.hitPoints.id, label: 'Healing' }), - resultBased: new fields.BooleanField({ initial: false, label: "DAGGERHEART.Actions.Settings.ResultBased.label" }), + resultBased: new fields.BooleanField({ + initial: false, + label: 'DAGGERHEART.Actions.Settings.ResultBased.label' + }), value: new fields.EmbeddedDataField(DHActionDiceData), - valueAlt: new fields.EmbeddedDataField(DHActionDiceData), + valueAlt: new fields.EmbeddedDataField(DHActionDiceData) }) }, extraSchemas = {}; @@ -153,7 +159,7 @@ export class DHBaseAction extends foundry.abstract.DataModel { return updateSource; } - getRollData(data={}) { + getRollData(data = {}) { const actorData = this.actor.getRollData(false); // Remove when included directly in Actor getRollData @@ -166,11 +172,11 @@ export class DHBaseAction extends foundry.abstract.DataModel { return a; }, {}) : 1; */ - actorData.scale = data.costs?.length // Right now only return the first scalable cost. - ? (data.costs.find(c => c.scalable)?.total ?? 1) - : 1; + actorData.scale = data.costs?.length // Right now only return the first scalable cost. + ? (data.costs.find(c => c.scalable)?.total ?? 1) + : 1; actorData.roll = {}; - + return actorData; } @@ -191,12 +197,14 @@ export class DHBaseAction extends foundry.abstract.DataModel { // Prepare Costs const costsConfig = this.prepareCost(); - if(isFastForward && !this.hasCost(costsConfig)) return ui.notifications.warn("You don't have the resources to use that action."); + if (isFastForward && !this.hasCost(costsConfig)) + return ui.notifications.warn("You don't have the resources to use that action."); // config = this.prepareUseCost(config) // Prepare Uses const usesConfig = this.prepareUse(); - if(isFastForward && !this.hasUses(usesConfig)) return ui.notifications.warn("That action doesn't have remaining uses."); + if (isFastForward && !this.hasUses(usesConfig)) + return ui.notifications.warn("That action doesn't have remaining uses."); // config = this.prepareUseCost(config) // Prepare Roll Data @@ -209,24 +217,24 @@ export class DHBaseAction extends foundry.abstract.DataModel { costs: costsConfig, uses: usesConfig, data: actorData - } - - if ( Hooks.call(`${SYSTEM.id}.preUseAction`, this, config) === false ) return; + }; + + if (Hooks.call(`${SYSTEM.id}.preUseAction`, this, config) === false) return; // Display configuration window if necessary - if ( config.dialog?.configure && this.requireConfigurationDialog(config) ) { + if (config.dialog?.configure && this.requireConfigurationDialog(config)) { config = await D20RollDialog.configure(config); if (!config) return; } - if ( this.hasRoll ) { + if (this.hasRoll) { const rollConfig = this.prepareRoll(config); config.roll = rollConfig; config = await this.actor.diceRoll(config); if (!config) return; } - if( this.hasSave ) { + if (this.hasSave) { /* config.targets.forEach((t) => { if(t.hit) { const target = game.canvas.tokens.get(t.id), @@ -258,16 +266,16 @@ export class DHBaseAction extends foundry.abstract.DataModel { }) */ } - if ( this.doFollowUp() ) { - if(this.rollDamage) await this.rollDamage(event, config); - if(this.rollHealing) await this.rollHealing(event, config); - if(this.trigger) await this.trigger(event, config); + if (this.doFollowUp()) { + if (this.rollDamage) await this.rollDamage(event, config); + if (this.rollHealing) await this.rollHealing(event, config); + if (this.trigger) await this.trigger(event, config); } // Consume resources await this.consume(config); - - if ( Hooks.call(`${SYSTEM.id}.postUseAction`, this, config) === false ) return; + + if (Hooks.call(`${SYSTEM.id}.postUseAction`, this, config) === false) return; return config; } @@ -287,7 +295,7 @@ export class DHBaseAction extends foundry.abstract.DataModel { hasHealing: !!this.healing, hasEffect: !!this.effects?.length, hasSave: this.hasSave - } + }; } requireConfigurationDialog(config) { @@ -317,7 +325,6 @@ export class DHBaseAction extends foundry.abstract.DataModel { } targets = targets.map(t => this.formatTarget(t)); return targets; - } prepareRange() { @@ -326,7 +333,7 @@ export class DHBaseAction extends foundry.abstract.DataModel { } prepareRoll() { - const roll = { + const roll = { modifiers: [], trait: this.roll?.trait, label: 'Attack', @@ -334,8 +341,8 @@ export class DHBaseAction extends foundry.abstract.DataModel { difficulty: this.roll?.difficulty, formula: this.roll.getFormula() }; - if(this.roll?.type === 'diceSet') roll.lite = true; - + if (this.roll?.type === 'diceSet') roll.lite = true; + return roll; } @@ -344,12 +351,14 @@ export class DHBaseAction extends foundry.abstract.DataModel { } async consume(config) { - const resources = config.costs.filter(c => c.enabled !== false).map(c => { - return { type: c.type, value: (c.total ?? c.value) * -1 }; - }); - + const resources = config.costs + .filter(c => c.enabled !== false) + .map(c => { + return { type: c.type, value: (c.total ?? c.value) * -1 }; + }); + await this.actor.modifyResource(resources); - if(config.uses?.enabled) { + if (config.uses?.enabled) { const newActions = foundry.utils.getProperty(this.item.system, this.systemPath).map(x => x.toObject()); newActions[this.index].uses.value++; await this.item.update({ [`system.${this.systemPath}`]: newActions }); @@ -388,13 +397,16 @@ export class DHBaseAction extends foundry.abstract.DataModel { hasCost(costs) { const realCosts = this.getRealCosts(costs); - return realCosts.reduce((a, c) => a && this.actor.system.resources[c.type]?.value >= (c.total ?? c.value), true); + return realCosts.reduce( + (a, c) => a && this.actor.system.resources[c.type]?.value >= (c.total ?? c.value), + true + ); } /* COST */ /* USES */ calcUses(uses) { - if(!uses) return null; + if (!uses) return null; return { ...uses, enabled: uses.hasOwnProperty('enabled') ? uses.enabled : true @@ -402,7 +414,7 @@ export class DHBaseAction extends foundry.abstract.DataModel { } hasUses(uses) { - if(!uses) return true; + if (!uses) return true; return (uses.hasOwnProperty('enabled') && !uses.enabled) || uses.value + 1 <= uses.max; } /* USES */ @@ -432,7 +444,7 @@ export class DHBaseAction extends foundry.abstract.DataModel { /* TARGET */ /* RANGE */ - + /* RANGE */ /* EFFECTS */ @@ -441,10 +453,10 @@ export class DHBaseAction extends foundry.abstract.DataModel { let effects = this.effects; data.system.targets.forEach(async token => { if (!token.hit && !force) return; - if(this.hasSave && token.saved.success === true) { - effects = this.effects.filter(e => e.onSave === true) + if (this.hasSave && token.saved.success === true) { + effects = this.effects.filter(e => e.onSave === true); } - if(!effects.length) return; + if (!effects.length) return; effects.forEach(async e => { const actor = canvas.tokens.get(token.id)?.actor, effect = this.item.effects.get(e._id); @@ -479,38 +491,68 @@ export class DHBaseAction extends foundry.abstract.DataModel { /* SAVE */ async rollSave(target, event, message) { - if(!target?.actor) return; - target.actor.diceRoll({ - event, - title: 'Roll Save', - roll: { - trait: this.save.trait, - difficulty: this.save.difficulty, - type: "reaction" - }, - data: target.actor.getRollData() - }).then(async (result) => { - if(result) this.updateChatMessage(message, target.id, {result: result.roll.total, success: result.roll.success}); - }) + if (!target?.actor) return; + target.actor + .diceRoll({ + event, + title: 'Roll Save', + roll: { + trait: this.save.trait, + difficulty: this.save.difficulty, + type: 'reaction' + }, + data: target.actor.getRollData() + }) + .then(async result => { + if (result) + this.updateChatMessage(message, target.id, { + result: result.roll.total, + success: result.roll.success + }); + }); } - async updateChatMessage(message, targetId, changes, chain=true) { + async updateChatMessage(message, targetId, changes, chain = true) { setTimeout(async () => { const chatMessage = ui.chat.collection.get(message._id), msgTargets = chatMessage.system.targets, msgTarget = msgTargets.find(mt => mt.id === targetId); msgTarget.saved = changes; - await chatMessage.update({'system.targets': msgTargets}); - },100); - if(chain) { - if(message.system.source.message) this.updateChatMessage(ui.chat.collection.get(message.system.source.message), targetId, changes, false); + await chatMessage.update({ 'system.targets': msgTargets }); + }, 100); + if (chain) { + if (message.system.source.message) + this.updateChatMessage(ui.chat.collection.get(message.system.source.message), targetId, changes, false); const relatedChatMessages = ui.chat.collection.filter(c => c.system.source.message === message._id); relatedChatMessages.forEach(c => { this.updateChatMessage(c, targetId, changes, false); - }) + }); } } /* SAVE */ + + async toChat(origin) { + const cls = getDocumentClass('ChatMessage'); + const systemData = { + title: game.i18n.localize('DAGGERHEART.ActionType.action'), + origin: origin, + img: this.img, + name: this.name, + description: this.description, + actions: [] + }; + const msg = new cls({ + type: 'abilityUse', + user: game.user.id, + system: systemData, + content: await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/chat/ability-use.hbs', + systemData + ) + }); + + cls.create(msg.toObject()); + } } export class DHDamageAction extends DHBaseAction { @@ -525,7 +567,7 @@ export class DHDamageAction extends DHBaseAction { getFormulaValue(part, data) { let formulaValue = part.value; - if(this.hasRoll && part.resultBased && data.system.roll.result.duality === -1) return part.valueAlt; + if (this.hasRoll && part.resultBased && data.system.roll.result.duality === -1) return part.valueAlt; return formulaValue; } @@ -535,19 +577,19 @@ export class DHDamageAction extends DHBaseAction { if (!formula || formula == '') return; let roll = { formula: formula, total: formula }, bonusDamage = []; - - if(isNaN(formula)) formula = Roll.replaceFormulaData(formula, this.getRollData(data.system ?? data)); + + if (isNaN(formula)) formula = Roll.replaceFormulaData(formula, this.getRollData(data.system ?? data)); const config = { title: game.i18n.format('DAGGERHEART.Chat.DamageRoll.Title', { damage: this.name }), - roll: {formula}, - targets: (data.system?.targets.filter(t => t.hit) ?? data.targets), + roll: { formula }, + targets: data.system?.targets.filter(t => t.hit) ?? data.targets, hasSave: this.hasSave, source: data.system?.source, event }; - if(this.hasSave) config.onSave = this.save.damageMod; - if(data.system) { + if (this.hasSave) config.onSave = this.save.damageMod; + if (data.system) { config.source.message = data._id; config.directDamage = false; } @@ -597,7 +639,8 @@ export class DHHealingAction extends DHBaseAction { getFormulaValue(data) { let formulaValue = this.healing.value; - if(this.hasRoll && this.healing.resultBased && data.system.roll.result.duality === -1) return this.healing.valueAlt; + if (this.hasRoll && this.healing.resultBased && data.system.roll.result.duality === -1) + return this.healing.valueAlt; return formulaValue; } @@ -608,12 +651,12 @@ export class DHHealingAction extends DHBaseAction { if (!formula || formula == '') return; let roll = { formula: formula, total: formula }, bonusDamage = []; - + const config = { title: game.i18n.format('DAGGERHEART.Chat.HealingRoll.Title', { healing: game.i18n.localize(SYSTEM.GENERAL.healingTypes[this.healing.type].label) }), - roll: {formula}, + roll: { formula }, targets: (data.system?.targets ?? data.targets).filter(t => t.hit), messageType: 'healing', type: this.healing.type,