diff --git a/daggerheart.mjs b/daggerheart.mjs index 56ad3e3d..0d9d5ae1 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -235,3 +235,48 @@ Hooks.on('renderJournalDirectory', async (tab, html, _, options) => { }; } }); + +Hooks.on('moveToken', async (movedToken, data) => { + const effectsAutomation = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).effects; + if (!effectsAutomation.rangeDependent) return; + + const rangeDependantEffects = movedToken.actor.effects.filter(effect => effect.system.rangeDependence.enabled); + + const updateEffects = async (disposition, token, effects, effectUpdates) => { + const rangeMeasurement = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement); + + for (let effect of effects.filter(x => x.system.rangeDependence.enabled)) { + const { target, range, type } = effect.system.rangeDependence; + if ((target === 'friendly' && disposition !== 1) || (target === 'hostile' && disposition !== -1)) + return false; + + const distanceBetween = canvas.grid.measurePath([ + { ...movedToken.toObject(), x: data.destination.x, y: data.destination.y }, + token + ]).distance; + const distance = rangeMeasurement[range]; + + const reverse = type === CONFIG.DH.GENERAL.rangeInclusion.outsideRange.id; + const newDisabled = reverse ? distanceBetween <= distance : distanceBetween > distance; + const oldDisabled = effectUpdates[effect.uuid] ? effectUpdates[effect.uuid].disabled : newDisabled; + effectUpdates[effect.uuid] = { + disabled: oldDisabled || newDisabled, + value: effect + }; + } + }; + + const effectUpdates = {}; + for (let token of game.scenes.find(x => x.active).tokens) { + if (token.id !== movedToken.id) { + await updateEffects(token.disposition, token, rangeDependantEffects, effectUpdates); + } + + if (token.actor) await updateEffects(movedToken.disposition, token, token.actor.effects, effectUpdates); + } + + for (let key in effectUpdates) { + const effect = effectUpdates[key]; + await effect.value.update({ disabled: effect.disabled }); + } +}); diff --git a/lang/en.json b/lang/en.json index 244b25db..efe04425 100755 --- a/lang/en.json +++ b/lang/en.json @@ -88,6 +88,17 @@ } } }, + "ACTIVEEFFECT": { + "Config": { + "rangeDependence": { + "title": "Range Dependence" + } + }, + "RangeDependance": { + "hint": "Settings for an optional distance at which this effect should activate", + "title": "Range Dependant" + } + }, "ACTORS": { "Adversary": { "FIELDS": { @@ -758,6 +769,10 @@ "oneHanded": "One-Handed", "twoHanded": "Two-Handed" }, + "RangeInclusion": { + "withinRange": "Within Range", + "outsideRange": "Outside Range" + }, "Condition": { "dead": { "name": "Dead", @@ -2005,6 +2020,12 @@ "hordeDamage": { "label": "Automatic Horde Damage", "hint": "Automatically active horde effect to lower damage when reaching half or lower HP." + }, + "effects": { + "rangeDependent": { + "label": "Effect Range Dependent", + "hint": "Effects with defined range dependency will automatically turn on/off depending on range" + } } } }, diff --git a/module/applications/sheets-configs/activeEffectConfig.mjs b/module/applications/sheets-configs/activeEffectConfig.mjs index 88edf9d6..25f7d2b5 100644 --- a/module/applications/sheets-configs/activeEffectConfig.mjs +++ b/module/applications/sheets-configs/activeEffectConfig.mjs @@ -27,7 +27,7 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac header: { template: 'systems/daggerheart/templates/sheets/activeEffect/header.hbs' }, tabs: { template: 'templates/generic/tab-navigation.hbs' }, details: { template: 'systems/daggerheart/templates/sheets/activeEffect/details.hbs', scrollable: [''] }, - duration: { template: 'systems/daggerheart/templates/sheets/activeEffect/duration.hbs' }, + settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/settings.hbs' }, changes: { template: 'systems/daggerheart/templates/sheets/activeEffect/changes.hbs', scrollable: ['ol[data-changes]'] @@ -39,7 +39,7 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac sheet: { tabs: [ { id: 'details', icon: 'fa-solid fa-book' }, - { id: 'duration', icon: 'fa-solid fa-clock' }, + { id: 'settings', icon: 'fa-solid fa-bars', label: 'DAGGERHEART.GENERAL.Tabs.settings' }, { id: 'changes', icon: 'fa-solid fa-gears' } ], initial: 'details', diff --git a/module/config/actionConfig.mjs b/module/config/actionConfig.mjs index 1f1ebf8b..3c669a7b 100644 --- a/module/config/actionConfig.mjs +++ b/module/config/actionConfig.mjs @@ -43,25 +43,6 @@ export const actionTypes = { } }; -export const targetTypes = { - self: { - id: 'self', - label: 'Self' - }, - friendly: { - id: 'friendly', - label: 'Friendly' - }, - hostile: { - id: 'hostile', - label: 'Hostile' - }, - any: { - id: 'any', - label: 'Any' - } -}; - export const damageOnSave = { none: { id: 'none', diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 81d4309f..361adcea 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -43,6 +43,40 @@ export const range = { } }; +export const rangeInclusion = { + withinRange: { + id: 'withinRange', + label: 'DAGGERHEART.CONFIG.RangeInclusion.withinRange' + }, + outsideRange: { + id: 'outsideRange', + label: 'DAGGERHEART.CONFIG.RangeInclusion.outsideRange' + } +}; + +export const otherTargetTypes = { + friendly: { + id: 'friendly', + label: 'Friendly' + }, + hostile: { + id: 'hostile', + label: 'Hostile' + }, + any: { + id: 'any', + label: 'Any' + } +}; + +export const targetTypes = { + self: { + id: 'self', + label: 'Self' + }, + ...otherTargetTypes +}; + export const burden = { oneHanded: { value: 'oneHanded', diff --git a/module/data/activeEffect/_module.mjs b/module/data/activeEffect/_module.mjs index 126aec5e..79ad7813 100644 --- a/module/data/activeEffect/_module.mjs +++ b/module/data/activeEffect/_module.mjs @@ -1,7 +1,9 @@ +import BaseEffect from './baseEffect.mjs'; import BeastformEffect from './beastformEffect.mjs'; -export { BeastformEffect }; +export { BaseEffect, BeastformEffect }; export const config = { + base: BaseEffect, beastform: BeastformEffect }; diff --git a/module/data/activeEffect/baseEffect.mjs b/module/data/activeEffect/baseEffect.mjs new file mode 100644 index 00000000..0ac87de0 --- /dev/null +++ b/module/data/activeEffect/baseEffect.mjs @@ -0,0 +1,33 @@ +export default class BaseEffect extends foundry.abstract.TypeDataModel { + static defineSchema() { + const fields = foundry.data.fields; + + return { + rangeDependence: new fields.SchemaField({ + enabled: new fields.BooleanField({ + required: true, + initial: false, + label: 'DAGGERHEART.GENERAL.enabled' + }), + type: new fields.StringField({ + required: true, + choices: CONFIG.DH.GENERAL.rangeInclusion, + initial: CONFIG.DH.GENERAL.rangeInclusion.withinRange.id, + label: 'DAGGERHEART.GENERAL.type' + }), + target: new fields.StringField({ + required: true, + choices: CONFIG.DH.GENERAL.otherTargetTypes, + initial: CONFIG.DH.GENERAL.otherTargetTypes.hostile.id, + label: 'DAGGERHEART.GENERAL.Target.single' + }), + range: new fields.StringField({ + required: true, + choices: CONFIG.DH.GENERAL.range, + initial: CONFIG.DH.GENERAL.range.melee.id, + label: 'DAGGERHEART.GENERAL.range' + }) + }) + }; + } +} diff --git a/module/data/activeEffect/beastformEffect.mjs b/module/data/activeEffect/beastformEffect.mjs index 91b84614..e040d8d8 100644 --- a/module/data/activeEffect/beastformEffect.mjs +++ b/module/data/activeEffect/beastformEffect.mjs @@ -1,6 +1,7 @@ import { updateActorTokens } from '../../helpers/utils.mjs'; +import BaseEffect from './baseEffect.mjs'; -export default class BeastformEffect extends foundry.abstract.TypeDataModel { +export default class BeastformEffect extends BaseEffect { static defineSchema() { const fields = foundry.data.fields; return { diff --git a/module/data/fields/action/targetField.mjs b/module/data/fields/action/targetField.mjs index 1024d5d7..f54cd6fe 100644 --- a/module/data/fields/action/targetField.mjs +++ b/module/data/fields/action/targetField.mjs @@ -4,8 +4,8 @@ export default class TargetField extends fields.SchemaField { constructor(options = {}, context = {}) { const targetFields = { type: new fields.StringField({ - choices: CONFIG.DH.ACTIONS.targetTypes, - initial: CONFIG.DH.ACTIONS.targetTypes.any.id, + choices: CONFIG.DH.GENERAL.targetTypes, + initial: CONFIG.DH.GENERAL.targetTypes.any.id, nullable: true }), amount: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }) @@ -16,11 +16,11 @@ export default class TargetField extends fields.SchemaField { static prepareConfig(config) { if (!this.target?.type) return []; let targets; - if (this.target?.type === CONFIG.DH.ACTIONS.targetTypes.self.id) + if (this.target?.type === CONFIG.DH.GENERAL.targetTypes.self.id) targets = [this.actor.token ?? this.actor.prototypeToken]; else { targets = Array.from(game.user.targets); - if (this.target.type !== CONFIG.DH.ACTIONS.targetTypes.any.id) { + if (this.target.type !== CONFIG.DH.GENERAL.targetTypes.any.id) { targets = targets.filter(t => TargetField.isTargetFriendly.call(this, t)); if (this.target.amount && targets.length > this.target.amount) targets = []; } @@ -43,9 +43,9 @@ export default class TargetField extends fields.SchemaField { : this.actor.prototypeToken.disposition, targetDisposition = target.document.disposition; return ( - (this.target.type === CONFIG.DH.ACTIONS.targetTypes.friendly.id && + (this.target.type === CONFIG.DH.GENERAL.targetTypes.friendly.id && actorDisposition === targetDisposition) || - (this.target.type === CONFIG.DH.ACTIONS.targetTypes.hostile.id && + (this.target.type === CONFIG.DH.GENERAL.targetTypes.hostile.id && actorDisposition + targetDisposition === 0) ); } diff --git a/module/data/settings/Automation.mjs b/module/data/settings/Automation.mjs index 63377b26..d4d6a2a7 100644 --- a/module/data/settings/Automation.mjs +++ b/module/data/settings/Automation.mjs @@ -23,6 +23,12 @@ export default class DhAutomation extends foundry.abstract.DataModel { required: true, initial: true, label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.hordeDamage.label' + }), + effects: new fields.SchemaField({ + rangeDependent: new fields.BooleanField({ + initial: true, + label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.effects.rangeDependent.label' + }) }) }; } diff --git a/templates/settings/automation-settings.hbs b/templates/settings/automation-settings.hbs index 9ffe5049..a02d6f97 100644 --- a/templates/settings/automation-settings.hbs +++ b/templates/settings/automation-settings.hbs @@ -10,6 +10,7 @@ {{formGroup settingFields.schema.fields.actionPoints value=settingFields._source.actionPoints localize=true}} {{formGroup settingFields.schema.fields.hordeDamage value=settingFields._source.hordeDamage localize=true}} + {{formGroup settingFields.schema.fields.effects.fields.rangeDependent value=settingFields._source.effects.rangeDependent localize=true}}