From c0a2ed022cd106a6b6c92791452ca87775da3d69 Mon Sep 17 00:00:00 2001 From: juanferrer Date: Fri, 8 May 2026 06:42:13 +0100 Subject: [PATCH 01/20] WIP: V14 --- src/module/actor/actor.js | 14 ++++++++------ src/module/actor/sheets/base-actor-sheet.js | 4 ++-- src/module/combat/combat.js | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/module/actor/actor.js b/src/module/actor/actor.js index 51676c72..2c6e6600 100644 --- a/src/module/actor/actor.js +++ b/src/module/actor/actor.js @@ -39,6 +39,7 @@ export class DemonlordActor extends Actor { * @override */ prepareBaseData() { + super.prepareBaseData() const system = this.system // Set the base perception equal to intellect if (this.type === 'character') { @@ -143,6 +144,7 @@ export class DemonlordActor extends Actor { * @override */ prepareDerivedData() { + super.prepareDerivedData() const system = this.system // We can reapply some active effects if we know they happened @@ -959,7 +961,7 @@ getTargetAttackBane(target) { } // Add concentration if it's in the spell duration - const concentrate = CONFIG.statusEffects.find(e => e.id === 'concentrate') + const concentrate = CONFIG.statusEffects['concentrate'] if ( spell.system.duration.toLowerCase().includes('concentration') && this.effects.find(e => e.statuses?.has('concentrate')) === undefined && @@ -1141,7 +1143,7 @@ getTargetAttackBane(target) { let roll = await actor.rollAttributeChallenge(attribute, 0, 0, {mode: 'stunnedRoll'}) const targetNumber = game.settings.get('demonlord', 'optionalRuleDieRollsMode') === 'b' ? 11 : 10 if (roll.total < targetNumber) { - const stunnedEffect = foundry.utils.deepClone(CONFIG.statusEffects.find(e => e.id === 'stunned')) + const stunnedEffect = foundry.utils.deepClone(CONFIG.statusEffects['stunned']) stunnedEffect['statuses'] = stunnedEffect.id stunnedEffect.duration.rounds = 1 await ActiveEffect.create(stunnedEffect, { @@ -1159,7 +1161,7 @@ getTargetAttackBane(target) { const isStunned = actor.effects.find(e => e.statuses?.has('stunned')) === undefined ? false : true await actor.update({ 'system.characteristics.insanity.value': newValue }) if (!isFrightened) { - const frightenedEffect = foundry.utils.deepClone(CONFIG.statusEffects.find(e => e.id === 'frightened')) + const frightenedEffect = foundry.utils.deepClone(CONFIG.statusEffects['frightened']) frightenedEffect['statuses'] = frightenedEffect.id frightenedEffect.duration.rounds = newValue @@ -1206,7 +1208,7 @@ getTargetAttackBane(target) { // Nested function async function setFrightenedAffliction(durationRoll) { - const frightenedEffect = foundry.utils.deepClone(CONFIG.statusEffects.find(e => e.id === 'frightened')) + const frightenedEffect = foundry.utils.deepClone(CONFIG.statusEffects['frightened']) frightenedEffect['statuses'] = frightenedEffect.id frightenedEffect.duration.rounds = durationRoll.total await ActiveEffect.create(frightenedEffect, { @@ -1564,7 +1566,7 @@ getTargetAttackBane(target) { rollFormula: null, }) if (roll.total < targetNumber) { - const frightenedEffect = foundry.utils.deepClone(CONFIG.statusEffects.find(e => e.id === 'frightened')) + const frightenedEffect = foundry.utils.deepClone(CONFIG.statusEffects['frightened']) frightenedEffect['statuses'] = frightenedEffect.id await ActiveEffect.create(frightenedEffect, { parent: actor, @@ -1911,7 +1913,7 @@ getTargetAttackBane(target) { getSizeFromString(sizeString) { let result = 0 - if (sizeString.includes("/")) { + if (sizeString.toString().includes("/")) { const [numerator, denominator] = sizeString.split("/") result = parseInt(numerator) / parseInt(denominator) } else if (['½', '¼', '⅛'].includes(sizeString)) { diff --git a/src/module/actor/sheets/base-actor-sheet.js b/src/module/actor/sheets/base-actor-sheet.js index 2812f155..f1e20aa7 100644 --- a/src/module/actor/sheets/base-actor-sheet.js +++ b/src/module/actor/sheets/base-actor-sheet.js @@ -521,7 +521,7 @@ export default class DLBaseActorSheet extends HandlebarsApplicationMixin(ActorSh } input.checked = true - const affliction = CONFIG.statusEffects.find(a => a.id === afflictionId) + const affliction = CONFIG.statusEffects[afflictionId] if (!affliction) return false affliction['statuses'] = [affliction.id] @@ -541,7 +541,7 @@ export default class DLBaseActorSheet extends HandlebarsApplicationMixin(ActorSh let result = await this.actor.rollAttributeChallenge(attribute, html.form.elements.boonsbanes.value, html.form.elements.modifier.value) if (result._total >= 10 || game.settings.get('demonlord', 'optionalRuleDieRollsMode') === 'b' && result._total >= 11) { affliction['statuses'] = [affliction.id] - const effect = CONFIG.statusEffects.find(a => a.id === "helped") + const effect = CONFIG.statusEffects["helped"] effect['statuses'] = [effect.id] if (game.user.isGM) { await ActiveEffect.create(effect, { diff --git a/src/module/combat/combat.js b/src/module/combat/combat.js index 5dab69db..06f5aec6 100644 --- a/src/module/combat/combat.js +++ b/src/module/combat/combat.js @@ -645,7 +645,7 @@ Hooks.on('targetToken', async (user, target, isTargeted) => { }) return } - const surrounded = CONFIG.statusEffects.find(a => a.id === "surrounded") + const surrounded = CONFIG.statusEffects["surrounded"] surrounded['statuses'] = [surrounded.id] let targetSize = Math.max(target.document.width, target.document.height) let numberOfSurrounders = await getNumberOfSurrounders(target, targetSize) From 5bc423d90aa2c54adc5fe5f42d20790046bb9231 Mon Sep 17 00:00:00 2001 From: juanferrer Date: Fri, 8 May 2026 07:34:10 +0100 Subject: [PATCH 02/20] Remove warnings from deprecated CONST.ACTIVE_EFFECT_MODES --- src/module/active-effects/item-effects.js | 16 ++++++++-------- src/module/actor/actor.js | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/module/active-effects/item-effects.js b/src/module/active-effects/item-effects.js index f50eaa8a..86299a1f 100644 --- a/src/module/active-effects/item-effects.js +++ b/src/module/active-effects/item-effects.js @@ -4,54 +4,54 @@ import { plusify } from '../utils/utils' export const multiplyEffect = (key, value, priority) => ({ key: key, value: parseFloat(value), - mode: CONST.ACTIVE_EFFECT_MODES.MULTIPLY, + mode: CONST.ACTIVE_EFFECT_CHANGE_TYPES.MULTIPLY, priority: priority }) export const addEffect = (key, value, priority, noPlusify=false) => ({ key: key, value: noPlusify ? value : (value ? plusify(value) : 0), - mode: CONST.ACTIVE_EFFECT_MODES.ADD, + mode: CONST.ACTIVE_EFFECT_CHANGE_TYPES.ADD, priority: priority }) export const concatDiceEffect = (key, value) => ({ key: key, value: value ? '+' + String(value) : null, - mode: CONST.ACTIVE_EFFECT_MODES.ADD, + mode: CONST.ACTIVE_EFFECT_CHANGE_TYPES.ADD, }) export const concatString = (key, value, separator = '') => ({ key: key, value: value ? value + separator : null, - mode: CONST.ACTIVE_EFFECT_MODES.ADD, + mode: CONST.ACTIVE_EFFECT_CHANGE_TYPES.ADD, }) export const overrideEffect = (key, value, priority, noParse=false) => ({ key: key, value: noParse ? value : parseInt(value), - mode: CONST.ACTIVE_EFFECT_MODES.OVERRIDE, + mode: CONST.ACTIVE_EFFECT_CHANGE_TYPES.OVERRIDE, priority: priority }) export const upgradeEffect = (key, value, priority) => ({ key: key, value: parseInt(value), - mode: CONST.ACTIVE_EFFECT_MODES.UPGRADE, + mode: CONST.ACTIVE_EFFECT_CHANGE_TYPES.UPGRADE, priority: priority }) export const downgradeEffect = (key, value, priority) => ({ key: key, value: parseInt(value), - mode: CONST.ACTIVE_EFFECT_MODES.DOWNGRADE, + mode: CONST.ACTIVE_EFFECT_CHANGE_TYPES.DOWNGRADE, priority: priority }) export const addObject = (key, value) => ({ key: key, value: JSON.stringify(value), - mode: CONST.ACTIVE_EFFECT_MODES.ADD, + mode: CONST.ACTIVE_EFFECT_CHANGE_TYPES.ADD, }) const falsyChangeFilter = change => Boolean(change?.value) diff --git a/src/module/actor/actor.js b/src/module/actor/actor.js index 2c6e6600..4e0adba5 100644 --- a/src/module/actor/actor.js +++ b/src/module/actor/actor.js @@ -405,7 +405,7 @@ export class DemonlordActor extends Actor { } async _handleOnDeleteDescendantDocuments(documents) { - + // Also, if any of the effects in the deleted documents contains an affliction (and it's the last instance of this affliction), remove it if (['character', 'creature'].includes(this.type)) { for (const doc of documents.filter(d => d.effects?.some(e => e.changes?.some(c => c.key === 'system.maluses.affliction')))) { @@ -525,7 +525,7 @@ getTargetAttackBane(target) { // Adjust bane if source of affliction can be seen, actor already has 1 (frightened), we need to add the difference // 0 - no bane // 1 - creature is horrifying, creature is frightening (only 2025 trtait mode) - // 2 - creature firhtened + // 2 - creature firhtened if (attacker.isFrightenedFrom(target)) baneValue += 2 return baneValue } @@ -1191,7 +1191,7 @@ getTargetAttackBane(target) { changes: [{ key: 'system.bonuses.challenge.boons.will', value: -1, - mode: CONST.ACTIVE_EFFECT_MODES.ADD, + mode: CONST.ACTIVE_EFFECT_CHANGE_TYPES.ADD, }, ], flags: { demonlord: { @@ -1228,7 +1228,7 @@ getTargetAttackBane(target) { changes: [{ key: 'system.bonuses.challenge.boons.will', value: (target.system.willChallengeRollBanes)*-1, - mode: CONST.ACTIVE_EFFECT_MODES.ADD, + mode: CONST.ACTIVE_EFFECT_CHANGE_TYPES.ADD, }, ], flags: { demonlord: { @@ -1254,7 +1254,7 @@ getTargetAttackBane(target) { changes: [{ key: 'system.bonuses.challenge.boons.will', value: darkMagicSpellsKnown.length, - mode: CONST.ACTIVE_EFFECT_MODES.ADD, + mode: CONST.ACTIVE_EFFECT_CHANGE_TYPES.ADD, }, ], flags: { demonlord: { From 6a4b19ef6bca147288ece48c5633e24952f2d8a0 Mon Sep 17 00:00:00 2001 From: juanferrer Date: Fri, 8 May 2026 07:35:15 +0100 Subject: [PATCH 03/20] Hide the default status icons --- src/module/demonlord.js | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/module/demonlord.js b/src/module/demonlord.js index bda935de..c12fd155 100644 --- a/src/module/demonlord.js +++ b/src/module/demonlord.js @@ -216,26 +216,29 @@ Hooks.once('setup', function () { const effects = DLAfflictions.buildAll() - // Add the default status icons if the setting is not on - if (!game.settings.get('demonlord', 'statusIcons')) { - for (const effect of CONFIG.statusEffects) { - effects.push({ - id: effect.id, - name: effect.name, - img: effect.img, - }) + // Hide the default status icons if the setting is on + if (game.settings.get('demonlord', 'statusIcons')) { + // Regardless of the setting, keep the "invisible" status so that actors can turn invisible + // And dead, otherwise can't "kill 'em ded" + const importantEffects = ['dead', 'invisible'] + + for (const statusEffect of CONFIG.statusEffects) { + if (!importantEffects.includes(statusEffect.id)) { + statusEffect.hud = false + } } } - // Regardless of the setting, add the "invisible" status so that actors can turn invisible - // And dead, otherwise can't "kill 'em ded" - else { - effects.push(CONFIG.statusEffects.find(e => e.id === 'dead')) - effects.push(CONFIG.statusEffects.find(e => e.id === 'invisible')) - effects.push(CONFIG.statusEffects.find(e => e.id === 'dead')) - } - - CONFIG.statusEffects = effects + // Finally register all the system effects + for (const effect of effects) { + CONFIG.statusEffects[effect.id] = { + id: effect.id, + name: effect.name, + img: effect.icon, + hud: true, + order: effect.order + } + } // Set active effect keys-labels DLActiveEffectConfig.initializeChangeKeys() @@ -367,7 +370,7 @@ Hooks.on('updateActor', async (actor, updateData, options) => { export async function findAddEffect(actor, effectId, overlay) { if (!actor.effects.find(e => e.statuses?.has(effectId)) && !actor.isImmuneToAffliction(effectId)) { - const effect = CONFIG.statusEffects.find(e => e.id === effectId) + const effect = CONFIG.statusEffects[effectId] if (!effect) { ui.notifications.error(game.i18n.localize('DL.UnknownEffect') + ': ' + effectId) return From c8c1fdedab8ec43b082bc873bd4e2b734f50407c Mon Sep 17 00:00:00 2001 From: juanferrer Date: Fri, 8 May 2026 08:17:06 +0100 Subject: [PATCH 04/20] Remove default ActiveEffectConfig sheet --- src/module/demonlord.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/module/demonlord.js b/src/module/demonlord.js index c12fd155..6998a1a2 100644 --- a/src/module/demonlord.js +++ b/src/module/demonlord.js @@ -79,6 +79,7 @@ Hooks.once('init', async function () { CONFIG.Actor.documentClass = DemonlordActor CONFIG.Token.objectClass = DemonlordToken CONFIG.Item.documentClass = DemonlordItem + foundry.applications.apps.DocumentSheetConfig.unregisterSheet(ActiveEffect, "core", foundry.applications.sheets.ActiveEffectConfig, {}) foundry.applications.apps.DocumentSheetConfig.registerSheet(ActiveEffect, "demonlord", DLActiveEffectConfig, {makeDefault: true}) CONFIG.ui.combat = DLCombatTracker CONFIG.Combatant.documentClass = DLCombatant From bd6766c1fee53c045b41a6e0e03f128700d42aaf Mon Sep 17 00:00:00 2001 From: juanferrer Date: Fri, 8 May 2026 08:41:09 +0100 Subject: [PATCH 05/20] WIP: Fixing active effects --- .../sheets/active-effect-config.js | 4 +- .../item/parts/AE-config-changes.hbs | 37 +++------- .../item/parts/AE-config-details.hbs | 13 ++-- .../item/parts/AE-config-duration.hbs | 70 +++++++++++-------- 4 files changed, 61 insertions(+), 63 deletions(-) diff --git a/src/module/active-effects/sheets/active-effect-config.js b/src/module/active-effects/sheets/active-effect-config.js index b5b447bc..3a6d4777 100644 --- a/src/module/active-effects/sheets/active-effect-config.js +++ b/src/module/active-effects/sheets/active-effect-config.js @@ -42,8 +42,8 @@ export class DLActiveEffectConfig extends foundry.applications.sheets.ActiveEffe isActorEffect: this.document.parent.documentName === 'Actor', isItemEffect: this.document.parent.documentName === 'Item', descriptionHTML: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.description, {secrets: this.document.isOwner}), - modes: Object.entries(CONST.ACTIVE_EFFECT_MODES).reduce((obj, e) => { - obj[e[1]] = game.i18n.localize('EFFECT.MODE_' + e[0]) + modes: Object.entries(CONST.ACTIVE_EFFECT_CHANGE_TYPES).reduce((obj, e) => { + obj[e[1]] = game.i18n.localize('EFFECT.CHANGES.TYPES.' + e[0]) return obj }, {}), } diff --git a/src/templates/item/parts/AE-config-changes.hbs b/src/templates/item/parts/AE-config-changes.hbs index 6e152516..39fae54b 100644 --- a/src/templates/item/parts/AE-config-changes.hbs +++ b/src/templates/item/parts/AE-config-changes.hbs @@ -1,31 +1,16 @@
-
-
{{localize "EFFECT.ChangeKey"}}
-
{{localize "EFFECT.ChangeMode"}}
-
{{localize "EFFECT.ChangeValue"}}
-
{{localize "EFFECT.ChangePriority"}}
-
+
+
{{localize "EFFECT.FIELDS.changes.element.key.label"}}
+
{{localize "EFFECT.FIELDS.changes.element.type.label"}}
+
{{localize "EFFECT.FIELDS.changes.element.value.label"}}
+
{{localize "EFFECT.FIELDS.changes.element.priority.label"}}
+
+ +
-
    - {{#each source.changes as |change i|}} - {{#with ../fields.changes.element.fields as |changeFields|}} -
  1. -
    - {{formInput changeFields.key name=(concat "changes." i ".key") value=change.key choices=@root.availableChangeKeys}} -
    -
    - {{formInput changeFields.mode name=(concat "changes." i ".mode") value=change.mode choices=@root.modes}} -
    -
    - {{formInput changeFields.value name=(concat "changes." i ".value") value=change.value}} -
    -
    - {{formInput changeFields.priority name=(concat "changes." i ".priority") value=change.priority - placeholder=(lookup ../../priorities change.mode)}} -
    -
    -
  2. - {{/with}} +
      + {{#each changes as |change|}} + {{{change}}} {{/each}}
diff --git a/src/templates/item/parts/AE-config-details.hbs b/src/templates/item/parts/AE-config-details.hbs index 61d26114..ead03ef6 100644 --- a/src/templates/item/parts/AE-config-details.hbs +++ b/src/templates/item/parts/AE-config-details.hbs @@ -1,13 +1,14 @@ -
+
{{formGroup fields.tint value=source.tint rootId=rootId placeholder="#ffffff"}} {{formGroup fields.description value=source.description rootId=rootId}} {{formGroup fields.disabled value=source.disabled rootId=rootId}} {{#if isActorEffect}} - {{formGroup fields.origin value=source.origin rootId=rootId disabled=true}} + {{formGroup fields.origin value=source.origin rootId=rootId disabled=true}} + {{else if isItemEffect}} + {{formGroup fields.transfer value=source.transfer rootId=rootId}} {{/if}} - {{#if isItemEffect}} - {{formGroup fields.transfer value=source.transfer rootId=rootId label=legacyTransfer.label hint=legacyTransfer.hint}} - {{/if}} -
\ No newline at end of file + {{formGroup fields.statuses value=source.statuses options=statuses sort=true rootId=rootId classes="statuses"}} + {{formGroup fields.showIcon value=source.showIcon options=showIconOptions rootId=rootId}} +
diff --git a/src/templates/item/parts/AE-config-duration.hbs b/src/templates/item/parts/AE-config-duration.hbs index db58bcde..bffd8ead 100644 --- a/src/templates/item/parts/AE-config-duration.hbs +++ b/src/templates/item/parts/AE-config-duration.hbs @@ -1,37 +1,49 @@ -
-
- {{formGroup fields.duration.fields.seconds value=source.duration.seconds rootId=rootId}} - {{formGroup fields.duration.fields.startTime value=source.duration.startTime rootId=rootId}} -
- -
-
- -
- - {{formInput fields.duration.fields.rounds value=source.duration.rounds - id=(concat rootId "-duration.rounds")}} - - {{formInput fields.duration.fields.turns value=source.duration.turns - id=(concat rootId "-duration.turns")}} +
+ {{#if start}} +
+ {{localize "EFFECT.START.Header"}} +
+ {{localize "EFFECT.FIELDS.start.time.label"}} +
{{start.time}}
-
-
- + {{#if start.combat}} +
+ {{localize "DOCUMENT.Combat"}} +
+ {{localize "EFFECT.START.Combat" round=start.round turn=start.turn}} +
+
+ {{#if start.combatant}} +
+ {{localize "DOCUMENT.Combatant"}} +
+ {{localize "EFFECT.START.Combatant" combatant=start.combatantName initiative=start.combatantInitiative}} +
+
+ {{/if}} + {{/if}} +
+ {{/if}} +
+ {{localize "EFFECT.DURATION.Header"}} +
+
- - {{formInput fields.duration.fields.startRound value=source.duration.startRound - id=(concat rootId "-duration.startRound")}} - - {{formInput fields.duration.fields.startTurn value=source.duration.startTurn - id=(concat rootId "-duration.startTurn")}} + {{formInput fields.duration.fields.value value=source.duration.value id=(concat rootId "-duration.value") + aria=(object label=(localize "EFFECT.FIELDS.duration.value.label"))}} + {{formInput fields.duration.fields.units value=source.duration.units id=(concat rootId "-duration.units") + options=durationUnits aria=(object label=(localize "EFFECT.FIELDS.duration.units.label"))}}
+ {{formGroup fields.duration.fields.expiry choices=expiryEvents value=source.duration.expiry rootId=rootId}}
{{localize "DL.SpecialDurationLabel"}} - -
+ +
From dbcb093860d83e6df11c2195edfc84daef984622 Mon Sep 17 00:00:00 2001 From: juanferrer Date: Tue, 12 May 2026 07:47:40 +0100 Subject: [PATCH 06/20] Continue fixing active effects sheet --- src/module/templates.js | 1 + src/templates/item/parts/AE-config-change.hbs | 18 ++++++++++++++++++ src/templates/item/parts/AE-config-changes.hbs | 4 ++-- 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 src/templates/item/parts/AE-config-change.hbs diff --git a/src/module/templates.js b/src/module/templates.js index 3df51be5..a6c72dbe 100644 --- a/src/module/templates.js +++ b/src/module/templates.js @@ -67,6 +67,7 @@ export const preloadHandlebarsTemplates = async function () { "systems/demonlord/templates/item/parts/AE-config-details.hbs", "systems/demonlord/templates/item/parts/AE-config-duration.hbs", "systems/demonlord/templates/item/parts/AE-config-changes.hbs", + "systems/demonlord/templates/item/parts/AE-config-change.hbs", // Item Sheet Partials 'systems/demonlord/templates/item/partial/item-activation.hbs', diff --git a/src/templates/item/parts/AE-config-change.hbs b/src/templates/item/parts/AE-config-change.hbs new file mode 100644 index 00000000..90ff6280 --- /dev/null +++ b/src/templates/item/parts/AE-config-change.hbs @@ -0,0 +1,18 @@ +
  • +
    + {{formInput fields.key name=change.keyPath value=change.key}} +
    +
    + {{formInput fields.type name=change.typePath value=change.type choices=changeTypes}} +
    +
    + {{formInput fields.value name=change.valuePath value=change.value elementType="input"}} +
    + + {{!--
    + {{formInput fields.priority name=change.priorityPath value=change.priority placeholder=defaultPriority}} +
    --}} +
    + +
    +
  • diff --git a/src/templates/item/parts/AE-config-changes.hbs b/src/templates/item/parts/AE-config-changes.hbs index 39fae54b..475dd111 100644 --- a/src/templates/item/parts/AE-config-changes.hbs +++ b/src/templates/item/parts/AE-config-changes.hbs @@ -3,14 +3,14 @@
    {{localize "EFFECT.FIELDS.changes.element.key.label"}}
    {{localize "EFFECT.FIELDS.changes.element.type.label"}}
    {{localize "EFFECT.FIELDS.changes.element.value.label"}}
    -
    {{localize "EFFECT.FIELDS.changes.element.priority.label"}}
    + {{!--
    {{localize "EFFECT.FIELDS.changes.element.priority.label"}}
    --}}
      {{#each changes as |change|}} - {{{change}}} + {{> "systems/demonlord/templates/item/parts/AE-config-change.hbs" data=change}} {{/each}}
    From 3efcd16de6c17165bfb54222a7be41ca8fce31d1 Mon Sep 17 00:00:00 2001 From: juanferrer Date: Mon, 18 May 2026 15:08:15 +0100 Subject: [PATCH 07/20] Fix macros --- src/module/macros/gm-macros.js | 74 ++++++++++++++---------------- src/module/macros/player-macros.js | 16 +++---- 2 files changed, 42 insertions(+), 48 deletions(-) diff --git a/src/module/macros/gm-macros.js b/src/module/macros/gm-macros.js index ea194ce8..e6e1e723 100644 --- a/src/module/macros/gm-macros.js +++ b/src/module/macros/gm-macros.js @@ -27,7 +27,7 @@ export function requestInitiativeRollMacro() { chatData["whisper"] = targets; let template = 'systems/demonlord/templates/chat/makeinitroll.hbs'; - renderTemplate(template, templateData).then(content => { + foundry.applications.handlebars.renderTemplate(template, templateData).then(content => { chatData.content = content; ChatMessage.create(chatData); }); @@ -90,13 +90,12 @@ export function requestChallengeRollMacro() { chatData["whisper"] = targets; let template = 'systems/demonlord/templates/chat/makechallengeroll.hbs'; - renderTemplate(template, templateData).then(content => { + foundry.applications.handlebars.renderTemplate(template, templateData).then(content => { chatData.content = content; ChatMessage.create(chatData); }); } - let applyChanges = false; new DialogV2({ window: { title: game.i18n.localize('DL.MacroChallengeTitle'), @@ -124,23 +123,22 @@ export function requestChallengeRollMacro() { action: 'yes', icon: 'fas fa-check', label: game.i18n.localize('DL.MacroChallengeRequestRoll'), - callback: () => applyChanges = true, + callback: (event, button, dialog) => dialog.element, default: true, }, { action: 'no', icon: 'fas fa-times', - label: game.i18n.localize('DL.MacroCancel') + label: game.i18n.localize('DL.MacroCancel'), + callback: () => close() }, ], - close: html => { - if (applyChanges) { - for (let token of canvas.tokens.controlled) { - let attribute = html.find('[name="attribute-type"]')[0].value || "none"; - let boonsbanes = html.find('[name="boonsbanes"]')[0].value || "none"; + submit: result => { + for (let token of canvas.tokens.controlled) { + let attribute = result.querySelector('[name="attribute-type"]').value || "none"; + let boonsbanes = result.querySelector('[name="boonsbanes"]').value || "none"; - requestRoll(token, attribute, boonsbanes); - } + requestRoll(token, attribute, boonsbanes); } } }).render(true); @@ -177,13 +175,13 @@ export function wealthManagerMacro() {

    ` + game.i18n.localize('DL.MacroWealthManager.AddSubtract') + `
    - + - + - + - +


    ` + game.i18n.localize('DL.MacroWealthManager.CurrentWealth') + `

    @@ -201,8 +199,8 @@ export function wealthManagerMacro() { { action: 'ok', label: game.i18n.localize('DL.MacroWealthManager.ButtonApply'), - callback: async (html) => { - coinmanager(html); + callback: async (event, button, dialog) => { + coinmanager(dialog.element); }, }, { @@ -214,11 +212,11 @@ export function wealthManagerMacro() { } async function coinmanager(html) { - let playerName = html.find("#playerName")[0].value; - let gc = html.find("#gc")[0].value; - let ss = html.find("#ss")[0].value; - let cp = html.find("#cp")[0].value; - let bits = html.find("#bits")[0].value; + let playerName = html.querySelector("#playerName").value; + let gc = html.querySelector("#gc").value; + let ss = html.querySelector("#ss").value; + let cp = html.querySelector("#cp").value; + let bits = html.querySelector("#bits").value; if (playerName == 'everyone') { await updateAllWealth(gc, ss, cp, bits); } else { @@ -229,7 +227,7 @@ export function wealthManagerMacro() { async function updateWealth(playerName, gc, ss, cp, bits) { let actors = []; let players = game.users.filter((t) => t.role != 4); - players.forEach((p) => { + players?.map((p) => { const actor = p.character if (!actor) return if (actor.name === playerName) @@ -241,19 +239,18 @@ export function wealthManagerMacro() { let currentCP = parseInt(actors[0].system.wealth.cp); let currentBits = parseInt(actors[0].system.wealth.bits); - await actors[0].update( - { - 'system.wealth.gc': currentGC + parseInt(gc), - 'system.wealth.ss': currentSS + parseInt(ss), - 'system.wealth.cp': currentCP + parseInt(cp), - 'system.wealth.bits': currentBits + parseInt(bits) - }); + await actors[0].update({ + 'system.wealth.gc': currentGC + parseInt(gc), + 'system.wealth.ss': currentSS + parseInt(ss), + 'system.wealth.cp': currentCP + parseInt(cp), + 'system.wealth.bits': currentBits + parseInt(bits) + }); expMessage(actors[0].name, gc, ss, cp, bits); } async function updateAllWealth(gc, ss, cp, bits) { let players = game.users.filter((t) => t.role !== 4); - await Promise.all(players.forEach(async (p) => { + await Promise.all(players?.map(async (p) => { const actor = p.character if (!actor) return @@ -262,13 +259,12 @@ export function wealthManagerMacro() { let currentCP = parseInt(actor.system.wealth.cp); let currentBits = parseInt(actor.system.wealth.bits); - await actor.update( - { - 'system.wealth.gc': currentGC + parseInt(gc), - 'system.wealth.ss': currentSS + parseInt(ss), - 'system.wealth.cp': currentCP + parseInt(cp), - 'system.wealth.bits': currentBits + parseInt(bits) - }); + await actor.update({ + 'system.wealth.gc': currentGC + parseInt(gc), + 'system.wealth.ss': currentSS + parseInt(ss), + 'system.wealth.cp': currentCP + parseInt(cp), + 'system.wealth.bits': currentBits + parseInt(bits) + }); expMessage(actor.name, gc, ss, cp, bits); })); diff --git a/src/module/macros/player-macros.js b/src/module/macros/player-macros.js index 23214110..0c10ecba 100644 --- a/src/module/macros/player-macros.js +++ b/src/module/macros/player-macros.js @@ -18,7 +18,6 @@ export function makeChallengeRollMacro() { } } - let applyChanges = false; new DialogV2({ window: { title: game.i18n.localize('DL.MacroMakeChallengeRollTitle'), @@ -46,22 +45,21 @@ export function makeChallengeRollMacro() { action: 'yes', icon: 'fas fa-check', label: game.i18n.localize('DL.MacroMakeChallengeRollRoll'), - callback: () => applyChanges = true, + callback: (event, button, dialog) => dialog.element, default: true }, { action: 'no', icon: 'fas fa-times', - label: game.i18n.localize('DL.MacroCancel') + label: game.i18n.localize('DL.MacroCancel'), + callback: () => close() }, ], - close: async html => { - if (applyChanges) { - let attribute = html.find('[name="attribute-type"]')[0].value || "none"; - let boonsbanes = html.find('[name="boonsbanes"]')[0].value || "none"; + submit: async result => { + let attribute = result.querySelector('[name="attribute-type"]').value || "none"; + let boonsbanes = result.querySelector('[name="boonsbanes"]').value || "none"; - await makeRoll(attribute, boonsbanes); - } + await makeRoll(attribute, boonsbanes); } }).render(true); } From f544906ed07b103b00514652451a9e8a403152b8 Mon Sep 17 00:00:00 2001 From: juanferrer Date: Mon, 18 May 2026 15:10:38 +0100 Subject: [PATCH 08/20] Remove last remaining uses of html.find --- src/module/dialog/player-tracker.js | 8 ++++---- src/module/macros/player-macros.js | 2 +- src/module/settings.js | 24 ++++++++++++------------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/module/dialog/player-tracker.js b/src/module/dialog/player-tracker.js index f01bc328..d33b54f9 100644 --- a/src/module/dialog/player-tracker.js +++ b/src/module/dialog/player-tracker.js @@ -121,9 +121,9 @@ export class PlayerTracker extends HandlebarsApplicationMixin(ApplicationV2) { if (!this.options.editable) return - const html = $(this.element) + const html = this.element - html.find('.gmnote-control').click(async ev => { + html.querySelectorAll('.gmnote-control')?.forEach(el => el.addEventListener(async ev => { const a = ev.currentTarget const actor = game.actors.get(a.dataset.id) @@ -136,7 +136,7 @@ export class PlayerTracker extends HandlebarsApplicationMixin(ApplicationV2) { }) .then(() => this.render()) case 'save': { - const textarea = html.find('textarea[id="person.' + actor.id + '"]') + const textarea = html.querySelectorAll('textarea[id="person.' + actor.id + '"]') return await actor .update({ 'data.gmnote': textarea[0].value, @@ -146,7 +146,7 @@ export class PlayerTracker extends HandlebarsApplicationMixin(ApplicationV2) { } } } - }) + })) } /** diff --git a/src/module/macros/player-macros.js b/src/module/macros/player-macros.js index 0c10ecba..609ac264 100644 --- a/src/module/macros/player-macros.js +++ b/src/module/macros/player-macros.js @@ -11,7 +11,7 @@ export function makeChallengeRollMacro() { ui.notifications.info(game.i18n.localize('DL.DialogWarningActorsNotSelected')); } else { - await Promise.all(selected.forEach(async s => { + await Promise.all(selected.map(async s => { const a = s.actor await a.rollAttributeChallenge(a.getAttribute(attributeName), boonsbanes, 0) })) diff --git a/src/module/settings.js b/src/module/settings.js index 85274c44..57b220d1 100644 --- a/src/module/settings.js +++ b/src/module/settings.js @@ -70,8 +70,8 @@ export class DiceSoNiceSettings extends HandlebarsApplicationMixin(ApplicationV2 // eslint-disable-next-line no-unused-vars _onRender(context, options) { - const html = $(this.element) - html.find('button').on('click', async event => { + const html = this.element + html.querySelectorAll('button')?.forEach(el => el.addEventListener('click', async event => { if (event.currentTarget?.dataset?.action === 'reset') { const keys = ['colourBoBDieDSN', 'boonColour', 'baneColour', 'replaced3'] await Promise.all( @@ -81,7 +81,7 @@ export class DiceSoNiceSettings extends HandlebarsApplicationMixin(ApplicationV2 ) this.close() } - }) + })) } static async handler(event, form, formData) { @@ -196,8 +196,8 @@ export class OptionalRulesSettings extends HandlebarsApplicationMixin(Applicatio // eslint-disable-next-line no-unused-vars _onRender(context, options) { - const html = $(this.element) - html.find('button').on('click', async event => { + const html = this.element + html.querySelectorAll('button')?.forEach(el => el.addEventListener('click', async event => { if (event.currentTarget?.dataset?.action === 'reset') { const keys = [ 'optionalRuleConsistentDamage', @@ -224,7 +224,7 @@ export class OptionalRulesSettings extends HandlebarsApplicationMixin(Applicatio ) this.close() } - }) + })) } static async handler(event, form, formData) { @@ -322,8 +322,8 @@ export class ChatCardSettings extends HandlebarsApplicationMixin(ApplicationV2) // eslint-disable-next-line no-unused-vars _onRender(context, options) { - const html = $(this.element) - html.find('button').on('click', async event => { + const html = this.element + html.querySelectorAll('button')?.forEach(el => el.addEventListener('click', async event => { if (event.currentTarget?.dataset?.action === 'reset') { const keys = ['attackShowAttack','attackShowDefense','rollCreaturesToGM','hideActorInfo','hideDescription','hideDamage'] await Promise.all( @@ -333,7 +333,7 @@ export class ChatCardSettings extends HandlebarsApplicationMixin(ApplicationV2) ) this.close() } - }) + })) } static async handler(event, form, formData) { @@ -420,8 +420,8 @@ export class CombatSettings extends HandlebarsApplicationMixin(ApplicationV2) { // eslint-disable-next-line no-unused-vars _onRender(context, options) { - const html = $(this.element) - html.find('button').on('click', async event => { + const html = this.element + html.querySelectorAll('button')?.forEach(el => el.addEventListener('click', async event => { if (event.currentTarget?.dataset?.action === 'reset') { const keys = ['initMessage','initRandomize','autoSetDefeated','showEncounterDifficulty','targetingOnSelect','templateAutoTargeting','templateAutoRemove','autoDeleteEffects','concentrationEffect', 'finesseAutoSelect', 'launchDialogReminder'] await Promise.all( @@ -431,7 +431,7 @@ export class CombatSettings extends HandlebarsApplicationMixin(ApplicationV2) { ) this.close() } - }) + })) } static async handler(event, form, formData) { From fa12b50e14da02d94bb299dd292f18a346f3d014 Mon Sep 17 00:00:00 2001 From: juanferrer Date: Mon, 18 May 2026 15:34:22 +0100 Subject: [PATCH 09/20] Remove remaining jQuery .on uses --- src/module/chat/chat-listeners.js | 20 ++++++++++---------- src/module/pixi/action-template.js | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/module/chat/chat-listeners.js b/src/module/chat/chat-listeners.js index 2a904458..de3d82ee 100644 --- a/src/module/chat/chat-listeners.js +++ b/src/module/chat/chat-listeners.js @@ -11,16 +11,16 @@ import { changesMatch } from '../utils/chat' const tokenManager = new TokenManager() export function initChatListeners(html) { - $(html).on('click', '.roll-healing', _onChatApplyHealing.bind(this)) - $(html).on('click', '.roll-damage', _onChatRollDamage.bind(this)) - $(html).on('click', '.apply-damage', _onChatApplyDamage.bind(this)) - $(html).on('click', '.apply-effect', _onChatApplyEffect.bind(this)) - $(html).on('click', '.use-talent', _onChatUseTalent.bind(this)) - $(html).on('click', '.place-template', _onChatPlaceTemplate.bind(this)) - $(html).on('click', '.request-challengeroll', _onChatRequestChallengeRoll.bind(this)) - $(html).on('click', '.make-challengeroll', _onChatMakeChallengeRoll.bind(this)) - $(html).on('click', '.request-initroll', _onChatRequestInitRoll.bind(this)) - $(html).on('click', '.make-initroll', _onChatMakeInitRoll.bind(this)) + html.querySelectorAll('.roll-healing').forEach(el => el.addEventListener('click', _onChatApplyHealing.bind(this))) + html.querySelectorAll('.roll-damage').forEach(el => el.addEventListener('click', _onChatRollDamage.bind(this))) + html.querySelectorAll('.apply-damage').forEach(el => el.addEventListener('click', _onChatApplyDamage.bind(this))) + html.querySelectorAll('.apply-effect').forEach(el => el.addEventListener('click', _onChatApplyEffect.bind(this))) + html.querySelectorAll('.use-talent').forEach(el => el.addEventListener('click', _onChatUseTalent.bind(this))) + html.querySelectorAll('.place-template').forEach(el => el.addEventListener('click', _onChatPlaceTemplate.bind(this))) + html.querySelectorAll('.request-challengeroll').forEach(el => el.addEventListener('click', _onChatRequestChallengeRoll.bind(this))) + html.querySelectorAll('.make-challengeroll').forEach(el => el.addEventListener('click', _onChatMakeChallengeRoll.bind(this))) + html.querySelectorAll('.request-initroll').forEach(el => el.addEventListener('click', _onChatRequestInitRoll.bind(this))) + html.querySelectorAll('.make-initroll').forEach(el => el.addEventListener('click', _onChatMakeInitRoll.bind(this))) } /* -------------------------------------------- */ diff --git a/src/module/pixi/action-template.js b/src/module/pixi/action-template.js index 80d547b1..28896e85 100644 --- a/src/module/pixi/action-template.js +++ b/src/module/pixi/action-template.js @@ -143,8 +143,8 @@ export class ActionTemplate extends foundry.canvas.placeables.MeasuredTemplate { } // Activate listeners - canvas.stage.on('mousemove', handlers.mm) - canvas.stage.on('mousedown', handlers.lc) + canvas.stage.addEventListener('mousemove', handlers.mm) + canvas.stage.addEventListener('mousedown', handlers.lc) canvas.app.view.oncontextmenu = handlers.rc canvas.app.view.onwheel = handlers.mw } From b124fc8d72db502eb1c8a82500031a42f359bb71 Mon Sep 17 00:00:00 2001 From: juanferrer Date: Mon, 18 May 2026 16:46:45 +0100 Subject: [PATCH 10/20] Remove most remaining uses of jQuery --- src/module/actor/sheets/base-actor-sheet.js | 46 ++++++++++---------- src/module/actor/sheets/character-sheet.js | 12 +++--- src/module/actor/sheets/creature-sheet.js | 12 +++--- src/module/actor/sheets/vehicle-sheet.js | 16 +++---- src/module/chat/chat-listeners.js | 2 +- src/module/demonlord.js | 6 ++- src/module/item/sheets/base-item-sheet.js | 47 ++++++++++----------- src/module/utils/rolltable.js | 15 +++---- 8 files changed, 79 insertions(+), 77 deletions(-) diff --git a/src/module/actor/sheets/base-actor-sheet.js b/src/module/actor/sheets/base-actor-sheet.js index f1e20aa7..554117bd 100644 --- a/src/module/actor/sheets/base-actor-sheet.js +++ b/src/module/actor/sheets/base-actor-sheet.js @@ -285,20 +285,20 @@ export default class DLBaseActorSheet extends HandlebarsApplicationMixin(ActorSh } static async onToggleInfo(event) { - const elem = $(event.target) + const elem = event.target const root = elem.closest('[data-item-id]') const selector = '.fa-chevron-down, .fa-chevron-up' - const chevron = elem.is(selector) ? elem : elem.find(selector); - const elements = $(root).find('.dlInfo') - elements.each((_, i) => { + const chevron = elem.matches(selector) ? elem : elem.querySelector(selector); + const elements = root.querySelectorAll('.dlInfo') + elements.forEach((_, i) => { if (i.style.display === 'none') { $(i).slideDown(100) - chevron?.removeClass('fa-chevron-up') - chevron?.addClass('fa-chevron-down') + chevron?.classList.remove('fa-chevron-up') + chevron?.classList.add('fa-chevron-down') } else { $(i).slideUp(100) - chevron?.removeClass('fa-chevron-down') - chevron?.addClass('fa-chevron-up') + chevron?.classList.remove('fa-chevron-down') + chevron?.classList.add('fa-chevron-up') } }) } @@ -442,7 +442,7 @@ export default class DLBaseActorSheet extends HandlebarsApplicationMixin(ActorSh tippy(e.querySelectorAll('[data-tippy-content]')) tippy(e.querySelectorAll('[data-tippy-html]'), { content(reference) { - return $(reference).data('tippyHtml') + return reference.dataset.tippyHtml }, allowHTML: true }) @@ -461,7 +461,7 @@ export default class DLBaseActorSheet extends HandlebarsApplicationMixin(ActorSh }) tippy(e.querySelectorAll('[data-tab="afflictions"] [data-tippy-affliction]'), { content(reference) { - return $(reference).data('tippyAffliction') + return reference.dataset.tippyAffliction }, trigger: 'mouseenter', arrow: true, @@ -629,14 +629,14 @@ export default class DLBaseActorSheet extends HandlebarsApplicationMixin(ActorSh // Clone inventory item e.querySelectorAll('.item-clone')?.forEach(el => el.addEventListener('click', async ev => { - const li = $(ev.currentTarget).parents('.item') - const item = foundry.utils.duplicate(this.actor.items.get(li.data('itemId'))) + const li = ev.currentTarget.closest('.item') + const item = foundry.utils.duplicate(this.actor.items.get(li.dataset.itemId)) await Item.create(item, { parent: this.actor }) })) // Wear item style e.querySelectorAll('.item-wear')?.forEach(el => { - const itemId = $(el).closest('[data-item-id]').data('itemId') + const itemId = el.closest('[data-item-id]').dataset.itemId const item = this.actor.items.get(itemId) if ( item.system.wear && @@ -644,7 +644,7 @@ export default class DLBaseActorSheet extends HandlebarsApplicationMixin(ActorSh item.system.requirement?.attribute != '' && +item.system.requirement?.minvalue > (+this.actor.getAttribute(item.system.requirement?.attribute)?.value + +this.actor.getAttribute(item.system.requirement?.attribute)?.requirementModifier) ) { - $(el).addClass('dl-text-red') + el.classList.add('dl-text-red') } }) @@ -661,8 +661,8 @@ export default class DLBaseActorSheet extends HandlebarsApplicationMixin(ActorSh // Rollable Attributes e.querySelectorAll('.attribute .name')?.forEach(el => el.addEventListener('click', ev => { - const div = $(ev.currentTarget) - const attributeName = div.data('key') + const div = ev.currentTarge + const attributeName = div.dataset.key const attribute = this.actor.getAttribute(attributeName) if (!attribute.immune) { // Make an attribute attack if a target is selected, otherwise, challenge roll @@ -676,27 +676,27 @@ export default class DLBaseActorSheet extends HandlebarsApplicationMixin(ActorSh // Set immune on rollable attribute e.querySelectorAll('.attribute .name')?.forEach(el => el.addEventListener('contextmenu', async ev => { - const div = $(ev.currentTarget) - const attributeName = div.data('key') + const div = ev.currentTarget + const attributeName = div.dataset.key await this.actor.update({ system: { attributes: { [attributeName]: { immune: !this.actor.system.attributes[attributeName].immune } } } }) })) // Rollable Attack e.querySelectorAll('.attack-roll')?.forEach(el => el.addEventListener('click', async ev => { - const id = $(ev.currentTarget).closest('[data-item-id]').data('itemId') + const id = ev.currentTarget.closest('[data-item-id]').dataset.itemId await this.actor.rollWeaponAttack(id, { event: ev }) })) // Rollable Talent e.querySelectorAll('.talent-roll').forEach(el => el.addEventListener('mousedown', async ev => { - const id = $(ev.currentTarget).closest('[data-item-id]').data('itemId') + const id = ev.currentTarget.closest('[data-item-id]').dataset.itemId if (ev.button == 0) await this.actor.rollTalent(id, { event: ev }) else if (ev.button == 2) await this.actor.deactivateTalent(this.actor.items.get(id), 0) })) // Talent uses e.querySelectorAll('.talent-uses').forEach(el => el.addEventListener('mousedown', async ev => { - const id = $(ev.currentTarget).closest('[data-item-id]').data('itemId') + const id = ev.currentTarget.closest('[data-item-id]').dataset.itemId const talent = this.actor.items.get(id) if (ev.button == 0) await this.actor.activateTalent(talent, true) else if (ev.button == 2) await this.actor.deactivateTalent(talent, 1) @@ -788,11 +788,11 @@ export default class DLBaseActorSheet extends HandlebarsApplicationMixin(ActorSh /* _onDragOver(event) { - $(event.target).addClass('drop-hover') + event.target.classList.add('drop-hover') } _onDragLeave(event) { - $(event.target).removeClass('drop-hover') + event.target.classList.remove('drop-hover') }*/ /** @override */ diff --git a/src/module/actor/sheets/character-sheet.js b/src/module/actor/sheets/character-sheet.js index f9e53704..d2c07346 100644 --- a/src/module/actor/sheets/character-sheet.js +++ b/src/module/actor/sheets/character-sheet.js @@ -305,16 +305,16 @@ export default class DLCharacterSheet extends DLBaseActorSheet { } async onEditRole(ev) { - const div = $(ev.currentTarget) - const role = this.actor.getEmbeddedDocument('Item', div.data('itemId')) + const div = ev.currentTarget + const role = this.actor.getEmbeddedDocument('Item', div.data.itemId) if (ev.button == 0) role.sheet.render(true) else if (ev.button == 2) await role.delete({ parent: this.actor }) } async onEditRelic(ev) { - const div = $(ev.currentTarget) - const role = this.actor.getEmbeddedDocument('Item', div.data('itemId')) + const div = ev.currentTarge + const role = this.actor.getEmbeddedDocument('Item', div.dataset.itemId) if (ev.button == 0) role.sheet.render(true) else if (ev.button == 2) await role.delete({ parent: this.actor }) @@ -418,7 +418,7 @@ export default class DLCharacterSheet extends DLBaseActorSheet { // Ammo uses e.querySelectorAll('.ammo-amount')?.forEach(el => el.addEventListener('mousedown', async ev => { - const id = $(ev.currentTarget).closest('[data-item-id]').data('itemId') + const id = ev.currentTarget.closest('[data-item-id]').dataset.itemId const item = foundry.utils.duplicate(this.actor.items.get(id)) const amount = item.system.quantity if (ev.button == 0 && amount >= 0) item.system.quantity = +amount + 1 @@ -428,7 +428,7 @@ export default class DLCharacterSheet extends DLBaseActorSheet { // Item uses e.querySelectorAll('.item-uses')?.forEach(el => el.addEventListener('mousedown', async ev => { - const id = $(ev.currentTarget).closest('[data-item-id]').data('itemId') + const id = ev.currentTarget.closest('[data-item-id]').dataset.itemId const item = foundry.utils.duplicate(this.actor.items.get(id)) if (ev.button == 0) { item.system.quantity++ diff --git a/src/module/actor/sheets/creature-sheet.js b/src/module/actor/sheets/creature-sheet.js index d1505cd1..a6e29db0 100644 --- a/src/module/actor/sheets/creature-sheet.js +++ b/src/module/actor/sheets/creature-sheet.js @@ -135,8 +135,8 @@ export default class DLCreatureSheet extends DLBaseActorSheet { } async onEditRelic(ev) { - const div = $(ev.currentTarget) - const relic = this.actor.getEmbeddedDocument('Item', div.data('itemId')) + const div = ev.currentTarget + const relic = this.actor.getEmbeddedDocument('Item', div.dataset.itemId) if (ev.button == 0) relic.sheet.render(true) else if (ev.button == 2) await relic.delete({ parent: this.actor }) @@ -173,7 +173,7 @@ export default class DLCreatureSheet extends DLBaseActorSheet { // Ammo uses e.querySelectorAll('.ammo-amount')?.forEach(el => el.addEventListener('mousedown', async ev => { - const id = $(ev.currentTarget).closest('[data-item-id]').data('itemId') + const id = ev.currentTarget.closest('[data-item-id]').dataset.itemId const item = foundry.utils.duplicate(this.actor.items.get(id)) const amount = item.system.quantity if (ev.button == 0 && amount >= 0) item.system.quantity = +amount + 1 @@ -183,7 +183,7 @@ export default class DLCreatureSheet extends DLBaseActorSheet { // Item uses e.querySelectorAll('.item-uses')?.forEach(el => el.addEventListener('mousedown', async ev => { - const id = $(ev.currentTarget).closest('[data-item-id]').data('itemId') + const id = ev.currentTarget.closest('[data-item-id]').dataset.itemId const item = foundry.utils.duplicate(this.actor.items.get(id)) if (ev.button == 0) { item.system.quantity++ @@ -196,8 +196,8 @@ export default class DLCreatureSheet extends DLBaseActorSheet { })) e.querySelectorAll('.characteristic .name')?.forEach(el => el.addEventListener('contextmenu', async ev => { - const div = $(ev.currentTarget) - const characteristicName = div.data('key') + const div = ev.currentTarget + const characteristicName = div.dataset.key await this.actor.update({ system: { characteristics: { [characteristicName]: { immune: !this.actor.system.characteristics[characteristicName].immune } } } }) })) } diff --git a/src/module/actor/sheets/vehicle-sheet.js b/src/module/actor/sheets/vehicle-sheet.js index 5436862e..c24a6fda 100644 --- a/src/module/actor/sheets/vehicle-sheet.js +++ b/src/module/actor/sheets/vehicle-sheet.js @@ -124,16 +124,16 @@ export default class DLVehicleSheet extends DLBaseActorSheet { } async onEditRole(ev) { - const div = $(ev.currentTarget) - const role = this.actor.getEmbeddedDocument('Item', div.data('itemId')) + const div = ev.currentTarget + const role = this.actor.getEmbeddedDocument('Item', div.dataset.itemId) if (ev.button == 0) role.sheet.render(true) else if (ev.button == 2) await role.delete({ parent: this.actor }) } async onEditRelic(ev) { - const div = $(ev.currentTarget) - const relic = this.actor.getEmbeddedDocument('Item', div.data('itemId')) + const div = ev.currentTarget + const relic = this.actor.getEmbeddedDocument('Item', div.dataset.itemId) if (ev.button == 0) relic.sheet.render(true) else if (ev.button == 2) await relic.delete({ parent: this.actor }) @@ -161,7 +161,7 @@ export default class DLVehicleSheet extends DLBaseActorSheet { // Ammo uses e.querySelectorAll('.ammo-amount')?.forEach(el => el.addEventListener('mousedown', async ev => { - const id = $(ev.currentTarget).closest('[data-item-id]').data('itemId') + const id = ev.currentTarget.closest('[data-item-id]').dataset.itemId const item = foundry.utils.duplicate(this.actor.items.get(id)) const amount = item.system.quantity if (ev.button == 0 && amount >= 0) item.system.quantity = +amount + 1 @@ -171,7 +171,7 @@ export default class DLVehicleSheet extends DLBaseActorSheet { // Item uses e.querySelectorAll('.item-uses')?.forEach(el => el.addEventListener('mousedown', async ev => { - const id = $(ev.currentTarget).closest('[data-item-id]').data('itemId') + const id = ev.currentTarget.closest('[data-item-id]').dataset.itemId const item = foundry.utils.duplicate(this.actor.items.get(id)) if (ev.button == 0) { item.system.quantity++ @@ -184,8 +184,8 @@ export default class DLVehicleSheet extends DLBaseActorSheet { })) e.querySelectorAll('.characteristic .name')?.forEach(el => el.addEventListener('contextmenu', async ev => { - const div = $(ev.currentTarget) - const characteristicName = div.data('key') + const div = ev.currentTarget + const characteristicName = div.dataset.key await this.actor.update({ system: { characteristics: { [characteristicName]: { immune: !this.actor.system.characteristics[characteristicName].immune } } } }) })) } diff --git a/src/module/chat/chat-listeners.js b/src/module/chat/chat-listeners.js index de3d82ee..685a7d40 100644 --- a/src/module/chat/chat-listeners.js +++ b/src/module/chat/chat-listeners.js @@ -484,7 +484,7 @@ function _getChatCardTargets(_card) { async function _onChatPlaceTemplate(event) { event.preventDefault() - const itemUuid = $(event.currentTarget).data('itemUuid') + const itemUuid = event.currentTarget.dataset.itemUuid const item = await fromUuid(itemUuid) const template = game.demonlord.canvas.ActionTemplate.fromItem(item) diff --git a/src/module/demonlord.js b/src/module/demonlord.js index 6998a1a2..0010c88c 100644 --- a/src/module/demonlord.js +++ b/src/module/demonlord.js @@ -532,7 +532,11 @@ Hooks.on('renderChatMessageHTML', async (app, html, _msg) => { html.querySelectorAll('.owneronly')?.forEach(el => el.remove()) if (game.settings.get('demonlord', 'hideActorInfo')) html.querySelectorAll('.showlessinfo')?.forEach(el => el.remove()) - if (game.settings.get('demonlord', 'hideDescription')) html.querySelectorAll('.showdescription')?.forEach(el => $(el).empty()) + if (game.settings.get('demonlord', 'hideDescription')) html.querySelectorAll('.showdescription')?.forEach(el => { + while (el.firstChild) { + el.removeChild(el.lastChild) + } + }) } initChatListeners(html) }) diff --git a/src/module/item/sheets/base-item-sheet.js b/src/module/item/sheets/base-item-sheet.js index bfe113f2..db94c930 100644 --- a/src/module/item/sheets/base-item-sheet.js +++ b/src/module/item/sheets/base-item-sheet.js @@ -446,20 +446,20 @@ export default class DLBaseItemSheet extends HandlebarsApplicationMixin(ItemShee } static async onToggleInfo(event) { - const elem = $(event.target) + const elem = event.target const root = elem.closest('[data-item-id]') const selector = '.fa-chevron-down, .fa-chevron-up' - const chevron = elem.is(selector) ? elem : elem.find(selector); - const elements = $(root).find('.dlInfo') - elements.each((_, el) => { + const chevron = elem.matches(selector) ? elem : elem.querySelector(selector); + const elements = root.querySelectorAll('.dlInfo') + elements.forEach((_, el) => { if (el.style.display === 'none') { $(el).slideDown(100) - chevron?.removeClass('fa-chevron-up') - chevron?.addClass('fa-chevron-down') + chevron?.classList.remove('fa-chevron-up') + chevron?.classList.add('fa-chevron-down') } else { $(el).slideUp(100) - chevron?.removeClass('fa-chevron-down') - chevron?.addClass('fa-chevron-up') + chevron?.classList.remove('fa-chevron-down') + chevron?.classList.add('fa-chevron-up') } }) } @@ -601,13 +601,12 @@ export default class DLBaseItemSheet extends HandlebarsApplicationMixin(ItemShee static async onSelectLevel(event, target) { // Display/hide levels on click - const levelIndex = $(target).closest('[data-level-index]').data('levelIndex') - const form = $(target).closest("form") + const levelIndex = target.closest('[data-level-index]').dataset.levelIndex + const form = target.closest("form") this._selectedLevelIndex = levelIndex form.find('.level-selector').each((_, pl) => { - pl = $(pl) - if (pl.data('levelIndex') === levelIndex) pl.show() - else pl.hide() + if (pl.dataset.levelIndex === levelIndex) pl.style.display = 'block' + else pl.style.display = 'none' }) } @@ -667,7 +666,7 @@ export default class DLBaseItemSheet extends HandlebarsApplicationMixin(ItemShee tippy(e.querySelectorAll('[data-tippy-content]')) tippy(e.querySelectorAll('[data-tippy-html]'), { content(reference) { - return $(reference).data('tippyHtml') + return reference.dataset.tippyHtml }, allowHTML: true }) @@ -837,10 +836,10 @@ export default class DLBaseItemSheet extends HandlebarsApplicationMixin(ItemShee _getPathDataFromForm() { // Get all html elements that are 'path-level' and group their inputs by path-level const htmlLevels = [] - $(this.element) - .find('.level-selector') - .each((i, pl) => { - htmlLevels.push($(pl).find("*[name^='level']")) + this.element + .querySelectorAll('.level-selector') + .forEach((i, pl) => { + htmlLevels.push(pl.querySelectorAll("*[name^='level']")) }) // From the htmlLevels, construct the levels array based on the input names and values @@ -1059,20 +1058,20 @@ export default class DLBaseItemSheet extends HandlebarsApplicationMixin(ItemShee } _onDragOver(event) { - $(event.target).addClass('drop-hover') + event.target.classList.add('drop-hover') } _onDragLeave(event) { - $(event.target).removeClass('drop-hover') + event.target.classList.remove('drop-hover') } async _onDrop(event) { - $(event.target).removeClass('drop-hover') + event.target.classList.remove('drop-hover') const group = event.target.closest('[data-group]')?.dataset?.group const levelIndex = event.target.closest('[data-level-index]')?.dataset?.levelIndex try { - $(event.target).removeClass('drop-hover') + event.target.classList.remove('drop-hover') const data = JSON.parse(event.dataTransfer.getData('text/plain')) if (data.type !== 'Item') return await this._addItem(data, levelIndex, group) @@ -1161,7 +1160,7 @@ export default class DLBaseItemSheet extends HandlebarsApplicationMixin(ItemShee // if (!data.system.contents) { // return // } - // const itemId = $(ev.currentTarget).closest('[data-item-id]').data('itemId') + // const itemId = ev.currentTarget.closest('[data-item-id]').dataset.itemId // const nestedData = data.system.contents.find(i => i._id === itemId) // await getNestedDocument(nestedData).then(d => { // if (d.sheet) d.sheet.render(true) @@ -1180,7 +1179,7 @@ export default class DLBaseItemSheet extends HandlebarsApplicationMixin(ItemShee } // async _onContentsItemDelete(ev) { - // const itemIndex = $(ev.currentTarget).closest('[data-item-index]').data('itemIndex') + // const itemIndex = ev.currentTarget.closest('[data-item-index]').dataset.itemIndex // await this.deleteContentsItem(itemIndex) // } diff --git a/src/module/utils/rolltable.js b/src/module/utils/rolltable.js index 586ff25b..a4b7f68b 100644 --- a/src/module/utils/rolltable.js +++ b/src/module/utils/rolltable.js @@ -5,8 +5,8 @@ export function _renderRollTableDirectory(html) { if (!game.settings.get('demonlord', 'enableQuickDraw')) return - const tables = $(html).find('.directory-item.document') - tables.each(k => { + const tables = html.querySelectorAll('.directory-item.document') + tables.forEach(k => { let rollIcon = document.createElement('dl') enrichRollTableSidebar(rollIcon, tables, k) rollIcon.addEventListener('click', rollTableFromSidebar) @@ -16,8 +16,8 @@ export function _renderRollTableDirectory(html) { export function _renderCompendium(html, data) { if (!game.settings.get('demonlord', 'enableQuickDraw')) return if (data.collection.metadata.type !== 'RollTable') return - const tables = $(html).find('.directory-item.document') - tables.each(k => { + const tables = html.querySelectorAll('.directory-item.document') + tables.forEach(k => { let rollIcon = document.createElement('dl') enrichRollTableSidebar(rollIcon, tables, k) rollIcon.addEventListener('click', event => @@ -28,11 +28,10 @@ export function _renderCompendium(html, data) { export function _renderDLSheet(jn, element) { if (!game.settings.get('demonlord', 'enableQuickDraw')) return - $(element) - .find('.content-link') - .contextmenu(elem => { + element.querySelectorAll('.content-link').forEach(el => + el.addEventListener('contextmenu', (elem => { linkContextDraw(elem.currentTarget) - }) + }))) } function linkContextDraw(target) { From a5544a6aec8206b2693ed449f05f66ab1f4f9723 Mon Sep 17 00:00:00 2001 From: juanferrer Date: Fri, 22 May 2026 13:05:25 +0100 Subject: [PATCH 11/20] Correct issues with info dropdowns --- src/module/actor/sheets/base-actor-sheet.js | 2 +- src/module/item/sheets/base-item-sheet.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/module/actor/sheets/base-actor-sheet.js b/src/module/actor/sheets/base-actor-sheet.js index 554117bd..c5c0acf8 100644 --- a/src/module/actor/sheets/base-actor-sheet.js +++ b/src/module/actor/sheets/base-actor-sheet.js @@ -290,7 +290,7 @@ export default class DLBaseActorSheet extends HandlebarsApplicationMixin(ActorSh const selector = '.fa-chevron-down, .fa-chevron-up' const chevron = elem.matches(selector) ? elem : elem.querySelector(selector); const elements = root.querySelectorAll('.dlInfo') - elements.forEach((_, i) => { + elements.forEach(i => { if (i.style.display === 'none') { $(i).slideDown(100) chevron?.classList.remove('fa-chevron-up') diff --git a/src/module/item/sheets/base-item-sheet.js b/src/module/item/sheets/base-item-sheet.js index db94c930..54910ee2 100644 --- a/src/module/item/sheets/base-item-sheet.js +++ b/src/module/item/sheets/base-item-sheet.js @@ -451,7 +451,7 @@ export default class DLBaseItemSheet extends HandlebarsApplicationMixin(ItemShee const selector = '.fa-chevron-down, .fa-chevron-up' const chevron = elem.matches(selector) ? elem : elem.querySelector(selector); const elements = root.querySelectorAll('.dlInfo') - elements.forEach((_, el) => { + elements.forEach(el => { if (el.style.display === 'none') { $(el).slideDown(100) chevron?.classList.remove('fa-chevron-up') From 09845af61c2fc145f9d7b8d6f3e815a5b6c0bd38 Mon Sep 17 00:00:00 2001 From: juanferrer Date: Fri, 22 May 2026 13:07:38 +0100 Subject: [PATCH 12/20] WIP: Migrate from measured templates to scene regions --- src/module/chat/chat-listeners.js | 2 +- src/module/config.js | 6 +-- src/module/demonlord.js | 8 ++-- src/module/pixi/action-template.js | 74 ++++++++++++++++++++++-------- 4 files changed, 63 insertions(+), 27 deletions(-) diff --git a/src/module/chat/chat-listeners.js b/src/module/chat/chat-listeners.js index 685a7d40..a338726b 100644 --- a/src/module/chat/chat-listeners.js +++ b/src/module/chat/chat-listeners.js @@ -489,7 +489,7 @@ async function _onChatPlaceTemplate(event) { const template = game.demonlord.canvas.ActionTemplate.fromItem(item) if (template) { - template.drawPreview() + //template.drawPreview() } } diff --git a/src/module/config.js b/src/module/config.js index 2d8d383c..4a755a4a 100644 --- a/src/module/config.js +++ b/src/module/config.js @@ -54,12 +54,12 @@ DL.actionRange = { DL.actionAreaShape = { circle: 'circle', cone: 'cone', - cube: 'rect', + cube: 'rectangle', cylinder: 'circle', hemisphere: 'circle', line: 'ray', sphere: 'circle', - square: 'rect', + square: 'rectangle', } DL.actionDuration = { @@ -126,4 +126,4 @@ DL.defaultItemIcons = { relic: 'icons/commodities/treasure/sceptre-jeweled-gold.webp' } -DL.difficultyScale = [1,5,10,25,50,100,250,500,750,1000,1500] \ No newline at end of file +DL.difficultyScale = [1,5,10,25,50,100,250,500,750,1000,1500] diff --git a/src/module/demonlord.js b/src/module/demonlord.js index 0010c88c..cdd77e7f 100644 --- a/src/module/demonlord.js +++ b/src/module/demonlord.js @@ -72,8 +72,6 @@ Hooks.once('init', async function () { healingPotionMacro: macros.healingPotionMacro, } - CONFIG.MeasuredTemplate.defaults.angle = 53.13 - // Define custom Entity classes CONFIG.DL = DL CONFIG.Actor.documentClass = DemonlordActor @@ -683,6 +681,8 @@ Hooks.on('DL.ApplyHealing', data => { Hooks.on('DL.Action', async () => { if (!game.settings.get('demonlord', 'templateAutoRemove')) return - const actionTemplates = canvas.scene.templates.filter(a => a.flags.demonlord.actionTemplate).map(a => a.id) - if (actionTemplates.length > 0) await canvas.scene.deleteEmbeddedDocuments('MeasuredTemplate', actionTemplates) + const templateType = foundry.canvas.placeables.SceneRegion ? 'SceneRegion' : 'MeasuredTemplate' + const sceneTemplates = canvas.scene.regions ?? canvas.scene.templates ?? [] + const actionTemplates = sceneTemplates.filter(a => a.flags.demonlord?.actionTemplate).map(a => a.id) + if (actionTemplates.length > 0) await canvas.scene.deleteEmbeddedDocuments(templateType, actionTemplates) }) diff --git a/src/module/pixi/action-template.js b/src/module/pixi/action-template.js index 28896e85..4a33f84d 100644 --- a/src/module/pixi/action-template.js +++ b/src/module/pixi/action-template.js @@ -4,25 +4,32 @@ import { TokenManager } from './token-manager' const tokenManager = new TokenManager() /** - * A helper class for building MeasuredTemplates for 5e spells and abilities - * @extends {MeasuredTemplate} + * A helper class for building scene regions for 5e spells and abilities + * @extends {SceneRegion|MeasuredTemplate} */ -export class ActionTemplate extends foundry.canvas.placeables.MeasuredTemplate { - static fromItem(item) { +export class ActionTemplate extends foundry.canvas.placeables.Region { + static defaults = { + angle: 53.13, + width: 1 + } + + static async fromItem(item) { const target = foundry.utils.getProperty(item, 'system.activatedEffect.target') || {} const templateShape = DL.actionAreaShape[target.type] if (!templateShape) return null // Prepare template data const templateData = { - t: templateShape, + type: templateShape, user: game.user._id, - distance: target.value, - direction: 0, + rotation: 0, x: 0, y: 0, fillColor: game.user.color, texture: item.system.activatedEffect.texture, + gridBased: false, + hole: false, + flags: { demonlord: { actionTemplate: true, @@ -30,30 +37,59 @@ export class ActionTemplate extends foundry.canvas.placeables.MeasuredTemplate { }, } + let value = Number.parseInt(target.value) * canvas.dimensions.size // The radius is in pixels, but the value is in grid units + // Additional type-specific data switch (templateShape) { case 'cone': - templateData.angle = CONFIG.MeasuredTemplate.defaults.angle + templateData.angle = this.defaults.angle + templateData.radius = value + templateData.curvature = 'round' break - case 'rect': // 5e rectangular AoEs are always cubes - templateData.distance = Math.hypot(target.value, target.value) - templateData.width = target.value - templateData.direction = 45 + case 'rectangle': // All rectangles are square + templateData.anchorX = value * 0.5 + templateData.anchorY = value * 0.5 + templateData.width = value + templateData.height = value break case 'ray': // 5e rays are most commonly 1 square (5 ft) in width + templateData.height = value templateData.width = target.width ?? canvas.dimensions.distance break + case 'circle': + template.radius = value + break default: break } // Return the template constructed from the item data - const cls = CONFIG.MeasuredTemplate.documentClass - const template = new cls(templateData, { parent: canvas.scene }) - const object = new this(template) - object.item = item - object.actorSheet = item.actor?.sheet || null - return object + const template = await canvas.regions.placeRegion({ + name: `${item.name}`, + shapes: [templateData] + }, { create: false }) + + //const cls = CONFIG.Region.documentClass + //const template = new cls(templateData, { parent: canvas.scene }) + + if (template) { + // Only do the following if the template was actually created + const object = new this(template) + object.item = item + object.actorSheet = item.actor?.sheet || null + + // First clear the tokens + canvas.tokens.setTargets([]) + + // Automatically target tokens + for (let token of canvas.tokens.placeables) { + if (template.testPoint(token.document.getCenterPoint())) { + token.setTarget(true, { releaseOthers: false }) + } + } + + return object + } } /* -------------------------------------------- */ @@ -127,7 +163,7 @@ export class ActionTemplate extends foundry.canvas.placeables.MeasuredTemplate { this.autoTargeting() // Create the template - await canvas.scene.createEmbeddedDocuments('MeasuredTemplate', [data]) + await canvas.scene.createEmbeddedDocuments('Region', [data]) } // Rotate the template by 3 degree increments (mouse-wheel) From f178cc5f5ba69b10f25d44b445e10509e7c7891f Mon Sep 17 00:00:00 2001 From: juanferrer Date: Fri, 5 Jun 2026 07:46:00 +0100 Subject: [PATCH 13/20] Complete migration from measured templates to scene regions --- src/module/config.js | 2 +- src/module/pixi/action-template.js | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/module/config.js b/src/module/config.js index 4a755a4a..ac3b25bc 100644 --- a/src/module/config.js +++ b/src/module/config.js @@ -57,7 +57,7 @@ DL.actionAreaShape = { cube: 'rectangle', cylinder: 'circle', hemisphere: 'circle', - line: 'ray', + line: 'line', sphere: 'circle', square: 'rectangle', } diff --git a/src/module/pixi/action-template.js b/src/module/pixi/action-template.js index 4a33f84d..9cd7a727 100644 --- a/src/module/pixi/action-template.js +++ b/src/module/pixi/action-template.js @@ -47,17 +47,18 @@ export class ActionTemplate extends foundry.canvas.placeables.Region { templateData.curvature = 'round' break case 'rectangle': // All rectangles are square - templateData.anchorX = value * 0.5 - templateData.anchorY = value * 0.5 + templateData.anchorX = 0.5 + templateData.anchorY = 0.5 templateData.width = value templateData.height = value break - case 'ray': // 5e rays are most commonly 1 square (5 ft) in width - templateData.height = value - templateData.width = target.width ?? canvas.dimensions.distance + case 'line': + //templateData.height = value + templateData.length = value + templateData.width = canvas.dimensions.size // Lines are most commonly 1 square in width break case 'circle': - template.radius = value + templateData.radius = value break default: break @@ -84,6 +85,7 @@ export class ActionTemplate extends foundry.canvas.placeables.Region { // Automatically target tokens for (let token of canvas.tokens.placeables) { if (template.testPoint(token.document.getCenterPoint())) { + // TODO: Should it ignore self? token.setTarget(true, { releaseOthers: false }) } } From cbb62495f8aba16905b6530326cf18a97522e642 Mon Sep 17 00:00:00 2001 From: juanferrer Date: Tue, 9 Jun 2026 22:14:53 +0100 Subject: [PATCH 14/20] Fix active effects for v14 --- .../sheets/active-effect-config.js | 21 ++++++------------- src/module/data/item/CreatureRoleDataModel.js | 2 +- src/module/macros/gm-macros.js | 3 ++- src/templates/item/parts/AE-config-change.hbs | 14 ++++++------- .../item/parts/AE-config-changes.hbs | 7 ++++--- 5 files changed, 20 insertions(+), 27 deletions(-) diff --git a/src/module/active-effects/sheets/active-effect-config.js b/src/module/active-effects/sheets/active-effect-config.js index 3a6d4777..73127e77 100644 --- a/src/module/active-effects/sheets/active-effect-config.js +++ b/src/module/active-effects/sheets/active-effect-config.js @@ -25,35 +25,26 @@ export class DLActiveEffectConfig extends foundry.applications.sheets.ActiveEffe /** @override */ async _prepareContext(options={}) { let context = await super._prepareContext(options) - const legacyTransfer = CONFIG.ActiveEffect.legacyTransferral - - const labels = { - transfer: { - name: game.i18n.localize(`EFFECT.Transfer${legacyTransfer ? "Legacy" : ""}`), - hint: game.i18n.localize(`EFFECT.TransferHint${legacyTransfer ? "Legacy" : ""}`) - } - } const effect = foundry.utils.deepClone(this.document) const data = { - labels, + changesFields: this.document.system.schema.fields, + changeFields: this.document.system.schema.fields.changes.element.fields, effect: effect, // Backwards compatibility data: effect, isActorEffect: this.document.parent.documentName === 'Actor', isItemEffect: this.document.parent.documentName === 'Item', - descriptionHTML: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.description, {secrets: this.document.isOwner}), - modes: Object.entries(CONST.ACTIVE_EFFECT_CHANGE_TYPES).reduce((obj, e) => { + types: Object.entries(CONST.ACTIVE_EFFECT_CHANGE_TYPES).reduce((obj, e) => { obj[e[1]] = game.i18n.localize('EFFECT.CHANGES.TYPES.' + e[0]) return obj }, {}), + descriptionHTML: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.description, {secrets: this.document.isOwner}), + availableChangeKeys: DLActiveEffectConfig._availableChangeKeys, + specialDurations: DLActiveEffectConfig._specialDurations } context = foundry.utils.mergeObject(context, data) - context.descriptionHTML = await foundry.applications.ux.TextEditor.implementation.enrichHTML(effect.description, {secrets: effect.isOwner}) - context.availableChangeKeys = DLActiveEffectConfig._availableChangeKeys - context.specialDurations = DLActiveEffectConfig._specialDurations - return context } diff --git a/src/module/data/item/CreatureRoleDataModel.js b/src/module/data/item/CreatureRoleDataModel.js index 27c8857a..a7172086 100644 --- a/src/module/data/item/CreatureRoleDataModel.js +++ b/src/module/data/item/CreatureRoleDataModel.js @@ -34,7 +34,7 @@ export default class CreatureRoleDataModel extends foundry.abstract.TypeDataMode defense: makeIntField(), healingRate: makeIntField(), size: makeStringField(), - speed: makeIntField(10), + speed: makeIntField(), power: makeIntField(), insanity: makeInsanity(), corruption: makeCorruption(), diff --git a/src/module/macros/gm-macros.js b/src/module/macros/gm-macros.js index e6e1e723..fc24232d 100644 --- a/src/module/macros/gm-macros.js +++ b/src/module/macros/gm-macros.js @@ -75,7 +75,8 @@ export function requestChallengeRollMacro() { boonsbanestext: { value: boonsbanestext } - } + }, + tokenId: token.document.uuid }; let chatData = { diff --git a/src/templates/item/parts/AE-config-change.hbs b/src/templates/item/parts/AE-config-change.hbs index 90ff6280..0ed10aa7 100644 --- a/src/templates/item/parts/AE-config-change.hbs +++ b/src/templates/item/parts/AE-config-change.hbs @@ -1,17 +1,17 @@
  • - {{formInput fields.key name=change.keyPath value=change.key}} + {{formInput fields.key name=(concat "changes." index ".key") value=change.key choices=keys}}
    - {{formInput fields.type name=change.typePath value=change.type choices=changeTypes}} + {{formInput fields.type name=(concat "changes." index ".type") value=change.type choices=types}}
    - {{formInput fields.value name=change.valuePath value=change.value elementType="input"}} + {{formInput fields.value name=(concat "changes." index ".value") value=change.value elementType="input"}} +
    + +
    + {{formInput fields.priority name=(concat "changes." index ".name") value=change.priority placeholder=0}}
    - - {{!--
    - {{formInput fields.priority name=change.priorityPath value=change.priority placeholder=defaultPriority}} -
    --}}
    diff --git a/src/templates/item/parts/AE-config-changes.hbs b/src/templates/item/parts/AE-config-changes.hbs index 475dd111..0d72eb19 100644 --- a/src/templates/item/parts/AE-config-changes.hbs +++ b/src/templates/item/parts/AE-config-changes.hbs @@ -3,14 +3,15 @@
    {{localize "EFFECT.FIELDS.changes.element.key.label"}}
    {{localize "EFFECT.FIELDS.changes.element.type.label"}}
    {{localize "EFFECT.FIELDS.changes.element.value.label"}}
    - {{!--
    {{localize "EFFECT.FIELDS.changes.element.priority.label"}}
    --}} +
    {{localize "EFFECT.FIELDS.changes.element.priority.label"}}
      - {{#each changes as |change|}} - {{> "systems/demonlord/templates/item/parts/AE-config-change.hbs" data=change}} + {{!-- {{json changeFields.key}} --}} + {{#each effect.changes as |change index|}} + {{> "systems/demonlord/templates/item/parts/AE-config-change.hbs" fields=../changeFields change=change keys=../availableChangeKeys types=../types index=index}} {{/each}}
    From 2d5c1c63af187c5656f184661ca171bdfbb23721 Mon Sep 17 00:00:00 2001 From: juanferrer Date: Thu, 11 Jun 2026 09:51:17 +0100 Subject: [PATCH 15/20] Migrate specialDuration to core expiryEvents --- src/module/active-effects/active-effect.mjs | 39 +++++ src/module/active-effects/afflictions.js | 18 +-- src/module/active-effects/effects.js | 12 +- src/module/active-effects/item-effects.js | 11 ++ .../sheets/active-effect-config.js | 17 --- src/module/actor/actor.js | 116 +++++++-------- src/module/actor/sheets/base-actor-sheet.js | 2 +- src/module/chat/chat-listeners.js | 12 +- src/module/combat/combat.js | 135 ++++-------------- 9 files changed, 152 insertions(+), 210 deletions(-) create mode 100644 src/module/active-effects/active-effect.mjs diff --git a/src/module/active-effects/active-effect.mjs b/src/module/active-effects/active-effect.mjs new file mode 100644 index 00000000..3192d0a0 --- /dev/null +++ b/src/module/active-effects/active-effect.mjs @@ -0,0 +1,39 @@ +export default class DLActiveEffect extends foundry.documents.ActiveEffect { + /** + * Handle expiration logic for custom expiry events + * @param {*} event + * @param {*} context + * @returns + * @inheritdoc + */ + isExpiryEvent(event, context) { + const newEvents = new Set(['TurnStartSource', 'TurnEndSource', 'NextAttackRoll', 'NextChallengeRoll', 'NextD20Roll', 'NextDamageRoll', 'RestComplete']) + + if (!newEvents.has(event) || !newEvents.has(this.duration.expiry)) return super.isExpiryEvent(event, context) + + switch (event) { + case 'turnStartSource': + return this.origin?.startsWith(context.actorUuid) + case 'turnEndSource': + // Do not delete effects which are created in the same turn and round. + if (context.combat.current.round === this.duration.startRound && context.combat.current.turn === this.duration.startTurn + 1 || context.combat.current.round === this.duration.startRound + 1 && context.combat.current.turn === 0) return false + return this.origin?.startsWith(context.actorUuid) + + case 'nextAttackRoll': + return this.actor.uuid === context.actorUuid + + case 'nextChallengeRoll': + return this.actor.uuid === context.actorUuid + + case 'nextD20Roll': + return this.actor.uuid === context.actorUuid + + case 'nextDamageRoll': + return this.actor.uuid === context.actorUuid + + case 'restComplete': + return this.actor.uuid === context.actorUuid + + } + } +} diff --git a/src/module/active-effects/afflictions.js b/src/module/active-effects/afflictions.js index 35ef7a31..c7e430e0 100644 --- a/src/module/active-effects/afflictions.js +++ b/src/module/active-effects/afflictions.js @@ -303,7 +303,7 @@ export class DLAfflictions { addEffect('system.bonuses.defense.boons.spell', -1, effectPriority), ], ), - ) + ) // Unconscious effectsDataList.push( @@ -346,7 +346,7 @@ export class DLAfflictions { addEffect('system.bonuses.defense.boons.perception', 1, effectPriority), // TODO: Auto disable when Dazed, Stunned or Unconscious ], - {'specialDuration' : 'EndOfTheRound'} + { 'expiry': 'endOfTheRound' } ), ) @@ -356,7 +356,7 @@ export class DLAfflictions { 'help', 'systems/demonlord/assets/icons/effects/help.svg', [], // TODO: Add boons? Aka help should be applied to the receiver - {'specialDuration' : 'EndOfTheRound'} + { 'expiry': 'endOfTheRound' } ), ) @@ -368,7 +368,7 @@ export class DLAfflictions { addEffect('system.bonuses.attack.boons.all', 1, effectPriority), addEffect('system.bonuses.challenge.boons.all', 1, effectPriority), ], - {'specialDuration' : 'NextD20Roll'} + { 'expiry': 'nextD20Roll' } ), ) @@ -386,21 +386,21 @@ export class DLAfflictions { addEffect('system.bonuses.attack.boons.will', 1, effectPriority), addEffect('system.bonuses.attack.boons.perception', 1, effectPriority), ], - {'specialDuration' : 'EndOfTheRound'} + { 'expiry': 'roundEnd' } ), ) // Reload - effectsDataList.push(_buildBaseAffliction('reload', 'systems/demonlord/assets/icons/effects/reload.svg', [], {'specialDuration' : 'EndOfTheRound'})) + effectsDataList.push(_buildBaseAffliction('reload', 'systems/demonlord/assets/icons/effects/reload.svg', [], { 'expiry': 'roundEnd' })) // Retreat - effectsDataList.push(_buildBaseAffliction('retreat', 'systems/demonlord/assets/icons/effects/retreat.svg', [], {'specialDuration' : 'EndOfTheRound'})) + effectsDataList.push(_buildBaseAffliction('retreat', 'systems/demonlord/assets/icons/effects/retreat.svg', [], { 'expiry': 'roundEnd' })) // Rush - effectsDataList.push(_buildBaseAffliction('rush', 'systems/demonlord/assets/icons/effects/rush.svg', [], {'specialDuration' : 'EndOfTheRound'})) + effectsDataList.push(_buildBaseAffliction('rush', 'systems/demonlord/assets/icons/effects/rush.svg', [], { 'expiry': 'roundEnd' })) // Stabilize - effectsDataList.push(_buildBaseAffliction('stabilize', 'systems/demonlord/assets/icons/effects/stabilize.svg', [], {'specialDuration' : 'EndOfTheRound'})) + effectsDataList.push(_buildBaseAffliction('stabilize', 'systems/demonlord/assets/icons/effects/stabilize.svg', [], { 'expiry': 'roundEnd' })) // ----------------------- DAMAGE EFFECTS -------------------------- // diff --git a/src/module/active-effects/effects.js b/src/module/active-effects/effects.js index 3ebee57a..3412c63e 100644 --- a/src/module/active-effects/effects.js +++ b/src/module/active-effects/effects.js @@ -136,18 +136,18 @@ export function prepareActiveEffectCategories(effects, showCreateButtons = false e.dlRemaining = e.duration.label } - let specialDuration = foundry.utils.getProperty(e, `flags.${game.system.id}.specialDuration`) - if (specialDuration !== 'None' && specialDuration !== undefined) { + let expiryEvent = e.expiry + if (expiryEvent !== 'None' && expiryEvent !== undefined) { const actorName = e.origin !== null ? fromUuidSync(e.origin)?.parent?.name : '' - switch (specialDuration) { - case 'TurnEndSource': + switch (expiryEvent) { + case 'turnEndSource': e.dlRemaining = `TurnEnd [${actorName}]` break - case 'TurnStartSource': + case 'turnStartSource': e.dlRemaining = `TurnStart [${actorName}]` break default: - e.dlRemaining = specialDuration + e.dlRemaining = expiryEvent } } diff --git a/src/module/active-effects/item-effects.js b/src/module/active-effects/item-effects.js index 86299a1f..0db1558c 100644 --- a/src/module/active-effects/item-effects.js +++ b/src/module/active-effects/item-effects.js @@ -1,5 +1,6 @@ import { DemonlordActor } from '../actor/actor' import { plusify } from '../utils/utils' +import { i18n } from '../utils/utils' export const multiplyEffect = (key, value, priority) => ({ key: key, @@ -56,6 +57,16 @@ export const addObject = (key, value) => ({ const falsyChangeFilter = change => Boolean(change?.value) +export const registerExpiryEvents = () => { + CONFIG.ActiveEffect.expiryEvents.TurnStartSource = i18n('DL.SpecialDurationTurnStartSource') + CONFIG.ActiveEffect.expiryEvents.TurnEndSource = i18n('DL.SpecialDurationTurnEndSource') + CONFIG.ActiveEffect.expiryEvents.NextAttackRoll = i18n('DL.SpecialDurationNextAttackRoll') + CONFIG.ActiveEffect.expiryEvents.NextChallengeRoll = i18n('DL.SpecialDurationNextChallengeRoll') + CONFIG.ActiveEffect.expiryEvents.NextD20Roll = i18n('DL.SpecialDurationNextD20Roll') + CONFIG.ActiveEffect.expiryEvents.NextDamageRoll = i18n('DL.SpecialDurationNextDamageRoll') + CONFIG.ActiveEffect.expiryEvents.RestComplete = i18n('DL.SpecialDurationRestComplete') +} + /* -------------------------------------------- */ export class DLActiveEffects { diff --git a/src/module/active-effects/sheets/active-effect-config.js b/src/module/active-effects/sheets/active-effect-config.js index 3a6d4777..c2ceeac6 100644 --- a/src/module/active-effects/sheets/active-effect-config.js +++ b/src/module/active-effects/sheets/active-effect-config.js @@ -52,7 +52,6 @@ export class DLActiveEffectConfig extends foundry.applications.sheets.ActiveEffe context.descriptionHTML = await foundry.applications.ux.TextEditor.implementation.enrichHTML(effect.description, {secrets: effect.isOwner}) context.availableChangeKeys = DLActiveEffectConfig._availableChangeKeys - context.specialDurations = DLActiveEffectConfig._specialDurations return context } @@ -63,22 +62,6 @@ export class DLActiveEffectConfig extends foundry.applications.sheets.ActiveEffe if (currTabId !== "changes") this.position.height = this.element.offsetHeight ?? "auto"; } -static initializeSpecialDurations() { - DLActiveEffectConfig._specialDurations = { - 'None': i18n('DL.SpecialDurationNone'), - 'EndOfTheRound' : i18n('DL.SpecialDurationEndOfTheRound'), - 'TurnStart': i18n('DL.SpecialDurationTurnStart'), - 'TurnEnd': i18n('DL.SpecialDurationTurnEnd'), - 'TurnStartSource': i18n('DL.SpecialDurationTurnStartSource'), - 'TurnEndSource': i18n('DL.SpecialDurationTurnEndSource'), - 'NextAttackRoll': i18n('DL.SpecialDurationNextAttackRoll'), - 'NextChallengeRoll': i18n('DL.SpecialDurationNextChallengeRoll'), - 'NextD20Roll': i18n('DL.SpecialDurationNextD20Roll'), - 'NextDamageRoll': i18n('DL.SpecialDurationNextDamageRoll'), - 'RestComplete': i18n('DL.SpecialDurationRestComplete') - } -} - static initializeChangeKeys() { DLActiveEffectConfig._availableChangeKeys = { // : diff --git a/src/module/actor/actor.js b/src/module/actor/actor.js index 4e0adba5..fd7853b7 100644 --- a/src/module/actor/actor.js +++ b/src/module/actor/actor.js @@ -601,11 +601,13 @@ getTargetAttackBane(target) { const hitTarget = (defender && attackRoll?.total >= targetNumber) ? defenderToken : [] - for (let effect of this.appliedEffects) { - const specialDuration = foundry.utils.getProperty(effect, `flags.${game.system.id}.specialDuration`) - const animate = foundry.utils.getProperty(effect, `flags.${game.system.id}.doNotAnimate`) === undefined ? true: false - if (specialDuration === 'NextD20Roll' || specialDuration === 'NextAttackRoll') await effect?.delete({animate:animate}) - } + ActiveEffect.registry.refresh('nextD20Roll', { + actorUuid: this.uuid + }) + + ActiveEffect.registry.refresh('nextAttackRoll', { + actorUuid: this.uuid + }) Hooks.call('DL.RollAttack', { sourceToken: attacker.token || tokenManager.getTokenByActorId(attacker.id), @@ -688,11 +690,13 @@ getTargetAttackBane(target) { else postAttributeToChat(this, attribute.key, challengeRoll, parseInt(inputBoons) || 0, parseInt(inputModifier) || 0) - for (let effect of this.appliedEffects) { - const specialDuration = foundry.utils.getProperty(effect, `flags.${game.system.id}.specialDuration`) - const animate = foundry.utils.getProperty(effect, `flags.${game.system.id}.doNotAnimate`) === undefined ? true: false - if (specialDuration === 'NextD20Roll' || specialDuration === 'NextChallengeRoll') await effect?.delete({animate:animate}) - } + ActiveEffect.registry.refresh('nextD20Roll', { + actorUuid: this.uuid + }) + + ActiveEffect.registry.refresh('nextChallengeRoll', { + actorUuid: this.uuid + }) return challengeRoll } @@ -744,11 +748,13 @@ getTargetAttackBane(target) { await attackRoll.evaluate() postAttackToChat(this, tokenManager.targets[0].actor, fakeItem, attackRoll, attribute.key, defense, parseInt(inputBoons) || 0, parseInt(inputModifier) || 0) - for (let effect of this.appliedEffects) { - const specialDuration = foundry.utils.getProperty(effect, `flags.${game.system.id}.specialDuration`) - const animate = foundry.utils.getProperty(effect, `flags.${game.system.id}.doNotAnimate`) === undefined ? true: false - if (specialDuration === 'NextD20Roll' || specialDuration === 'NextAttackRoll') await effect?.delete({animate:animate}) - } + ActiveEffect.registry.refresh('nextD20Roll', { + actorUuid: this.uuid + }) + + ActiveEffect.registry.refresh('nextChallengeRoll', { + actorUuid: this.uuid + }) return attackRoll } @@ -831,11 +837,13 @@ getTargetAttackBane(target) { attackRoll = new Roll(this.rollFormula(modifiers, boons, boonsReroll), this.system) await attackRoll.evaluate() - for (let effect of this.appliedEffects) { - const specialDuration = foundry.utils.getProperty(effect, `flags.${game.system.id}.specialDuration`) - const animate = foundry.utils.getProperty(effect, `flags.${game.system.id}.doNotAnimate`) === undefined ? true: false - if (specialDuration === 'NextD20Roll' || specialDuration === 'NextAttackRoll') await effect?.delete({animate:animate}) - } + ActiveEffect.registry.refresh('nextD20Roll', { + actorUuid: this.uuid + }) + + ActiveEffect.registry.refresh('nextAttackRoll', { + actorUuid: this.uuid + }) if (itemMacroEnabled) { let successfullHit = false @@ -954,11 +962,13 @@ getTargetAttackBane(target) { postSpellToChat(this, spell, attackRoll, target[0]?.actor, parseInt(inputBoons) || 0, parseInt(inputModifier) || 0) - for (let effect of this.appliedEffects) { - const specialDuration = foundry.utils.getProperty(effect, `flags.${game.system.id}.specialDuration`) - const animate = foundry.utils.getProperty(effect, `flags.${game.system.id}.doNotAnimate`) === undefined ? true: false - if (specialDuration === 'NextD20Roll' || specialDuration === 'NextAttackRoll') await effect?.delete({animate:animate}) - } + ActiveEffect.registry.refresh('nextD20Roll', { + actorUuid: this.uuid + }) + + ActiveEffect.registry.refresh('nextAttackRoll', { + actorUuid: this.uuid + }) // Add concentration if it's in the spell duration const concentrate = CONFIG.statusEffects['concentrate'] @@ -1091,11 +1101,14 @@ getTargetAttackBane(target) { attackRoll = new Roll(this.rollFormula(modifiers, boons, boonsReroll), this.system) await attackRoll.evaluate() - for (let effect of this.appliedEffects) { - const specialDuration = foundry.utils.getProperty(effect, `flags.${game.system.id}.specialDuration`) - const animate = foundry.utils.getProperty(effect, `flags.${game.system.id}.doNotAnimate`) === undefined ? true: false - if (specialDuration === 'NextD20Roll' || specialDuration === 'NextAttackRoll') await effect?.delete({animate:animate}) - } + ActiveEffect.registry.refresh('nextD20Roll', { + actorUuid: this.uuid + }) + + ActiveEffect.registry.refresh('nextAttackRoll', { + actorUuid: this.uuid + }) + if (itemMacroEnabled) { const defender = target?.actor const targetNumber = @@ -1193,12 +1206,7 @@ getTargetAttackBane(target) { value: -1, mode: CONST.ACTIVE_EFFECT_CHANGE_TYPES.ADD, }, ], - flags: { - demonlord: { - specialDuration: 'NextChallengeRoll', - doNotAnimate: true, - }, - }, + expiry: 'NextChallengeRoll' }) // Nested function @@ -1230,11 +1238,7 @@ getTargetAttackBane(target) { value: (target.system.willChallengeRollBanes)*-1, mode: CONST.ACTIVE_EFFECT_CHANGE_TYPES.ADD, }, ], - flags: { - demonlord: { - specialDuration: 'NextChallengeRoll', animate: false - }, - }, + expiry: 'NextChallengeRoll' }) await ActiveEffect.create(willChallengeRollBanesEffect, { @@ -1256,12 +1260,7 @@ getTargetAttackBane(target) { value: darkMagicSpellsKnown.length, mode: CONST.ACTIVE_EFFECT_CHANGE_TYPES.ADD, }, ], - flags: { - demonlord: { - specialDuration: 'NextChallengeRoll', - doNotAnimate : true, - }, - }, + expiry: 'NextChallengeRoll' }) await ActiveEffect.create(darkMagicSpellsKnownEffect, { @@ -1485,12 +1484,7 @@ getTargetAttackBane(target) { duration: { rounds: 1, }, - flags: { - demonlord: { - immuneToActoruuid: target.actor.uuid, - specialDuration: 'RestComplete', - }, - }, + expiry: 'RestComplete', origin: target.actor.uuid }) @@ -1644,12 +1638,7 @@ getTargetAttackBane(target) { creature: target.actor.name }), icon: 'systems/demonlord/assets/icons/effects/eye-target.svg', - flags: { - demonlord: { - FrightenedFromActoruuid: target.actor.uuid, - specialDuration: 'RestComplete', - }, - }, + expiry: 'RestComplete', duration: { rounds: 1, }, @@ -1801,11 +1790,10 @@ getTargetAttackBane(target) { if (restTime === 24) this.applyHealing(true) } - for (let effect of this.appliedEffects) { - const specialDuration = foundry.utils.getProperty(effect, `flags.${game.system.id}.specialDuration`) - // if (!(specialDuration?.length > 0)) continue - if (specialDuration === 'RestComplete') await effect?.delete() - } + ActiveEffect.registry.refresh('restComplete', { + actorUuid: this.uuid + }) + postRestToChat(this, restTime, magicRecovery, talentRecovery, healing) } diff --git a/src/module/actor/sheets/base-actor-sheet.js b/src/module/actor/sheets/base-actor-sheet.js index c5c0acf8..318e5194 100644 --- a/src/module/actor/sheets/base-actor-sheet.js +++ b/src/module/actor/sheets/base-actor-sheet.js @@ -661,7 +661,7 @@ export default class DLBaseActorSheet extends HandlebarsApplicationMixin(ActorSh // Rollable Attributes e.querySelectorAll('.attribute .name')?.forEach(el => el.addEventListener('click', ev => { - const div = ev.currentTarge + const div = ev.currentTarget const attributeName = div.dataset.key const attribute = this.actor.getAttribute(attributeName) if (!attribute.immune) { diff --git a/src/module/chat/chat-listeners.js b/src/module/chat/chat-listeners.js index a338726b..0c1f58a3 100644 --- a/src/module/chat/chat-listeners.js +++ b/src/module/chat/chat-listeners.js @@ -57,14 +57,9 @@ async function _onChatRollDamage(event) { const li = event.currentTarget const actor = _getChatCardActor(li.closest('.demonlord')) - const appliedEffects = tokenManager.getTokenByActorId(actor.id)?.actor?.appliedEffects - - if (appliedEffects?.length) { - for (let effect of appliedEffects) { - const specialDuration = foundry.utils.getProperty(effect, `flags.${game.system.id}.specialDuration`) - if (specialDuration === 'NextDamageRoll') await effect?.delete() - } - } + ActiveEffect.registry.refresh('NextDamageRoll', { + actorUuid: this.uuid + }) const item = li.children[0] var damageformula = item.dataset.damage @@ -254,6 +249,7 @@ async function _onChatApplyEffect(event) { //Repace origin with Item UUID, otherwise effect cannot be removed //specialDuration: TurnStartSource, TurnEndSource + // TODO: Have a look at this let aeUuid = activeEffect.uuid let effectOrigin = aeUuid.substr(0, aeUuid.search('.ActiveEffect.')) let effectOriginName = fromUuidSync(effectOrigin).name diff --git a/src/module/combat/combat.js b/src/module/combat/combat.js index 06f5aec6..5372c0f5 100644 --- a/src/module/combat/combat.js +++ b/src/module/combat/combat.js @@ -449,11 +449,6 @@ export async function _onUpdateWorldTime(worldTime, _delta, _options, _userId) { const enabledEffects = actor.effects const tempEffects = enabledEffects.filter(e => e.duration?.rounds > 0 || e.duration?.seconds > 0) tempEffects.forEach(e => { - // Ignore effects with specialDuration - if (inCombat) { - let specialDuration = foundry.utils.getProperty(e, `flags.${game.system.id}.specialDuration`) - if (specialDuration !== 'None' && specialDuration !== undefined) return - } const eType = e.flags?.demonlord?.sourceType const isSpell1Round = eType === 'spell' && e.duration.rounds === 1 @@ -529,15 +524,23 @@ Hooks.on('preCreateCombatant', async (combatant, _data, _options, userId) => { await combatant.updateSource({initiative: null}) }) -// Delete Effects with specialDuration -async function deleteSpecialdurationEffects(combatant) { +// Delete effects that should expire during combat +async function deleteCombatEffects(combatant) { let tokenD = fromUuidSync(`Scene.${combatant.sceneId}.Token.${combatant.tokenId}`) if (!tokenD) return let actor = tokenD.actorLink ? game.actors.get(combatant.actorId) : fromUuidSync(`Scene.${combatant.sceneId}.Token.${combatant.tokenId}.Actor.${combatant.actorId}`) for (let effect of actor.appliedEffects) { - const specialDuration = foundry.utils.getProperty(effect, `flags.${game.system.id}.specialDuration`) - if (!(specialDuration?.length > 0)) continue - if (specialDuration !== "None" && specialDuration !== undefined && specialDuration !== 'RestComplete') await effect?.delete() + if ([ + // Durations that should expire during combat + 'turnStartSource', + 'turnEndSource', + 'roundStart', + 'roundEnd', + 'turnStart', + 'turnEnd', + 'combatStart', + 'combatEnd' + ].contains(effect.expiry)) await effect?.delete() } } @@ -549,29 +552,15 @@ async function deleteSurroundedStatus(combatant) { await effect?.delete() } -// Delete End Of The Round Effects -async function deleteEndOfTheRoundEffects(currentActors) { - for await (let actor of currentActors) { - let deleteIds = [] - const endOfTheRoundEffects = actor.appliedEffects.filter(e => e.flags?.demonlord?.specialDuration === 'EndOfTheRound') - endOfTheRoundEffects.forEach(e => { - deleteIds.push(e._id) - }) - if (deleteIds.length) await actor.deleteEmbeddedDocuments('ActiveEffect', deleteIds) - } -} - Hooks.on('deleteCombat', async (combat) => { for (let turn of combat.turns) { let actor = turn.actor - if (!actor) continue - for (let effect of actor.appliedEffects) { - const specialDuration = foundry.utils.getProperty(effect, `flags.${game.system.id}.specialDuration`) - if (!(specialDuration?.length > 0)) continue - if (specialDuration !== "None" && specialDuration !== undefined && specialDuration !== 'RestComplete') await effect?.delete() - } + if (!actor) continue await deleteSurroundedStatus(actor) } + for (let combatant of combat.combatants) { + await deleteCombatEffects(combatant) + } }) @@ -666,7 +655,7 @@ Hooks.on('targetToken', async (user, target, isTargeted) => { Hooks.on("createCombatant", async combatant => { - await deleteSpecialdurationEffects(combatant) + await deleteCombatEffects(combatant) let optionalRuleInitiative = game.settings.get("demonlord", "optionalRuleInitiativeMode") if (optionalRuleInitiative === "s") return await setCombatantGroup(combatant) @@ -689,90 +678,26 @@ Hooks.on("createCombatant", async combatant => { }) Hooks.on('deleteCombatant', async (combatant) => { - await deleteSpecialdurationEffects(combatant) - await deleteSurroundedStatus(combatant) + await deleteCombatEffects(combatant) + await deleteSurroundedStatus(combatant) }) Hooks.on('updateCombat', async (combat) => { if (!game.users.activeGM?.isSelf) return if (combat.current.combatantId === null) return - // SOURCE type expirations - for (let turn of combat.turns) { - let actor = turn.actor - if (!actor) continue - for (let effect of actor.appliedEffects) { - const specialDuration = foundry.utils.getProperty(effect, `flags.${game.system.id}.specialDuration`) - if (!(specialDuration?.length > 0)) continue - if ( - effect.origin?.startsWith(combat.turns.find(x => x._id === combat.previous.combatantId)?.actor.uuid) && - specialDuration === 'TurnEndSource' - ) { - console.warn( - `Effect "${effect.name}" deleted on ${actor.name}, reason: ${ - combat.turns.find(x => x._id === combat.previous.combatantId).actor.name - } ending its turn.`, - ) - // Do not delete effects which are created in the same turn and round. - if (game.combat.current.round === effect.duration.startRound && game.combat.current.turn === effect.duration.startTurn+1 || game.combat.current.round === effect.duration.startRound+1 && game.combat.current.turn ===0) continue - await effect?.delete() - continue - } - if ( - effect.origin?.startsWith(combat.turns.find(x => x._id === combat.current.combatantId).actor.uuid) && - specialDuration === 'TurnStartSource' - ) { - console.warn( - `Effect "${effect.name}" deleted on ${actor.name}, reason: ${ - combat.turns.find(x => x._id === combat.current.combatantId).actor.name - } starting its turn.`, - ) - await effect?.delete() - continue - } - } - } - // TARGET type expirations let currentActor = combat.turns.find(x => x._id === combat.current.combatantId).actor let previousActor = combat.turns.find(x => x._id === combat.previous.combatantId)?.actor - for (let effect of currentActor.allApplicableEffects()) { - const specialDuration = foundry.utils.getProperty(effect, `flags.${game.system.id}.specialDuration`) - if (specialDuration?.length > 0) { - if (specialDuration === 'TurnStart') { - console.warn( - `Effect "${effect.name}" deleted on ${currentActor.name}, reason: ${currentActor.name} starting its turn.`, - ) - await effect?.delete() - continue - } - } - } - - if (previousActor !== undefined) { - for (let effect of previousActor.allApplicableEffects()) { - const specialDuration = foundry.utils.getProperty(effect, `flags.${game.system.id}.specialDuration`) - if (specialDuration?.length > 0) { - if (specialDuration === 'TurnEnd') { - // Do not delete effects which are created in the same turn and round. PreviousActor startTurn+1! - if (game.combat.current.round === effect.duration.startRound && game.combat.current.turn === effect.duration.startTurn+1 || game.combat.current.round === effect.duration.startRound+1 && game.combat.current.turn ===0) continue - console.warn( - `Effect "${effect.name}" deleted on ${previousActor.name}, reason: ${previousActor.name} ending its turn.`, - ) - await effect?.delete() - continue - } - } - } - } - // End Of The Round Expirations - if (game.combat.turn === 0) { - const allCombatants = [...combat.combatants] - let allActors = [] - allCombatants.forEach((combatant) => { - if (combatant.actor.appliedEffects.find(e => e.flags?.demonlord?.specialDuration === 'EndOfTheRound')) allActors.push(combatant.actor) - }) - await deleteEndOfTheRoundEffects(allActors) - } + // SOURCE type expirations + // Now call all the events from combat changing) + ActiveEffect.registry.refresh('TurnEndSource', { + actorUuid: previousActor.uuid, + combat: game.combat.current + }) + ActiveEffect.registry.refresh('TurnStartSource', { + actorUuid: currentActor.uuid, + combat: game.combat.current + }) }) From df1f8ae3c7cd192a9445541a99639cee9aaf49fc Mon Sep 17 00:00:00 2001 From: juanferrer Date: Thu, 11 Jun 2026 09:51:44 +0100 Subject: [PATCH 16/20] Stop overriding active effect config templates for details and duration parts --- src/module/active-effects/sheets/active-effect-config.js | 4 ++-- src/module/templates.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/module/active-effects/sheets/active-effect-config.js b/src/module/active-effects/sheets/active-effect-config.js index c2ceeac6..60d256a5 100644 --- a/src/module/active-effects/sheets/active-effect-config.js +++ b/src/module/active-effects/sheets/active-effect-config.js @@ -16,8 +16,8 @@ export class DLActiveEffectConfig extends foundry.applications.sheets.ActiveEffe }; static PARTS = foundry.utils.mergeObject(super.PARTS ?? {}, { - details: { template: "systems/demonlord/templates/item/parts/AE-config-details.hbs"}, - duration: { template: "systems/demonlord/templates/item/parts/AE-config-duration.hbs"}, + // details: { template: "systems/demonlord/templates/item/parts/AE-config-details.hbs"}, + // duration: { template: "systems/demonlord/templates/item/parts/AE-config-duration.hbs"}, changes: { template: "systems/demonlord/templates/item/parts/AE-config-changes.hbs"} }) diff --git a/src/module/templates.js b/src/module/templates.js index a6c72dbe..1806f611 100644 --- a/src/module/templates.js +++ b/src/module/templates.js @@ -64,8 +64,8 @@ export const preloadHandlebarsTemplates = async function () { // General templates 'systems/demonlord/templates/generic/tab-navigation.hbs', - "systems/demonlord/templates/item/parts/AE-config-details.hbs", - "systems/demonlord/templates/item/parts/AE-config-duration.hbs", + // "systems/demonlord/templates/item/parts/AE-config-details.hbs", + // "systems/demonlord/templates/item/parts/AE-config-duration.hbs", "systems/demonlord/templates/item/parts/AE-config-changes.hbs", "systems/demonlord/templates/item/parts/AE-config-change.hbs", From ed035ac84785269d6fd0c288d51efe431466af5d Mon Sep 17 00:00:00 2001 From: juanferrer Date: Fri, 12 Jun 2026 08:02:18 +0100 Subject: [PATCH 17/20] Finish specialDuration migration to core expiryEvents --- src/module/active-effects/active-effect.mjs | 4 ++-- src/module/active-effects/item-effects.js | 14 +++++++------- src/module/chat/chat-listeners.js | 4 ++-- src/module/combat/combat.js | 17 ++++++++++------- src/module/demonlord.js | 19 +++++++------------ src/module/utils/chat.js | 10 +++++----- 6 files changed, 33 insertions(+), 35 deletions(-) diff --git a/src/module/active-effects/active-effect.mjs b/src/module/active-effects/active-effect.mjs index 3192d0a0..ced86afe 100644 --- a/src/module/active-effects/active-effect.mjs +++ b/src/module/active-effects/active-effect.mjs @@ -7,7 +7,7 @@ export default class DLActiveEffect extends foundry.documents.ActiveEffect { * @inheritdoc */ isExpiryEvent(event, context) { - const newEvents = new Set(['TurnStartSource', 'TurnEndSource', 'NextAttackRoll', 'NextChallengeRoll', 'NextD20Roll', 'NextDamageRoll', 'RestComplete']) + const newEvents = new Set(['turnStartSource', 'turnEndSource', 'nextAttackRoll', 'nextChallengeRoll', 'nextD20Roll', 'nextDamageRoll', 'restComplete']) if (!newEvents.has(event) || !newEvents.has(this.duration.expiry)) return super.isExpiryEvent(event, context) @@ -16,7 +16,7 @@ export default class DLActiveEffect extends foundry.documents.ActiveEffect { return this.origin?.startsWith(context.actorUuid) case 'turnEndSource': // Do not delete effects which are created in the same turn and round. - if (context.combat.current.round === this.duration.startRound && context.combat.current.turn === this.duration.startTurn + 1 || context.combat.current.round === this.duration.startRound + 1 && context.combat.current.turn === 0) return false + if (context.combat.round === this.start.round && context.combat.turn === this.start.turn + 1 || context.combat.round === this.start.round + 1 && context.combat.turn === 0) return false return this.origin?.startsWith(context.actorUuid) case 'nextAttackRoll': diff --git a/src/module/active-effects/item-effects.js b/src/module/active-effects/item-effects.js index 0db1558c..c44742e0 100644 --- a/src/module/active-effects/item-effects.js +++ b/src/module/active-effects/item-effects.js @@ -58,13 +58,13 @@ export const addObject = (key, value) => ({ const falsyChangeFilter = change => Boolean(change?.value) export const registerExpiryEvents = () => { - CONFIG.ActiveEffect.expiryEvents.TurnStartSource = i18n('DL.SpecialDurationTurnStartSource') - CONFIG.ActiveEffect.expiryEvents.TurnEndSource = i18n('DL.SpecialDurationTurnEndSource') - CONFIG.ActiveEffect.expiryEvents.NextAttackRoll = i18n('DL.SpecialDurationNextAttackRoll') - CONFIG.ActiveEffect.expiryEvents.NextChallengeRoll = i18n('DL.SpecialDurationNextChallengeRoll') - CONFIG.ActiveEffect.expiryEvents.NextD20Roll = i18n('DL.SpecialDurationNextD20Roll') - CONFIG.ActiveEffect.expiryEvents.NextDamageRoll = i18n('DL.SpecialDurationNextDamageRoll') - CONFIG.ActiveEffect.expiryEvents.RestComplete = i18n('DL.SpecialDurationRestComplete') + CONFIG.ActiveEffect.expiryEvents.turnStartSource = i18n('DL.SpecialDurationTurnStartSource') + CONFIG.ActiveEffect.expiryEvents.turnEndSource = i18n('DL.SpecialDurationTurnEndSource') + CONFIG.ActiveEffect.expiryEvents.nextAttackRoll = i18n('DL.SpecialDurationNextAttackRoll') + CONFIG.ActiveEffect.expiryEvents.nextChallengeRoll = i18n('DL.SpecialDurationNextChallengeRoll') + CONFIG.ActiveEffect.expiryEvents.nextD20Roll = i18n('DL.SpecialDurationNextD20Roll') + CONFIG.ActiveEffect.expiryEvents.nextDamageRoll = i18n('DL.SpecialDurationNextDamageRoll') + CONFIG.ActiveEffect.expiryEvents.restComplete = i18n('DL.SpecialDurationRestComplete') } /* -------------------------------------------- */ diff --git a/src/module/chat/chat-listeners.js b/src/module/chat/chat-listeners.js index 0c1f58a3..3d2a2d0f 100644 --- a/src/module/chat/chat-listeners.js +++ b/src/module/chat/chat-listeners.js @@ -253,9 +253,9 @@ async function _onChatApplyEffect(event) { let aeUuid = activeEffect.uuid let effectOrigin = aeUuid.substr(0, aeUuid.search('.ActiveEffect.')) let effectOriginName = fromUuidSync(effectOrigin).name - if (activeEffect.origin.startsWith('Compendium') || ['TurnStartSource', 'TurnEndSource'].includes(foundry.utils.getProperty(effectData, `flags.${game.system.id}.specialDuration`))) { + if (activeEffect.origin.startsWith('Compendium') || ['turnStartSource', 'turnEndSource'].includes(foundry.utils.getProperty(effectData, `flags.${game.system.id}.specialDuration`))) { effectData.origin = effectOrigin - if (['TurnStartSource', 'TurnEndSource'].includes(foundry.utils.getProperty(effectData, `flags.${game.system.id}.specialDuration`)) && effectData.origin.startsWith('Actor')) + if (['turnStartSource', 'turnEndSource'].includes(foundry.utils.getProperty(effectData, `flags.${game.system.id}.specialDuration`)) && effectData.origin.startsWith('Actor')) ui.notifications.warn(game.i18n.localize('DL.DialogEffectsWillNotExpire')) } if (effectData.name !== effectOriginName) effectData.name = `${effectData.name} [${effectOriginName}]` diff --git a/src/module/combat/combat.js b/src/module/combat/combat.js index 5372c0f5..d3673cb7 100644 --- a/src/module/combat/combat.js +++ b/src/module/combat/combat.js @@ -540,7 +540,7 @@ async function deleteCombatEffects(combatant) { 'turnEnd', 'combatStart', 'combatEnd' - ].contains(effect.expiry)) await effect?.delete() + ].includes(effect.expiry)) await effect?.delete() } } @@ -682,7 +682,8 @@ Hooks.on('deleteCombatant', async (combatant) => { await deleteSurroundedStatus(combatant) }) -Hooks.on('updateCombat', async (combat) => { +//Hooks.on('updateCombat', async (combat) => { +Hooks.on('combatTurn', async (combat, _updateData, _updateOptions) => { if (!game.users.activeGM?.isSelf) return if (combat.current.combatantId === null) return @@ -692,11 +693,13 @@ Hooks.on('updateCombat', async (combat) => { // SOURCE type expirations // Now call all the events from combat changing) - ActiveEffect.registry.refresh('TurnEndSource', { - actorUuid: previousActor.uuid, - combat: game.combat.current - }) - ActiveEffect.registry.refresh('TurnStartSource', { + if (previousActor) { + ActiveEffect.registry.refresh('turnEndSource', { + actorUuid: previousActor.uuid, + combat: game.combat.current + }) + } + ActiveEffect.registry.refresh('turnStartSource', { actorUuid: currentActor.uuid, combat: game.combat.current }) diff --git a/src/module/demonlord.js b/src/module/demonlord.js index cdd77e7f..aebe9f5a 100644 --- a/src/module/demonlord.js +++ b/src/module/demonlord.js @@ -39,10 +39,12 @@ import SpecialActionDataModel from './data/item/SpecialActionDataModel.js' import SpellDataModel from './data/item/SpellDataModel.js' import TalentDataModel from './data/item/TalentDataModel.js' import WeaponDataModel from './data/item/WeaponDataModel.js' +import DLActiveEffect from './active-effects/active-effect.mjs' import './playertrackercontrol' import {initChatListeners} from './chat/chat-listeners' import 'tippy.js/dist/tippy.css' import {registerHandlebarsHelpers} from "./utils/handlebars-helpers" +import { registerExpiryEvents } from "./active-effects/item-effects.js" import {_onUpdateWorldTime, DLCombat} from "./combat/combat" // optional for styling import { activateSocketListener } from "./utils/socket.js" import DLCompendiumBrowser from './compendium-browser/compendium-browser.js' @@ -77,6 +79,7 @@ Hooks.once('init', async function () { CONFIG.Actor.documentClass = DemonlordActor CONFIG.Token.objectClass = DemonlordToken CONFIG.Item.documentClass = DemonlordItem + CONFIG.ActiveEffect.documentClass = DLActiveEffect foundry.applications.apps.DocumentSheetConfig.unregisterSheet(ActiveEffect, "core", foundry.applications.sheets.ActiveEffectConfig, {}) foundry.applications.apps.DocumentSheetConfig.registerSheet(ActiveEffect, "demonlord", DLActiveEffectConfig, {makeDefault: true}) CONFIG.ui.combat = DLCombatTracker @@ -156,6 +159,8 @@ Hooks.once('init', async function () { preloadHandlebarsTemplates() registerHandlebarsHelpers() + registerExpiryEvents() + // Support Babele translations if (typeof Babele !== 'undefined') { Babele.get().setSystemTranslationsDir('packs/translations') @@ -235,13 +240,13 @@ Hooks.once('setup', function () { name: effect.name, img: effect.icon, hud: true, - order: effect.order + order: effect.order, + duration: effect.duration } } // Set active effect keys-labels DLActiveEffectConfig.initializeChangeKeys() - DLActiveEffectConfig.initializeSpecialDurations() }) /** @@ -443,16 +448,6 @@ export async function findDeleteEffect(actor, effectId) { return await effect?.delete() } - Hooks.on('preUpdateActiveEffect', async (activeEffect, changes, _, userId ) => { - // Set specialDuration effects to temporary - if (game.user.id !== userId) return - const specialDuration = foundry.utils.getProperty(changes, `flags.${game.system.id}.specialDuration`) - if (specialDuration !== "None" && specialDuration !== undefined) - { - changes.duration.rounds = 1 - } -}) - Hooks.on('deleteActiveEffect', async (activeEffect, _, userId) => { if (game.user.id !== userId) return const statuses = activeEffect.statuses diff --git a/src/module/utils/chat.js b/src/module/utils/chat.js index b7e52bd2..afa968e7 100644 --- a/src/module/utils/chat.js +++ b/src/module/utils/chat.js @@ -1,7 +1,7 @@ /** - * - * @param {[]} a - * @param {[]} b + * + * @param {[]} a + * @param {[]} b */ export function changesMatch(a, b) { // Falsy arrays are not equal to each other @@ -15,8 +15,8 @@ export function changesMatch(a, b) { // Check each element (order matters) for (let i = 0; i < a.length; i++) { - if (a[i].key !== b[i].key || a[i].mode !== b[i].mode || a[i].value !== b[i].value) return false + if (a[i].key !== b[i].key || a[i].type !== b[i].type || a[i].value !== b[i].value) return false } return true -} \ No newline at end of file +} From 5def785f895744a7fca0386279d46c046277d5e4 Mon Sep 17 00:00:00 2001 From: juanferrer Date: Fri, 12 Jun 2026 20:45:18 +0100 Subject: [PATCH 18/20] Correct broken actions --- .../active-effects/sheets/active-effect-config.js | 13 ++++--------- src/templates/item/parts/AE-config-change.hbs | 10 +++++----- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/module/active-effects/sheets/active-effect-config.js b/src/module/active-effects/sheets/active-effect-config.js index a8cf6339..90b19501 100644 --- a/src/module/active-effects/sheets/active-effect-config.js +++ b/src/module/active-effects/sheets/active-effect-config.js @@ -5,20 +5,15 @@ export class DLActiveEffectConfig extends foundry.applications.sheets.ActiveEffe static DEFAULT_OPTIONS = { window: { - title: "EFFECT.ConfigTitle", - resizable: true + title: 'EFFECT.ConfigTitle', }, - position: { - height: "auto", - width: 612 - }, - classes: ["sheet", "active-effect-sheet"], + classes: ['sheet', 'active-effect-sheet', 'active-effect-config'], }; static PARTS = foundry.utils.mergeObject(super.PARTS ?? {}, { // details: { template: "systems/demonlord/templates/item/parts/AE-config-details.hbs"}, // duration: { template: "systems/demonlord/templates/item/parts/AE-config-duration.hbs"}, - changes: { template: "systems/demonlord/templates/item/parts/AE-config-changes.hbs"} + changes: { template: 'systems/demonlord/templates/item/parts/AE-config-changes.hbs'} }) @@ -35,7 +30,7 @@ export class DLActiveEffectConfig extends foundry.applications.sheets.ActiveEffe isActorEffect: this.document.parent.documentName === 'Actor', isItemEffect: this.document.parent.documentName === 'Item', types: Object.entries(CONST.ACTIVE_EFFECT_CHANGE_TYPES).reduce((obj, e) => { - obj[e[1]] = game.i18n.localize('EFFECT.CHANGES.TYPES.' + e[0]) + obj[e[0]] = game.i18n.localize('EFFECT.CHANGES.TYPES.' + e[0]) return obj }, {}), descriptionHTML: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.description, {secrets: this.document.isOwner}), diff --git a/src/templates/item/parts/AE-config-change.hbs b/src/templates/item/parts/AE-config-change.hbs index 0ed10aa7..2ac4da36 100644 --- a/src/templates/item/parts/AE-config-change.hbs +++ b/src/templates/item/parts/AE-config-change.hbs @@ -1,16 +1,16 @@
  • - {{formInput fields.key name=(concat "changes." index ".key") value=change.key choices=keys}} + {{formInput fields.key name=(concat "system.changes." index ".key") value=change.key choices=keys}}
    - {{formInput fields.type name=(concat "changes." index ".type") value=change.type choices=types}} + {{formInput fields.type name=(concat "system.changes." index ".type") value=change.type choices=types}}
    - {{formInput fields.value name=(concat "changes." index ".value") value=change.value elementType="input"}} + {{formInput fields.value name=(concat "system.changes." index ".value") value=change.value elementType="input"}}
    - +
    - {{formInput fields.priority name=(concat "changes." index ".name") value=change.priority placeholder=0}} + {{formInput fields.priority name=(concat "system.changes." index ".priority") value=change.priority placeholder=0}}
    From b508bd01b96bc0ccaf95fced46aade289d654d3d Mon Sep 17 00:00:00 2001 From: juanferrer Date: Fri, 12 Jun 2026 20:47:31 +0100 Subject: [PATCH 19/20] Bump version number --- src/system.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/system.json b/src/system.json index 16d4f976..089f877c 100644 --- a/src/system.json +++ b/src/system.json @@ -2,7 +2,7 @@ "id": "demonlord", "title": "Shadow of the Demon Lord", "description": "The Shadow of the Demonlord system for Foundry VTT", - "version": "5.2.0", + "version": "6.0.0", "authors": [ { "name": "Xacus" @@ -19,8 +19,8 @@ ], "background": "systems/demonlord/assets/ui/thumbnail.webp", "compatibility": { - "minimum": 13, - "verified": 13 + "minimum": 14, + "verified": 14 }, "esmodules": ["module/demonlord.js"], "styles": ["styles/demonlord.css", "module/bundle.css"], From 6c41562d89aa76e2ad6145fe509afafd18758121 Mon Sep 17 00:00:00 2001 From: juanferrer Date: Fri, 12 Jun 2026 21:15:47 +0100 Subject: [PATCH 20/20] Mark as beta --- src/system.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/system.json b/src/system.json index 089f877c..dd1b0273 100644 --- a/src/system.json +++ b/src/system.json @@ -2,7 +2,7 @@ "id": "demonlord", "title": "Shadow of the Demon Lord", "description": "The Shadow of the Demonlord system for Foundry VTT", - "version": "6.0.0", + "version": "6.0.0-beta", "authors": [ { "name": "Xacus" @@ -146,7 +146,7 @@ "socket": true, "url": "https://github.com/Xacus/demonlord", "manifest": "https://github.com/Xacus/demonlord/releases/latest/download/system.json", - "download": "https://github.com/Xacus/demonlord/releases/download/3.0.0/system.zip", + "download": "https://github.com/Xacus/demonlord/releases/download/6.0.0-beta/system.zip", "bugs": "https://github.com/Xacus/demonlord/issues", "readme": "https://github.com/Xacus/demonlord/blob/master/README.md", "changelog": "https://github.com/Xacus/demonlord/blob/master/CHANGELOG.md",