From 4cb427df8faf455a344cb8cd2262947b6241c3ba Mon Sep 17 00:00:00 2001 From: juanferrer Date: Thu, 19 Mar 2026 20:37:07 +0000 Subject: [PATCH 1/4] Prevent combatants from being duplicated multiple times when using fastAndSlowTurns --- src/module/combat/combat-tracker.js | 3 ++- src/module/combat/combatant.js | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/module/combat/combat-tracker.js b/src/module/combat/combat-tracker.js index 06a0957b..2c6da49a 100644 --- a/src/module/combat/combat-tracker.js +++ b/src/module/combat/combat-tracker.js @@ -229,8 +229,9 @@ calculateEncounterDifficulty(combatants) { for (const combatant of combatants) { if (combatant.actor?.system.fastAndSlowTurn) { const existingDouble = game.combat.getCombatantsByToken(combatant.token).length > 1 - if (!existingDouble) { + if (!existingDouble && combatant.getFlag('demonlord', 'isDuplicated')) { // Add a second combatant with initiative + 50 (see DLCombat.getInitiativeValue) + await combatant.setFlag('demonlord', 'isDuplicated', true) const doubleCombatant = (await this.viewed.createEmbeddedDocuments('Combatant', [combatant]))[0] await doubleCombatant.update({ initiative: doubleCombatant.initiative + 50 }) } diff --git a/src/module/combat/combatant.js b/src/module/combat/combatant.js index d027e14b..088151df 100644 --- a/src/module/combat/combatant.js +++ b/src/module/combat/combatant.js @@ -1,15 +1,16 @@ export class DLCombatant extends foundry.documents.Combatant { - _onDelete(options, userId) { - super._onDelete(options, userId) - + async _onDelete(options, userId) { + // If the combatant is deleted, we also delete its double if it exists if (this.actor.system.fastAndSlowTurn) { const doubleCombatants = game.combat.getCombatantsByToken(this.token).filter(c => c.id !== this.id) for (const double of doubleCombatants) { if (double) { - double.delete() + await double.delete() } } } + + await super._onDelete(options, userId) } } From fba7ccbcf3f662fb81e0ae9bf0f6eace8482af2c Mon Sep 17 00:00:00 2001 From: juanferrer Date: Thu, 26 Mar 2026 22:27:15 +0000 Subject: [PATCH 2/4] Fix insanity setting for some actors --- src/module/actor/actor.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/module/actor/actor.js b/src/module/actor/actor.js index c667663d..e9e24624 100644 --- a/src/module/actor/actor.js +++ b/src/module/actor/actor.js @@ -201,6 +201,14 @@ export class DemonlordActor extends Actor { // And then round down system.characteristics.health.healingrate = Math.floor(system.characteristics.health.healingrate) // Insanity + if (!system.characteristics.insanity) { + system.characteristics.insanity = { + min: 0, + max: 0, + value: 0, + immune: 0 + } + } system.characteristics.insanity.max += system.attributes.will.value // Final armor computation From c5cff6d786cdf9ccad86836504fcdbc3be91405e Mon Sep 17 00:00:00 2001 From: juanferrer Date: Thu, 26 Mar 2026 22:29:24 +0000 Subject: [PATCH 3/4] Fix active effects from items applying incorrectly --- src/module/actor/actor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/module/actor/actor.js b/src/module/actor/actor.js index e9e24624..11604605 100644 --- a/src/module/actor/actor.js +++ b/src/module/actor/actor.js @@ -333,7 +333,7 @@ export class DemonlordActor extends Actor { // Also, if any of the effects in the document contains an affliction, activate it if (['character', 'creature'].includes(this.type)) { - const effects = doc.effects.filter(e => e.changes.some(c => c.key === 'system.maluses.affliction')) + const effects = doc.effects.filter(e => e.transfer && e.changes.some(c => c.key === 'system.maluses.affliction')) for (const activeEffect of effects) { const changes = activeEffect.changes for (const affliction of changes.filter(c => c.key === 'system.maluses.affliction')) { @@ -406,7 +406,7 @@ export class DemonlordActor extends Actor { // 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')))) { - const effects = doc.effects.filter(e => e.changes?.some(c => c.key === 'system.maluses.affliction')) + const effects = doc.effects.filter(e => e.transfer && e.changes?.some(c => c.key === 'system.maluses.affliction')) for (const activeEffect of effects) { const changes = activeEffect.changes for (const affliction of changes.filter(c => c.key === 'system.maluses.affliction')) { From d082137835a13c6e9ace7824a897133d208f887f Mon Sep 17 00:00:00 2001 From: juanferrer Date: Thu, 9 Apr 2026 21:21:49 +0100 Subject: [PATCH 4/4] Convert hasActed into a combatant flag --- src/module/combat/combat-tracker.js | 78 ++++++++++++++++------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/src/module/combat/combat-tracker.js b/src/module/combat/combat-tracker.js index 29b131c3..c556e763 100644 --- a/src/module/combat/combat-tracker.js +++ b/src/module/combat/combat-tracker.js @@ -229,9 +229,8 @@ calculateEncounterDifficulty(combatants) { for (const combatant of combatants) { if (combatant.actor?.system.fastAndSlowTurn) { const existingDouble = game.combat.getCombatantsByToken(combatant.token).length > 1 - if (!existingDouble && combatant.getFlag('demonlord', 'isDuplicated')) { + if (!existingDouble) { // Add a second combatant with initiative + 50 (see DLCombat.getInitiativeValue) - await combatant.setFlag('demonlord', 'isDuplicated', true) const doubleCombatant = (await this.viewed.createEmbeddedDocuments('Combatant', [combatant]))[0] await doubleCombatant.update({ initiative: doubleCombatant.initiative + 50 }) } @@ -239,40 +238,31 @@ calculateEncounterDifficulty(combatants) { } } - const trackerHeader = html.querySelector(".combat-tracker-header") - if (this.initiativeMethod === 's') { - if (game.combat?.turn === null) - trackerHeader.innerHTML = trackerHeader.innerHTML + `
${game.i18n.localize('DL.TurnChooseTurn')}
` - else - trackerHeader.innerHTML = trackerHeader.innerHTML + `
 
` - } - html.querySelectorAll('.combatant')?.forEach(el => { // For each combatant in the tracker, change the initiative selector const combId = el.getAttribute('data-combatant-id') const combatant = combatants.get(combId) if (!combatant) return + if (!game.modules.get('lancer-initiative')) { + const multipleCombatants = game.combat.getCombatantsByToken(combatant.token) + + if (combatant.actor?.system.fastAndSlowTurn && multipleCombatants.length == 2) { + // The combatant has a double initiative, so we display "Fast" and "Slow" + init = combatant._id === multipleCombatants[0]._id + ? game.i18n.localize('DL.TurnSlow') + : game.i18n.localize('DL.TurnFast') + // Display only + if (this.initiativeMethod === 's') el.getElementsByClassName('token-initiative')[0].innerHTML = `${init}` + } else { + init = combatant.actor?.system.fastturn + ? game.i18n.localize('DL.TurnFast') + : game.i18n.localize('DL.TurnSlow') - const multipleCombatants = game.combat.getCombatantsByToken(combatant.token) - const title = (game.user.isGM || combatant.actor.isOwner) && game.combat?.turn === null ? i18n('DL.TurnChangeTurn') : '' - const style = (game.user.isGM || combatant.actor.isOwner) && game.combat?.turn === null ? 'font-weight: bold; cursor: pointer;' : 'font-weight: normal; cursor: auto; opacity: 0.5;' - - if (combatant.actor?.system.fastAndSlowTurn && multipleCombatants.length == 2) { - // The combatant has a double initiative, so we display "Fast" and "Slow" - init = combatant._id === multipleCombatants[0]._id - ? game.i18n.localize('DL.TurnSlow') - : game.i18n.localize('DL.TurnFast') - // Display only - if (this.initiativeMethod === 's') el.getElementsByClassName('token-initiative')[0].innerHTML = `${init}` - } else { - init = combatant.actor?.system.fastturn - ? game.i18n.localize('DL.TurnFast') - : game.i18n.localize('DL.TurnSlow') - - // Change initiative by clicking on the name - if (this.initiativeMethod === 's') el.getElementsByClassName('token-initiative')[0].innerHTML = - `${init}` + // Change initiative by clicking on the name + if (this.initiativeMethod === 's') el.getElementsByClassName('token-initiative')[0].innerHTML = + `${init}` + } } if (this.initiativeMethod === 'h' && game.user.isGM) @@ -325,7 +315,18 @@ calculateEncounterDifficulty(combatants) { if (game.user.isGM) { const hasActedButton = document.createElement('button') hasActedButton.type = 'button' - hasActedButton.className = 'inline-control combatant-control icon fa-solid fa-hourglass-start' + hasActedButton.className = 'inline-control combatant-control icon fa-solid' + + const hasActed = combatant.getFlag('demonlord', 'hasActed') + + if (hasActed) { + hasActedButton.classList.add('fa-hourglass-end') + el.classList.add('hide') + } else { + hasActedButton.classList.add('fa-hourglass-start') + el.classList.remove('hide') + } + hasActedButton.setAttribute('data-action', 'toggleActed') hasActedButton.setAttribute('data-tooltip', '') hasActedButton.setAttribute('aria-label', game.i18n.localize('DL.ToggleActed')) @@ -357,7 +358,8 @@ calculateEncounterDifficulty(combatants) { const combId = li.dataset.combatantId const combatant = combatants.get(combId) if (!combatant) return - if (game.user.isGM || (combatant.actor.isOwner && game.combat?.turn === null)) { + + if (game.user.isGM || combatant.actor.isOwner) { await combatant.actor.update({'system.fastturn': !combatant.actor.system.fastturn}) const initChatMessage = await createInitChatMessage(combatant, {}) if (initChatMessage) ChatMessage.create(initChatMessage) @@ -406,12 +408,16 @@ calculateEncounterDifficulty(combatants) { } static async onToggleActed(event) { - const button = event.target.closest('.inline-control') - const combatant = event.target.closest('.combatant') + const buttonElement = event.target.closest('.inline-control') + const combatantElement = event.target.closest('.combatant') + + buttonElement.classList.toggle('fa-hourglass-start') + buttonElement.classList.toggle('fa-hourglass-end') + combatantElement.classList.toggle('hide') + + const combatant = game.combat.combatants.get(combatantElement.dataset.combatantId) - button.classList.toggle('fa-hourglass-start') - button.classList.toggle('fa-hourglass-end') - combatant.classList.toggle('hide') + await combatant.setFlag('demonlord', 'hasActed', buttonElement.classList.contains('fa-hourglass-end')) } async _onToggleHidden(combatant) {