Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c0a2ed0
WIP: V14
juanferrer May 8, 2026
5fac6f2
Merge branch 'master' of https://github.com/juanferrer/demonlord into…
juanferrer May 8, 2026
5bc423d
Remove warnings from deprecated CONST.ACTIVE_EFFECT_MODES
juanferrer May 8, 2026
6a4b19e
Hide the default status icons
juanferrer May 8, 2026
c8c1fde
Remove default ActiveEffectConfig sheet
juanferrer May 8, 2026
bd6766c
WIP: Fixing active effects
juanferrer May 8, 2026
dbcb093
Continue fixing active effects sheet
juanferrer May 12, 2026
3efcd16
Fix macros
juanferrer May 18, 2026
f544906
Remove last remaining uses of html.find
juanferrer May 18, 2026
fa12b50
Remove remaining jQuery .on uses
juanferrer May 18, 2026
b124fc8
Remove most remaining uses of jQuery
juanferrer May 18, 2026
a5544a6
Correct issues with info dropdowns
juanferrer May 22, 2026
09845af
WIP: Migrate from measured templates to scene regions
juanferrer May 22, 2026
f178cc5
Complete migration from measured templates to scene regions
juanferrer Jun 5, 2026
cbb6249
Fix active effects for v14
juanferrer Jun 9, 2026
2d5c1c6
Migrate specialDuration to core expiryEvents
juanferrer Jun 11, 2026
df1f8ae
Stop overriding active effect config templates for details and durati…
juanferrer Jun 11, 2026
ed035ac
Finish specialDuration migration to core expiryEvents
juanferrer Jun 12, 2026
df686b2
Merge branch 'v14' of https://github.com/juanferrer/demonlord into v14
juanferrer Jun 12, 2026
5def785
Correct broken actions
juanferrer Jun 12, 2026
b508bd0
Bump version number
juanferrer Jun 12, 2026
05b84f3
Merge branch 'v14'
juanferrer Jun 12, 2026
6c41562
Mark as beta
juanferrer Jun 12, 2026
0e55471
Merge branch 'Xacus:master' into master
juanferrer Jun 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions src/module/active-effects/active-effect.mjs
Original file line number Diff line number Diff line change
@@ -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.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':
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

}
}
}
18 changes: 9 additions & 9 deletions src/module/active-effects/afflictions.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ export class DLAfflictions {
addEffect('system.bonuses.defense.boons.spell', -1, effectPriority),
],
),
)
)

// Unconscious
effectsDataList.push(
Expand Down Expand Up @@ -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' }
),
)

Expand All @@ -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' }
),
)

Expand All @@ -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' }
),
)

Expand All @@ -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 -------------------------- //

Expand Down
12 changes: 6 additions & 6 deletions src/module/active-effects/effects.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down
27 changes: 19 additions & 8 deletions src/module/active-effects/item-effects.js
Original file line number Diff line number Diff line change
@@ -1,61 +1,72 @@
import { DemonlordActor } from '../actor/actor'
import { plusify } from '../utils/utils'
import { i18n } 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)

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 {
Expand Down
51 changes: 12 additions & 39 deletions src/module/active-effects/sheets/active-effect-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,54 +5,43 @@ 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"}
// 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'}
})


/** @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_MODES).reduce((obj, e) => {
obj[e[1]] = game.i18n.localize('EFFECT.MODE_' + e[0])
types: Object.entries(CONST.ACTIVE_EFFECT_CHANGE_TYPES).reduce((obj, e) => {
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}),
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
}
Expand All @@ -63,22 +52,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 = {
// <key> : <name>
Expand Down
Loading
Loading