From c0901dd030274a6638801985d45cf03109a0ffe1 Mon Sep 17 00:00:00 2001 From: alakshana Date: Mon, 13 May 2024 08:22:50 -0400 Subject: [PATCH 1/7] changes to damage application for players when damageTarget is disabled --- module.json | 2 +- scripts/damage-application.mjs | 99 ++++++++++++++++++++++++++++++---- scripts/effective-tray.mjs | 25 ++++----- 3 files changed, 99 insertions(+), 27 deletions(-) diff --git a/module.json b/module.json index bf7f3e4..2c41f47 100644 --- a/module.json +++ b/module.json @@ -1,7 +1,7 @@ { "id": "effectivetray", "title": "Effective Tray", - "version": "1.1.15", + "version": "1.1.16", "compatibility": { "minimum": "11", "verified": "11" diff --git a/scripts/damage-application.mjs b/scripts/damage-application.mjs index 3e04147..82ebfa8 100644 --- a/scripts/damage-application.mjs +++ b/scripts/damage-application.mjs @@ -1,4 +1,22 @@ -import { socketID } from "./const.mjs"; +import { MODULE, socketID } from "./const.mjs"; + +/* -------------------------------------------- */ +/* Functions */ +/* -------------------------------------------- */ + +/** +* Check targets for ownership when determining which target selection mode to use. +* @param {Array} targets Array of objects with target data, including UUID. +*/ +async function ownershipCheck(targets) { + for (const target of targets) { + const token = await fromUuid(target.uuid); + if (token?.isOwner) return true; + else continue; + }; + return false; +}; + /* -------------------------------------------- */ /* Damage Application Extension (from dnd5e) */ @@ -9,13 +27,73 @@ const MULTIPLIERS = [[-1, "-1"], [0, "0"], [.25, "¼"], [.5, "½"], [1, "1"], [2 export default class EffectiveDAE extends dnd5e.applications.components.DamageApplicationElement { + /** @override */ + async connectedCallback() { + // Fetch the associated chat message + const messageId = this.closest("[data-message-id]")?.dataset.messageId; + this.chatMessage = game.messages.get(messageId); + if ( !this.chatMessage ) return; + + // Build the frame HTML only once (technically, we've built it twice by this point :weary:) + if ( !this.targetList ) { + const div = document.createElement("div"); + div.classList.add("card-tray", "damage-tray", "collapsible", "collapsed"); + div.innerHTML = ` + +
+
+
+ + +
+
    + +
    +
    + `; + this.replaceChildren(div); + this.applyButton = div.querySelector(".apply-damage"); + this.applyButton.addEventListener("click", this._onApplyDamage.bind(this)); + this.targetList = div.querySelector(".targets"); + this.targetSourceControl = this.querySelector(".target-source-control"); + this.targetSourceControl.querySelectorAll("button").forEach(b => + b.addEventListener("click", this._onChangeTargetMode.bind(this)) + ); + if ( !this.chatMessage.getFlag("dnd5e", "targets")?.length ) this.targetSourceControl.hidden = true; + + if (!game.settings.get(MODULE, "damageTarget")) { + const targets = this.chatMessage.getFlag("dnd5e", "targets"); + if (!await ownershipCheck(targets)) this.targetSourceControl.hidden = true; + }; + + div.querySelector(".collapsible-content").addEventListener("click", event => { + event.stopImmediatePropagation(); + }); + } + + this.targetingMode = this.targetSourceControl.hidden ? "selected" : "targeted"; + } + /** * Create a list entry for a single target. * @param {string} uuid UUID of the token represented by this entry. * Extends this method to remove checking for token owner. */ + /** @override */ buildTargetListEntry(uuid) { const token = fromUuidSync(uuid); + if (!token?.isOwner) return; const targetOptions = this.getTargetOptions(uuid); const { total, active } = this.calculateDamage(token, targetOptions); @@ -80,6 +158,7 @@ export default class EffectiveDAE extends dnd5e.applications.components.DamageAp * Extends this method to emit a request for the active GM client to damage a non-owned actor. * Special handling is required for the Set `this.damages.properties`. */ + /** @override */ async _onApplyDamage(event) { event.preventDefault(); for (const target of this.targetList.querySelectorAll("[data-target-uuid]")) { @@ -90,19 +169,17 @@ export default class EffectiveDAE extends dnd5e.applications.components.DamageAp await token?.applyDamage(this.damages, options); } else { - const damage = [] - for (const d of this.damages) { - const damageObject = { - properties: Array.from(d.properties), - type: d.type, - value: d.value - }; - damage.push(damageObject); - }; + const damage = []; + + // Convert damage properties to an Array for socket emission + this.damages.forEach(d => { + foundry.utils.mergeObject(d, { properties: Array.from(d.properties) }); + damage.push(d); + }); if (!game.users.activeGM) return ui.notifications.warn(game.i18n.localize("EFFECTIVETRAY.NOTIFICATION.NoActiveGMDamage")); await game.socket.emit(socketID, { type: "damage", data: { id, options, damage } }); }; } this.querySelector(".collapsible").dispatchEvent(new PointerEvent("click", { bubbles: true, cancelable: true })); } -}; \ No newline at end of file +} \ No newline at end of file diff --git a/scripts/effective-tray.mjs b/scripts/effective-tray.mjs index 8829e6f..fa21687 100644 --- a/scripts/effective-tray.mjs +++ b/scripts/effective-tray.mjs @@ -101,7 +101,7 @@ export class effectiveTray { tray.querySelector('ul.effects.unlist').insertAdjacentHTML("beforeend", contents); // Handle click events - tray.querySelector(`li[data-uuid="${uuid}.ActiveEffect.${effect.id}"]`)?.querySelector("button").addEventListener('click', async function() { + tray.querySelector(`li[data-uuid="${uuid}.ActiveEffect.${effect.id}"]`)?.querySelector("button").addEventListener('click', async() => { const mode = tray.querySelector(`[aria-pressed="true"]`)?.dataset?.mode; if (!mode || mode === "selected") { const actors = new Set(); @@ -177,12 +177,12 @@ export class effectiveTray { if (game.settings.get(MODULE, "dontCloseOnPress")) { const buttons = tray.querySelectorAll("button"); for (const button of buttons) { - button.addEventListener('click', async function() { + button.addEventListener('click', async() => { if (!tray.querySelector(".et-uncollapsed")) { await tray.classList.add("et-uncollapsed"); await new Promise(r => setTimeout(r, 108)); await tray.classList.remove("collapsed"); - } + }; }); }; }; @@ -253,9 +253,7 @@ export class effectiveDamage { if (message.flags?.dnd5e?.roll?.type === "damage") { if (!game.user.isGM) { if (message.whisper.length && !message.whisper.includes(game.user.id)) return; - const damageApplication = game.settings.get(MODULE, "damageTarget") ? - document.createElement("effective-damage-application") : - document.createElement("damage-application"); + const damageApplication = document.createElement("effective-damage-application"); damageApplication.classList.add("dnd5e2"); damageApplication.damages = dnd5e.dice.aggregateDamageRolls(message.rolls, { respectProperties: true }).map(roll => ({ value: roll.total, @@ -373,20 +371,17 @@ async function _effectSocket(request) { }; // Make the GM client apply damage to the requested targets -// Convert damage properties back from an Array into a Set async function _damageSocket(request) { if (game.user !== game.users.activeGM) return; const id = request.data.id; const options = request.data.options; const damage = []; - for (const d of request.data.damage) { - const damageObject = { - properties: new Set(d.properties), - type: d.type, - value: d.value - }; - damage.push(damageObject); - }; + + // Convert damage properties back into a Set for damage application + request.data.damage.forEach(d => { + foundry.utils.mergeObject(d, { properties: new Set(d.properties) }); + damage.push(d); + }); return await _applyTargetDamage(id, options, damage); }; From 940fc9683c3f88c1075177d1110c468b334e4344 Mon Sep 17 00:00:00 2001 From: alakshana Date: Mon, 13 May 2024 08:24:07 -0400 Subject: [PATCH 2/7] vscode format --- scripts/damage-application.mjs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/damage-application.mjs b/scripts/damage-application.mjs index 82ebfa8..9ecf412 100644 --- a/scripts/damage-application.mjs +++ b/scripts/damage-application.mjs @@ -18,10 +18,10 @@ async function ownershipCheck(targets) { }; - /* -------------------------------------------- */ - /* Damage Application Extension (from dnd5e) */ - /* Refer to dnd5e for full documentation */ - /* -------------------------------------------- */ +/* -------------------------------------------- */ +/* Damage Application Extension (from dnd5e) */ +/* Refer to dnd5e for full documentation */ +/* -------------------------------------------- */ const MULTIPLIERS = [[-1, "-1"], [0, "0"], [.25, "¼"], [.5, "½"], [1, "1"], [2, "2"]]; @@ -32,10 +32,10 @@ export default class EffectiveDAE extends dnd5e.applications.components.DamageAp // Fetch the associated chat message const messageId = this.closest("[data-message-id]")?.dataset.messageId; this.chatMessage = game.messages.get(messageId); - if ( !this.chatMessage ) return; + if (!this.chatMessage) return; // Build the frame HTML only once (technically, we've built it twice by this point :weary:) - if ( !this.targetList ) { + if (!this.targetList) { const div = document.createElement("div"); div.classList.add("card-tray", "damage-tray", "collapsible", "collapsed"); div.innerHTML = ` @@ -70,11 +70,11 @@ export default class EffectiveDAE extends dnd5e.applications.components.DamageAp this.targetSourceControl.querySelectorAll("button").forEach(b => b.addEventListener("click", this._onChangeTargetMode.bind(this)) ); - if ( !this.chatMessage.getFlag("dnd5e", "targets")?.length ) this.targetSourceControl.hidden = true; + if (!this.chatMessage.getFlag("dnd5e", "targets")?.length) this.targetSourceControl.hidden = true; if (!game.settings.get(MODULE, "damageTarget")) { const targets = this.chatMessage.getFlag("dnd5e", "targets"); - if (!await ownershipCheck(targets)) this.targetSourceControl.hidden = true; + if (!await ownershipCheck(targets)) this.targetSourceControl.hidden = true; }; div.querySelector(".collapsible-content").addEventListener("click", event => { From e12242973b73b662c470a1bc34f77f37de215a59 Mon Sep 17 00:00:00 2001 From: alakshana Date: Mon, 13 May 2024 08:30:39 -0400 Subject: [PATCH 3/7] {boolean} --- scripts/damage-application.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/damage-application.mjs b/scripts/damage-application.mjs index 9ecf412..811e2e2 100644 --- a/scripts/damage-application.mjs +++ b/scripts/damage-application.mjs @@ -7,6 +7,7 @@ import { MODULE, socketID } from "./const.mjs"; /** * Check targets for ownership when determining which target selection mode to use. * @param {Array} targets Array of objects with target data, including UUID. + * @returns {boolean} */ async function ownershipCheck(targets) { for (const target of targets) { From 77f3f3933222a4bd06f2c0885825870361661b17 Mon Sep 17 00:00:00 2001 From: alakshana Date: Mon, 13 May 2024 09:14:16 -0400 Subject: [PATCH 4/7] isOwner check somehow was added back --- scripts/damage-application.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/damage-application.mjs b/scripts/damage-application.mjs index 811e2e2..f7c1f57 100644 --- a/scripts/damage-application.mjs +++ b/scripts/damage-application.mjs @@ -94,7 +94,6 @@ export default class EffectiveDAE extends dnd5e.applications.components.DamageAp /** @override */ buildTargetListEntry(uuid) { const token = fromUuidSync(uuid); - if (!token?.isOwner) return; const targetOptions = this.getTargetOptions(uuid); const { total, active } = this.calculateDamage(token, targetOptions); From 6cd79e1d971fa87ce33bc04f1bff3b397b9f1718 Mon Sep 17 00:00:00 2001 From: alakshana Date: Mon, 13 May 2024 09:26:33 -0400 Subject: [PATCH 5/7] vscode lying to me --- scripts/damage-application.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/damage-application.mjs b/scripts/damage-application.mjs index f7c1f57..811e2e2 100644 --- a/scripts/damage-application.mjs +++ b/scripts/damage-application.mjs @@ -94,6 +94,7 @@ export default class EffectiveDAE extends dnd5e.applications.components.DamageAp /** @override */ buildTargetListEntry(uuid) { const token = fromUuidSync(uuid); + if (!token?.isOwner) return; const targetOptions = this.getTargetOptions(uuid); const { total, active } = this.calculateDamage(token, targetOptions); From 2f2e4558bc5445ab2073150d2ba748a3da39f02f Mon Sep 17 00:00:00 2001 From: alakshana Date: Mon, 13 May 2024 09:33:36 -0400 Subject: [PATCH 6/7] comments --- scripts/damage-application.mjs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/scripts/damage-application.mjs b/scripts/damage-application.mjs index 811e2e2..28cc7d7 100644 --- a/scripts/damage-application.mjs +++ b/scripts/damage-application.mjs @@ -7,7 +7,7 @@ import { MODULE, socketID } from "./const.mjs"; /** * Check targets for ownership when determining which target selection mode to use. * @param {Array} targets Array of objects with target data, including UUID. - * @returns {boolean} +* @returns {boolean} */ async function ownershipCheck(targets) { for (const target of targets) { @@ -28,6 +28,9 @@ const MULTIPLIERS = [[-1, "-1"], [0, "0"], [.25, "¼"], [.5, "½"], [1, "1"], [2 export default class EffectiveDAE extends dnd5e.applications.components.DamageApplicationElement { + /** + * Determine which target selection mode to use based on damageTarget setting state. + */ /** @override */ async connectedCallback() { // Fetch the associated chat message @@ -73,6 +76,7 @@ export default class EffectiveDAE extends dnd5e.applications.components.DamageAp ); if (!this.chatMessage.getFlag("dnd5e", "targets")?.length) this.targetSourceControl.hidden = true; + // If damageTarget setting is disabled, check if users own any targeted tokens, otherwise use "selected" mode if (!game.settings.get(MODULE, "damageTarget")) { const targets = this.chatMessage.getFlag("dnd5e", "targets"); if (!await ownershipCheck(targets)) this.targetSourceControl.hidden = true; @@ -154,11 +158,11 @@ export default class EffectiveDAE extends dnd5e.applications.components.DamageAp } /** - * Handle clicking the apply damage button. - * @param {PointerEvent} event Triggering click event. - * Extends this method to emit a request for the active GM client to damage a non-owned actor. - * Special handling is required for the Set `this.damages.properties`. - */ + * Handle clicking the apply damage button. + * @param {PointerEvent} event Triggering click event. + * Extends this method to emit a request for the active GM client to damage a non-owned actor. + * Special handling is required for the Set `this.damages.properties`. + */ /** @override */ async _onApplyDamage(event) { event.preventDefault(); From d86f15192e772c32ffb0222be93a4e6a2af41ec3 Mon Sep 17 00:00:00 2001 From: alakshana Date: Mon, 13 May 2024 09:42:44 -0400 Subject: [PATCH 7/7] isOwner came back...again --- scripts/damage-application.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/damage-application.mjs b/scripts/damage-application.mjs index 28cc7d7..d26d3d7 100644 --- a/scripts/damage-application.mjs +++ b/scripts/damage-application.mjs @@ -98,7 +98,6 @@ export default class EffectiveDAE extends dnd5e.applications.components.DamageAp /** @override */ buildTargetListEntry(uuid) { const token = fromUuidSync(uuid); - if (!token?.isOwner) return; const targetOptions = this.getTargetOptions(uuid); const { total, active } = this.calculateDamage(token, targetOptions);