From a8b98bed382501d85ae0aba889921213ebddebb4 Mon Sep 17 00:00:00 2001 From: Dapoolp Date: Fri, 8 Aug 2025 20:01:34 +0200 Subject: [PATCH] Fix duplicate messages --- module/applications/ui/chatLog.mjs | 35 --------- module/data/action/baseAction.mjs | 28 +++---- module/data/chat-message/adversaryRoll.mjs | 78 ++++++++----------- module/data/fields/action/targetField.mjs | 1 + module/dice/d20Roll.mjs | 6 +- module/dice/dhRoll.mjs | 3 +- module/documents/chatMessage.mjs | 26 +++---- .../feature_Rally_PydiMnNCKpd44SGS.json | 12 +-- ...ture_Rally__Level_5__TVeEyqmPPiRa2r3i.json | 12 +-- styles/less/ui/chat/chat.less | 32 ++++---- templates/dialogs/dice-roll/costSelection.hbs | 12 +-- .../global/partials/domain-card-item.hbs | 2 +- templates/ui/chat/parts/button-part.hbs | 6 +- templates/ui/chat/parts/roll-part.hbs | 6 +- templates/ui/chat/parts/target-part.hbs | 6 +- templates/ui/chat/roll.hbs | 2 +- 16 files changed, 96 insertions(+), 171 deletions(-) diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index 6c864d09..b9772829 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -33,14 +33,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo html.querySelectorAll('.simple-roll-button').forEach(element => element.addEventListener('click', event => this.onRollSimple(event, data.message)) ); - html.querySelectorAll('.target-container').forEach(element => { - element.addEventListener('mouseenter', this.hoverTarget); - element.addEventListener('mouseleave', this.unhoverTarget); - element.addEventListener('click', this.clickTarget); - }); - html.querySelectorAll('.button-target-selection').forEach(element => { - element.addEventListener('click', event => this.onTargetSelection(event, data.message)); - }); html.querySelectorAll('.healing-button').forEach(element => element.addEventListener('click', event => this.onHealing(event, data.message)) ); @@ -138,33 +130,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo }); } - onTargetSelection(event, message) { - event.stopPropagation(); - const msg = ui.chat.collection.get(message._id); - msg.system.targetMode = Boolean(event.target.dataset.targetHit); - } - - hoverTarget(event) { - event.stopPropagation(); - const token = canvas.tokens.get(event.currentTarget.dataset.token); - if (!token?.controlled) token._onHoverIn(event, { hoverOutOthers: true }); - } - - unhoverTarget(event) { - const token = canvas.tokens.get(event.currentTarget.dataset.token); - if (!token?.controlled) token._onHoverOut(event); - } - - clickTarget(event) { - event.stopPropagation(); - const token = canvas.tokens.get(event.currentTarget.dataset.token); - if (!token) { - ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.attackTargetDoesNotExist')); - return; - } - game.canvas.pan(token); - } - async onRollSimple(event, message) { const buttonType = event.target.dataset.type ?? 'damage', total = message.rolls.reduce((a, c) => a + Roll.fromJSON(c).total, 0), diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index 8f04d1a0..9a1d16c0 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -145,9 +145,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel if (this.rollDamage && this.damage.parts.length) await this.rollDamage(event, config); else if (this.trigger) await this.trigger(event, config); else if (this.hasSave || this.hasEffect) { - const roll = new Roll(''); + const roll = new CONFIG.Dice.daggerheart.DHRoll(''); roll._evaluated = true; - if (this.hasTarget) config.targetSelection = config.targets.length > 0; + if(config.hasTarget) config.targetSelection = config.targets.length > 0; await CONFIG.Dice.daggerheart.DHRoll.toMessage(roll, config); } } @@ -180,7 +180,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel hasHealing: this.damage?.parts?.length && this.type === 'healing', hasEffect: !!this.effects?.length, hasSave: this.hasSave, - hasTarget: true, selectedRollMode: game.settings.get('core', 'rollMode'), isFastForward: event.shiftKey, data: this.getRollData(), @@ -253,8 +252,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel ) ) this.update({ 'uses.value': this.uses.value + 1 }); - if(config.roll?.success || successCost) - (config.message ?? config.parent).update({'system.successConsumed': true}) + if(config.roll?.success || successCost) { + setTimeout(() => { + (config.message ?? config.parent).update({'system.successConsumed': true}) + }, 50); + } } /* */ @@ -375,15 +377,15 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel async updateChatMessage(message, targetId, changes, chain = true) { setTimeout(async () => { - const chatMessage = ui.chat.collection.get(message._id), - msgTarget = - chatMessage.system.targets.find(mt => mt.id === targetId) ?? - chatMessage.system.oldTargets.find(mt => mt.id === targetId); - msgTarget.saved = changes; + const chatMessage = ui.chat.collection.get(message._id); + await chatMessage.update({ - system: { - targets: chatMessage.system.targets, - oldTargets: chatMessage.system.oldTargets + flags: { + [game.system.id]: { + "reactionRolls": { + [targetId]: changes + } + } } }); }, 100); diff --git a/module/data/chat-message/adversaryRoll.mjs b/module/data/chat-message/adversaryRoll.mjs index 10c0ba03..1f6e1b6e 100644 --- a/module/data/chat-message/adversaryRoll.mjs +++ b/module/data/chat-message/adversaryRoll.mjs @@ -24,7 +24,6 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { title: new fields.StringField(), roll: new fields.ObjectField(), targets: targetsField(), - oldTargets: targetsField(), targetSelection: new fields.BooleanField({ initial: false }), hasRoll: new fields.BooleanField({ initial: false }), hasDamage: new fields.BooleanField({ initial: false }), @@ -64,26 +63,14 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { return actionItem.system.actionsList?.find(a => a.id === this.source.action); } - // get messageTemplate() { - // return 'systems/daggerheart/templates/ui/chat/roll.hbs'; - // } - get targetMode() { return this.targetSelection; } set targetMode(mode) { this.targetSelection = mode; - this.updateTargets(); this.registerTargetHook(); - this.parent.update( - { - system: { - targetSelection: this.targetSelection, - oldTargets: this.oldTargets - } - } - ); + this.updateTargets(); } get hitTargets() { @@ -91,31 +78,23 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { } async updateTargets() { - this.currentTargets = this.getTargetList(); - if(!this.targetSelection) { - this.currentTargets.forEach(ct => { - if(this.targets.find(t => t.actorId === ct.actorId)) return; - const indexTarget = this.oldTargets.findIndex(ot => ot.actorId === ct.actorId); - if(indexTarget === -1) - this.oldTargets.push(ct); - }); - if(this.hasSave) this.setPendingSaves(); - // if(this.currentTargets.length) { - if(!this.parent._id) return; - const updates = await this.parent.update( - { - system: { - oldTargets: this.oldTargets - } - } - ); - if(!updates && ui.chat.collection.get(this.parent.id)) - ui.chat.updateMessage(this.parent); - // } - } + if(!ui.chat.collection.get(this.parent.id)) return; + let targets; + if(this.targetSelection) + targets = this.targets; + else + targets = Array.from(game.user.targets).map(t => game.system.api.fields.ActionFields.TargetField.formatTarget(t)); + + this.parent.setFlag(game.system.id, "targets", targets); + await this.parent.updateSource({ + system: { + targetSelection: this.targetSelection + } + }); } registerTargetHook() { + if(!this.parent.isAuthor) return; if(this.targetSelection && this.targetHook !== null) { Hooks.off("targetToken", this.targetHook); this.targetHook = null; @@ -127,9 +106,10 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { prepareDerivedData() { if(this.hasTarget) { this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0; - this.updateTargets(); - this.registerTargetHook(); - if(this.targetSelection === true) { + this.currentTargets = this.getTargetList(); + this. registerTargetHook(); + + if(this.targetSelection === true && this.hasRoll) { this.targetShort = this.targets.reduce((a,c) => { if(c.hit) a.hit += 1; else a.miss += 1; @@ -140,17 +120,21 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { } this.canViewSecret = this.parent.speakerActor?.testUserPermission(game.user, 'OBSERVER'); + this.canButtonApply = game.user.isGM; } getTargetList() { - return this.targetSelection !== true - ? Array.from(game.user.targets).map(t =>{ - const target = game.system.api.fields.ActionFields.TargetField.formatTarget(t), - oldTarget = this.targets.find(ot => ot.actorId === target.actorId) ?? this.oldTargets.find(ot => ot.actorId === target.actorId); - if(oldTarget) return oldTarget; - return target; - }) - : this.targets; + const targets = this.targetSelection && this.parent.isAuthor ? this.targets : (this.parent.getFlag(game.system.id, "targets") ?? this.targets), + reactionRolls = this.parent.getFlag(game.system.id, "reactionRolls"); + + if(reactionRolls) { + Object.entries(reactionRolls).forEach(([k, r]) => { + const target = targets.find(t => t.id === k); + if(target) target.saved = r; + }); + } + + return targets; } setPendingSaves() { diff --git a/module/data/fields/action/targetField.mjs b/module/data/fields/action/targetField.mjs index 681f8353..bfb01db9 100644 --- a/module/data/fields/action/targetField.mjs +++ b/module/data/fields/action/targetField.mjs @@ -15,6 +15,7 @@ export default class TargetField extends fields.SchemaField { static prepareConfig(config) { if (!this.target?.type) return []; + config.hasTarget = true; let targets; if (this.target?.type === CONFIG.DH.GENERAL.targetTypes.self.id) targets = [this.actor.token ?? this.actor.prototypeToken]; diff --git a/module/dice/d20Roll.mjs b/module/dice/d20Roll.mjs index 62dc0d7f..e9583339 100644 --- a/module/dice/d20Roll.mjs +++ b/module/dice/d20Roll.mjs @@ -141,6 +141,7 @@ export default class D20Roll extends DHRoll { static postEvaluate(roll, config = {}) { const data = super.postEvaluate(roll, config); data.type = config.roll?.type; + data.difficulty = config.roll.difficulty; if (config.targets?.length) { config.targetSelection = true; config.targets.forEach(target => { @@ -148,10 +149,9 @@ export default class D20Roll extends DHRoll { target.hit = roll.isCritical || roll.total >= difficulty; }); data.success = config.targets.some(target => target.hit) - } else if (config.roll.difficulty) { - data.difficulty = config.roll.difficulty; + } else if (config.roll.difficulty) data.success = roll.isCritical || roll.total >= config.roll.difficulty; - } + data.advantage = { type: config.roll.advantage, dice: roll.dAdvantage?.denomination, diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index 65292800..488f5a7e 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -2,7 +2,7 @@ import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs'; export default class DHRoll extends Roll { baseTerms = []; - constructor(formula, data, options) { + constructor(formula, data = {}, options = {}) { super(formula, data, options); if (!this.data || !Object.keys(this.data).length) this.data = options.data; } @@ -94,6 +94,7 @@ export default class DHRoll extends Roll { system: config, rolls: [roll] }; + config.selectedRollMode ??= game.settings.get('core', 'rollMode'); if(roll._evaluated) return await cls.create(msg, { rollMode: config.selectedRollMode }); return msg; } diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs index fb167cc7..06a9f147 100644 --- a/module/documents/chatMessage.mjs +++ b/module/documents/chatMessage.mjs @@ -1,11 +1,5 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { async renderHTML() { - // if (this.system.messageTemplate) - // this.content = await foundry.applications.handlebars.renderTemplate(this.system.messageTemplate, { - // ...this.system, - // _source: this.system._source - // }); - const actor = game.actors.get(this.speaker.actor); const actorData = actor && this.isContentVisible ? actor : { img: this.author.avatar ? this.author.avatar : 'icons/svg/mystery-man.svg', @@ -20,17 +14,6 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { return html; } - /* async _preCreate(data, options, user) { - options.speaker = ChatMessage.getSpeaker(); - const rollActorOwner = data.rolls?.[0]?.data?.parent?.owner; - if (rollActorOwner) { - data.author = rollActorOwner ? rollActorOwner.id : data.author; - await this.updateSource({ author: rollActorOwner ?? user }); - } - - return super._preCreate(data, options, rollActorOwner ?? user); - } */ - enrichChatMessage(html) { const elements = html.querySelectorAll('[data-perm-id]'); elements.forEach(e => { @@ -72,6 +55,10 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { element.addEventListener('mouseleave', this.unhoverTarget); element.addEventListener('click', this.clickTarget); }); + + html.querySelectorAll('.button-target-selection').forEach(element => { + element.addEventListener('click', this.onTargetSelection.bind(this)); + }); } getTargetList() { @@ -173,4 +160,9 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { } game.canvas.pan(token); } + + onTargetSelection(event) { + event.stopPropagation(); + this.system.targetMode = Boolean(event.target.dataset.targetHit); + } } diff --git a/src/packs/classes/feature_Rally_PydiMnNCKpd44SGS.json b/src/packs/classes/feature_Rally_PydiMnNCKpd44SGS.json index cd9fe232..56a3afb9 100644 --- a/src/packs/classes/feature_Rally_PydiMnNCKpd44SGS.json +++ b/src/packs/classes/feature_Rally_PydiMnNCKpd44SGS.json @@ -22,15 +22,7 @@ "description": "", "chatDisplay": false, "actionType": "action", - "cost": [ - { - "scalable": false, - "key": "Y7waM3ljoRLyk38N", - "value": 1, - "keyIsID": true, - "step": null - } - ], + "cost": [], "uses": { "value": null, "max": "", @@ -74,7 +66,7 @@ { "key": "system.bonuses.rally", "mode": 2, - "value": "1d6", + "value": "d6", "priority": null } ], diff --git a/src/packs/classes/feature_Rally__Level_5__TVeEyqmPPiRa2r3i.json b/src/packs/classes/feature_Rally__Level_5__TVeEyqmPPiRa2r3i.json index 1f85310e..243b179f 100644 --- a/src/packs/classes/feature_Rally__Level_5__TVeEyqmPPiRa2r3i.json +++ b/src/packs/classes/feature_Rally__Level_5__TVeEyqmPPiRa2r3i.json @@ -22,15 +22,7 @@ "description": "", "chatDisplay": true, "actionType": "action", - "cost": [ - { - "scalable": false, - "key": "oxv0m8AFUQVFKtZ4", - "value": 1, - "keyIsID": true, - "step": null - } - ], + "cost": [], "uses": { "value": null, "max": "", @@ -74,7 +66,7 @@ { "key": "system.bonuses.rally", "mode": 2, - "value": "1d8", + "value": "d8", "priority": null } ], diff --git a/styles/less/ui/chat/chat.less b/styles/less/ui/chat/chat.less index b66e6477..c59e8d20 100644 --- a/styles/less/ui/chat/chat.less +++ b/styles/less/ui/chat/chat.less @@ -22,19 +22,6 @@ } } } - - [data-use-perm='false'] { - pointer-events: none; - border-color: transparent; - } - [data-view-perm='false'] { - > * { - display: none; - } - &::after { - content: '??'; - } - } } .daggerheart, @@ -43,6 +30,19 @@ --text-color: light-dark(@dark-blue, @golden); --bg-color: light-dark(@dark-blue-40, @golden-40); + [data-use-perm='false'] { + pointer-events: none; + border-color: transparent; + } + [data-view-perm='false'] { + &[data-perm-hidden='true'], > * { + display: none; + } + &::after { + content: '??'; + } + } + &.duality { &.hope { --text-color: @golden; @@ -177,10 +177,6 @@ } } } - - .roll-difficulty { - margin-top: -5px; - } } } @@ -254,7 +250,7 @@ .button-target-selection { flex: 1; text-align: center; - padding: 5px 0; + margin: -5px 0; } .button-target-selection:hover, diff --git a/templates/dialogs/dice-roll/costSelection.hbs b/templates/dialogs/dice-roll/costSelection.hbs index cf906538..9c8479b7 100644 --- a/templates/dialogs/dice-roll/costSelection.hbs +++ b/templates/dialogs/dice-roll/costSelection.hbs @@ -5,25 +5,25 @@
  • - - + +
    - +
  • {{/if}} {{#each costs as | cost index |}}
  • - - + +
    {{#if (and scalable maxStep)}} {{/if}} - +
  • {{/each}} diff --git a/templates/sheets/global/partials/domain-card-item.hbs b/templates/sheets/global/partials/domain-card-item.hbs index 37d6a0e2..6ee46866 100644 --- a/templates/sheets/global/partials/domain-card-item.hbs +++ b/templates/sheets/global/partials/domain-card-item.hbs @@ -1,4 +1,4 @@ -
  • +
  • {{#if hasDamage}} {{#unless (empty damage)}} - + {{#if canButtonApply}}{{/if}} {{else}} {{/unless}} {{/if}} {{#if hasHealing}} {{#unless (empty damage)}} - + {{#if canButtonApply}}{{/if}} {{else}} {{/unless}} {{/if}} - {{#if hasEffect}}{{/if}} + {{#if (and hasEffect canButtonApply)}}{{/if}}
    \ No newline at end of file diff --git a/templates/ui/chat/parts/roll-part.hbs b/templates/ui/chat/parts/roll-part.hbs index db810bcd..1d7f3d2b 100644 --- a/templates/ui/chat/parts/roll-part.hbs +++ b/templates/ui/chat/parts/roll-part.hbs @@ -14,11 +14,11 @@
    {{#if roll.difficulty}} - {{#if canViewSecret}} + {{!-- {{#if canViewSecret}} --}} difficulty {{roll.difficulty}} - {{else}} + {{!-- {{else}} {{localize (ifThen roll.success "DAGGERHEART.GENERAL.success" "DAGGERHEART.GENERAL.failure")}} - {{/if}} + {{/if}} --}} {{/if}} diff --git a/templates/ui/chat/parts/target-part.hbs b/templates/ui/chat/parts/target-part.hbs index 94810a03..51b9e79c 100644 --- a/templates/ui/chat/parts/target-part.hbs +++ b/templates/ui/chat/parts/target-part.hbs @@ -14,7 +14,7 @@
    - {{#if targets.length}} + {{#if (and parent.isAuthor targets.length)}}
    @@ -29,7 +29,7 @@
    -
    {{name}}
    +
    {{name}}
    {{#if (and ../targetSelection ../hasRoll)}}
    {{#if hit}} @@ -40,7 +40,7 @@
    {{/if}}
    - {{#if (and ../hasSave (or hit (not @root.targetSelection)))}} + {{#if (and ../hasSave (or hit (not @root.hasRoll)))}}
    diff --git a/templates/ui/chat/roll.hbs b/templates/ui/chat/roll.hbs index 37e1aef2..2e6f5d24 100644 --- a/templates/ui/chat/roll.hbs +++ b/templates/ui/chat/roll.hbs @@ -5,4 +5,4 @@ {{#if hasTarget}}{{> 'systems/daggerheart/templates/ui/chat/parts/target-part.hbs'}}{{/if}}
    -{{> 'systems/daggerheart/templates/ui/chat/parts/button-part.hbs'}} \ No newline at end of file +{{#if (or parent.isAuthor canButtonApply)}}{{> 'systems/daggerheart/templates/ui/chat/parts/button-part.hbs'}}{{/if}} \ No newline at end of file