diff --git a/lang/en.json b/lang/en.json index 9553ab9f..3be24841 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1899,6 +1899,10 @@ "hint": "Multiply any damage dealt to you by this number" } }, + "Attribute": { + "single": "Attribute", + "plural": "Attributes" + }, "Bonuses": { "rest": { "downtimeAction": "Downtime Action", @@ -2338,6 +2342,7 @@ "scars": "Scars", "situationalBonus": "Situational Bonus", "spent": "Spent", + "status": "Status", "step": "Step", "stress": "Stress", "subclasses": "Subclasses", diff --git a/module/applications/sheets-configs/activeEffectConfig.mjs b/module/applications/sheets-configs/activeEffectConfig.mjs index 062883b6..d1ba85e0 100644 --- a/module/applications/sheets-configs/activeEffectConfig.mjs +++ b/module/applications/sheets-configs/activeEffectConfig.mjs @@ -204,6 +204,10 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac id: x.id, label: x.name })); + partContext.conditionalTypes = Object.values(CONFIG.DH.GENERAL.activeEffectConditionalType).map(x => ({ + id: x.id, + label: game.i18n.localize(x.label) + })); break; case 'changes': const fields = this.document.system.schema.fields.changes.element.fields; @@ -247,14 +251,22 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac } static #addConditional() { - const submitData = this._processFormData(null, this.form, new FormDataExtended(this.form)); + const submitData = this._processFormData( + null, + this.form, + new foundry.applications.ux.FormDataExtended(this.form) + ); const conditionals = Object.values(submitData.system?.conditionals ?? {}); conditionals.push(this.document.system.schema.fields.conditionals.element.getInitialValue()); return this.submit({ updateData: { system: { conditionals } } }); } static async #removeConditional(_event, button) { - const submitData = this._processFormData(null, this.form, new FormDataExtended(this.form)); + const submitData = this._processFormData( + null, + this.form, + new foundry.applications.ux.FormDataExtended(this.form) + ); const conditionals = Object.values(submitData.system.conditionals); const index = Number(button.dataset.index) || 0; conditionals.splice(index, 1); diff --git a/module/applications/sheets-configs/setting-active-effect-config.mjs b/module/applications/sheets-configs/setting-active-effect-config.mjs index 12ac90d1..bc60cd52 100644 --- a/module/applications/sheets-configs/setting-active-effect-config.mjs +++ b/module/applications/sheets-configs/setting-active-effect-config.mjs @@ -24,7 +24,9 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi actions: { editImage: SettingActiveEffectConfig.#editImage, addChange: SettingActiveEffectConfig.#addChange, - deleteChange: SettingActiveEffectConfig.#deleteChange + deleteChange: SettingActiveEffectConfig.#deleteChange, + addConditional: SettingActiveEffectConfig.#addConditional, + removeConditional: SettingActiveEffectConfig.#removeConditional } }; @@ -33,8 +35,10 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi tabs: { template: 'templates/generic/tab-navigation.hbs' }, details: { template: 'systems/daggerheart/templates/sheets/activeEffect/details.hbs', scrollable: [''] }, settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/settings.hbs' }, + conditionals: { template: 'systems/daggerheart/templates/sheets/activeEffect/conditionals.hbs' }, changes: { template: 'systems/daggerheart/templates/sheets/activeEffect/changes.hbs', + templates: ['systems/daggerheart/templates/sheets/activeEffect/change.hbs'], scrollable: ['ol[data-changes]'] }, footer: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-form-footer.hbs' } @@ -45,6 +49,11 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi tabs: [ { id: 'details', icon: 'fa-solid fa-book' }, { id: 'settings', icon: 'fa-solid fa-bars', label: 'DAGGERHEART.GENERAL.Tabs.settings' }, + { + id: 'conditionals', + icon: 'fa-solid fa-person-circle-question', + label: 'DAGGERHEART.GENERAL.Tabs.conditionals' + }, { id: 'changes', icon: 'fa-solid fa-gears' } ], initial: 'details', @@ -140,6 +149,20 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi ]; } break; + case 'conditionals': + context.conditionals = this.effect.system.conditionals.map(conditional => ({ + ...conditional, + ...game.system.api.data.activeEffects.EffectConditional.getConditionalFieldUseage(conditional.type) + })); + context.statusChoices = Object.values(CONFIG.statusEffects).map(x => ({ + id: x.id, + label: x.name + })); + context.conditionalTypes = Object.values(CONFIG.DH.GENERAL.activeEffectConditionalType).map(x => ({ + id: x.id, + label: game.i18n.localize(x.label) + })); + break; case 'changes': context.modes = Object.entries(CONST.ACTIVE_EFFECT_MODES).reduce((modes, [key, value]) => { modes[value] = game.i18n.localize(`EFFECT.MODE_${key}`); @@ -155,6 +178,11 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi static async #onSubmit(_event, _form, formData) { this.data = foundry.utils.expandObject(formData.object); + this.data.system.conditionals = Object.values(this.data.system.conditionals).map(x => ({ + ...x, + key: x.key.find(key => key) ?? '' + })); + this.close(); } @@ -183,6 +211,38 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi await fp.browse(); } + /** @inheritDoc */ + _onChangeForm(_formConfig, event) { + if ( + foundry.utils.isElementInstanceOf(event.target, 'select') && + event.target.name.startsWith('system.conditionals') && + event.target.name.endsWith('type') + ) { + const container = event.target.closest('.conditional-container'); + + const statusSelect = container.querySelector('.form-group.status-select'); + const attributeAuto = container.querySelector('.form-group.attribute-auto'); + if (event.target.value === CONFIG.DH.GENERAL.activeEffectConditionalType.status.id) { + statusSelect.classList.remove('not-visible'); + attributeAuto.classList.add('not-visible'); + } else { + statusSelect.classList.add('not-visible'); + attributeAuto.classList.remove('not-visible'); + } + statusSelect.querySelector('select').selectedIndex = '-1'; + attributeAuto.querySelector('input').value = ''; + + const { usesValue, usesComparator } = + game.system.api.data.activeEffects.EffectConditional.getConditionalFieldUseage(event.target.value); + + if (usesValue) container.querySelector('.form-group.value').classList.remove('not-visible'); + else container.querySelector('.form-group.value').classList.add('not-visible'); + + if (usesComparator) container.querySelector('.form-group.comparator').classList.remove('not-visible'); + else container.querySelector('.form-group.comparator').classList.add('not-visible'); + } + } + /** * Add a new change to the effect's changes array. * @this {ActiveEffectConfig} @@ -213,6 +273,27 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi this.render(); } + static #addConditional() { + const formData = foundry.utils.expandObject(new FormDataExtended(this.form).object); + const updatedConditionals = Object.values(formData.system.conditionals ?? {}); + updatedConditionals.push( + game.system.api.data.activeEffects.BaseEffect._schema.fields.conditionals.element.getInitialValue() + ); + + this.effect = { ...formData, system: { ...formData.system, conditionals: updatedConditionals } }; + this.render(); + } + + static async #removeConditional(_event, button) { + const submitData = foundry.utils.expandObject(new FormDataExtended(this.form).object); + const conditionals = Object.values(submitData.system.conditionals); + const index = Number(button.dataset.index) || 0; + conditionals.splice(index, 1); + + this.effect = { ...submitData, system: { ...submitData.system, conditionals } }; + this.render(); + } + static async configure(effect, options = {}) { return new Promise(resolve => { const app = new this(effect, options); diff --git a/module/applications/ux/contextMenu.mjs b/module/applications/ux/contextMenu.mjs index 081e6ba0..9a308667 100644 --- a/module/applications/ux/contextMenu.mjs +++ b/module/applications/ux/contextMenu.mjs @@ -52,46 +52,6 @@ * @extends {foundry.applications.ux.ContextMenu} */ export default class DHContextMenu extends foundry.applications.ux.ContextMenu { - /** - * @param {HTMLElement|jQuery} container - The HTML element that contains the context menu targets. - * @param {string} selector - A CSS selector which activates the context menu. - * @param {ContextMenuEntry[]} menuItems - An Array of entries to display in the menu - * @param {ContextMenuOptions} [options] - Additional options to configure the context menu. - */ - constructor(container, selector, menuItems, options) { - super(container, selector, menuItems, options); - - /** @deprecated since v13 until v15 */ - this.#jQuery = options.jQuery; - } - - /** - * Whether to pass jQuery objects or HTMLElement instances to callback. - * @type {boolean} - */ - #jQuery; - - /**@inheritdoc */ - activateListeners(menu) { - menu.addEventListener('click', this.#onClickItem.bind(this)); - } - - /** - * Handle click events on context menu items. - * @param {PointerEvent} event The click event - */ - #onClickItem(event) { - event.preventDefault(); - event.stopPropagation(); - const element = event.target.closest('.context-item'); - if (!element) return; - const item = this.menuItems.find(i => i.element === element); - item?.callback(this.#jQuery ? $(this.target) : this.target, event); - this.close(); - } - - /* -------------------------------------------- */ - /** * Trigger a context menu event in response to a normal click on a additional options button. * @param {PointerEvent} event diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index da7b96ac..5da380c3 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -870,6 +870,11 @@ export const activeEffectModes = { priority: 20, label: 'EFFECT.CHANGES.TYPES.add' }, + subtract: { + id: 'subtract', + priority: 20, + label: 'EFFECT.CHANGES.TYPES.subtract' + }, downgrade: { id: 'downgrade', priority: 30, @@ -932,11 +937,11 @@ export const activeEffectConditionalTarget = { export const activeEffectConditionalType = { attribute: { id: 'attribute', - label: 'Attribute' + label: 'DAGGERHEART.GENERAL.Attribute.single' }, status: { id: 'status', - label: 'Status' + label: 'DAGGERHEART.GENERAL.status' } }; diff --git a/module/data/activeEffect/baseEffect.mjs b/module/data/activeEffect/baseEffect.mjs index d45ba15a..14633ded 100644 --- a/module/data/activeEffect/baseEffect.mjs +++ b/module/data/activeEffect/baseEffect.mjs @@ -97,8 +97,10 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel { img: 'icons/magic/life/heart-cross-blue.webp', description: '', statuses: [], - changes: [], system: { + changes: [], + conditionals: [], + duration: { type: '', description: '' }, rangeDependence: { enabled: false, type: CONFIG.DH.GENERAL.rangeInclusion.withinRange.id, diff --git a/module/data/settings/Homebrew.mjs b/module/data/settings/Homebrew.mjs index b8804fa7..618413b0 100644 --- a/module/data/settings/Homebrew.mjs +++ b/module/data/settings/Homebrew.mjs @@ -183,6 +183,24 @@ export default class DhHomebrew extends foundry.abstract.DataModel { const initial = this.schema.fields.currency.fields[type].getInitialValue(); source.currency[type] = foundry.utils.mergeObject(initial, source.currency[type], { inplace: false }); } + + /* Migrate to v14 setup */ + const migrateEffects = features => { + for (const featureKey of Object.keys(features)) { + const feature = features[featureKey]; + for (const effect of feature.effects) { + if (effect.changes) effect.system.changes = effect.changes; + if (!effect.system.changes) effect.system.changes = []; + if (!effect.system.conditionals) effect.system.conditionals = []; + } + } + }; + + migrateEffects(source.restMoves.longRest.moves); + migrateEffects(source.restMoves.shortRest.moves); + migrateEffects(source.itemFeatures.weaponFeatures); + migrateEffects(source.itemFeatures.armorFeatures); + return source; } } diff --git a/styles/less/sheets/activeEffects/activeEffects.less b/styles/less/sheets/activeEffects/activeEffects.less index 42f79456..c6e00245 100644 --- a/styles/less/sheets/activeEffects/activeEffects.less +++ b/styles/less/sheets/activeEffects/activeEffects.less @@ -27,6 +27,7 @@ display: flex; flex-direction: column; gap: 8px; + width: 100%; .conditional-container { display: grid; @@ -45,7 +46,19 @@ } } - .form-group.not-visible { - display: none; + .form-group { + &.vertical { + flex-direction: column; + gap: 0; + align-items: flex-start; + + label { + line-height: 22px; + } + } + + &.not-visible { + display: none; + } } } diff --git a/templates/sheets/activeEffect/conditionals.hbs b/templates/sheets/activeEffect/conditionals.hbs index 0265eaae..cf36e13b 100644 --- a/templates/sheets/activeEffect/conditionals.hbs +++ b/templates/sheets/activeEffect/conditionals.hbs @@ -8,9 +8,17 @@
{{!-- {{formGroup ../systemFields.conditionals.element.fields.target value=conditional.target name=(concat "system.conditionals." index ".target") localize=true }} --}} - {{formGroup ../systemFields.conditionals.element.fields.type value=conditional.type name=(concat "system.conditionals." index ".type") localize=true }} +
+ -
+
+ +
+
+ +
@@ -20,7 +28,7 @@
-
+
@@ -28,7 +36,7 @@
-
+
@@ -36,7 +44,7 @@
-
+