This commit is contained in:
WBHarry 2026-03-08 15:37:37 +01:00
parent 8f7c7ce818
commit 79b34acf09
8 changed files with 134 additions and 53 deletions

View file

@ -1899,6 +1899,10 @@
"hint": "Multiply any damage dealt to you by this number" "hint": "Multiply any damage dealt to you by this number"
} }
}, },
"Attribute": {
"single": "Attribute",
"plural": "Attributes"
},
"Bonuses": { "Bonuses": {
"rest": { "rest": {
"downtimeAction": "Downtime Action", "downtimeAction": "Downtime Action",
@ -2338,6 +2342,7 @@
"scars": "Scars", "scars": "Scars",
"situationalBonus": "Situational Bonus", "situationalBonus": "Situational Bonus",
"spent": "Spent", "spent": "Spent",
"status": "Status",
"step": "Step", "step": "Step",
"stress": "Stress", "stress": "Stress",
"subclasses": "Subclasses", "subclasses": "Subclasses",

View file

@ -204,6 +204,10 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
id: x.id, id: x.id,
label: x.name label: x.name
})); }));
partContext.conditionalTypes = Object.values(CONFIG.DH.GENERAL.activeEffectConditionalType).map(x => ({
id: x.id,
label: game.i18n.localize(x.label)
}));
break; break;
case 'changes': case 'changes':
const fields = this.document.system.schema.fields.changes.element.fields; 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() { 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 ?? {}); const conditionals = Object.values(submitData.system?.conditionals ?? {});
conditionals.push(this.document.system.schema.fields.conditionals.element.getInitialValue()); conditionals.push(this.document.system.schema.fields.conditionals.element.getInitialValue());
return this.submit({ updateData: { system: { conditionals } } }); return this.submit({ updateData: { system: { conditionals } } });
} }
static async #removeConditional(_event, button) { 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 conditionals = Object.values(submitData.system.conditionals);
const index = Number(button.dataset.index) || 0; const index = Number(button.dataset.index) || 0;
conditionals.splice(index, 1); conditionals.splice(index, 1);

View file

@ -24,7 +24,9 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
actions: { actions: {
editImage: SettingActiveEffectConfig.#editImage, editImage: SettingActiveEffectConfig.#editImage,
addChange: SettingActiveEffectConfig.#addChange, 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' }, tabs: { template: 'templates/generic/tab-navigation.hbs' },
details: { template: 'systems/daggerheart/templates/sheets/activeEffect/details.hbs', scrollable: [''] }, details: { template: 'systems/daggerheart/templates/sheets/activeEffect/details.hbs', scrollable: [''] },
settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/settings.hbs' }, settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/settings.hbs' },
conditionals: { template: 'systems/daggerheart/templates/sheets/activeEffect/conditionals.hbs' },
changes: { changes: {
template: 'systems/daggerheart/templates/sheets/activeEffect/changes.hbs', template: 'systems/daggerheart/templates/sheets/activeEffect/changes.hbs',
templates: ['systems/daggerheart/templates/sheets/activeEffect/change.hbs'],
scrollable: ['ol[data-changes]'] scrollable: ['ol[data-changes]']
}, },
footer: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-form-footer.hbs' } footer: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-form-footer.hbs' }
@ -45,6 +49,11 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
tabs: [ tabs: [
{ id: 'details', icon: 'fa-solid fa-book' }, { id: 'details', icon: 'fa-solid fa-book' },
{ id: 'settings', icon: 'fa-solid fa-bars', label: 'DAGGERHEART.GENERAL.Tabs.settings' }, { 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' } { id: 'changes', icon: 'fa-solid fa-gears' }
], ],
initial: 'details', initial: 'details',
@ -140,6 +149,20 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
]; ];
} }
break; 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': case 'changes':
context.modes = Object.entries(CONST.ACTIVE_EFFECT_MODES).reduce((modes, [key, value]) => { context.modes = Object.entries(CONST.ACTIVE_EFFECT_MODES).reduce((modes, [key, value]) => {
modes[value] = game.i18n.localize(`EFFECT.MODE_${key}`); modes[value] = game.i18n.localize(`EFFECT.MODE_${key}`);
@ -155,6 +178,11 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
static async #onSubmit(_event, _form, formData) { static async #onSubmit(_event, _form, formData) {
this.data = foundry.utils.expandObject(formData.object); 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(); this.close();
} }
@ -183,6 +211,38 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
await fp.browse(); 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. * Add a new change to the effect's changes array.
* @this {ActiveEffectConfig} * @this {ActiveEffectConfig}
@ -213,6 +273,27 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
this.render(); 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 = {}) { static async configure(effect, options = {}) {
return new Promise(resolve => { return new Promise(resolve => {
const app = new this(effect, options); const app = new this(effect, options);

View file

@ -52,46 +52,6 @@
* @extends {foundry.applications.ux.ContextMenu} * @extends {foundry.applications.ux.ContextMenu}
*/ */
export default class DHContextMenu 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?.onClick(event, this.#jQuery ? $(this.target) : this.target);
this.close();
}
/* -------------------------------------------- */
/** /**
* Trigger a context menu event in response to a normal click on a additional options button. * Trigger a context menu event in response to a normal click on a additional options button.
* @param {PointerEvent} event * @param {PointerEvent} event

View file

@ -937,11 +937,11 @@ export const activeEffectConditionalTarget = {
export const activeEffectConditionalType = { export const activeEffectConditionalType = {
attribute: { attribute: {
id: 'attribute', id: 'attribute',
label: 'Attribute' label: 'DAGGERHEART.GENERAL.Attribute.single'
}, },
status: { status: {
id: 'status', id: 'status',
label: 'Status' label: 'DAGGERHEART.GENERAL.status'
} }
}; };

View file

@ -97,8 +97,10 @@ export default class BaseEffect extends foundry.data.ActiveEffectTypeDataModel {
img: 'icons/magic/life/heart-cross-blue.webp', img: 'icons/magic/life/heart-cross-blue.webp',
description: '', description: '',
statuses: [], statuses: [],
changes: [],
system: { system: {
changes: [],
conditionals: [],
duration: { type: '', description: '' },
rangeDependence: { rangeDependence: {
enabled: false, enabled: false,
type: CONFIG.DH.GENERAL.rangeInclusion.withinRange.id, type: CONFIG.DH.GENERAL.rangeInclusion.withinRange.id,

View file

@ -27,6 +27,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
width: 100%;
.conditional-container { .conditional-container {
display: grid; display: grid;
@ -45,7 +46,19 @@
} }
} }
.form-group.not-visible { .form-group {
&.vertical {
flex-direction: column;
gap: 0;
align-items: flex-start;
label {
line-height: 22px;
}
}
&.not-visible {
display: none; display: none;
} }
} }
}

View file

@ -8,9 +8,17 @@
<div class="conditionals-container"> <div class="conditionals-container">
<div class="conditional-container"> <div class="conditional-container">
{{!-- {{formGroup ../systemFields.conditionals.element.fields.target value=conditional.target name=(concat "system.conditionals." index ".target") localize=true }} --}} {{!-- {{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 }} <div class="form-group vertical">
<label>{{localize "DAGGERHEART.GENERAL.type"}}</label>
<div class="form-group status-select {{#unless (eq conditional.type 'status')}}not-visible{{/unless}}"> <div class="form-fields">
<select name="{{concat "system.conditionals." index ".type"}}">
{{selectOptions ../conditionalTypes selected=conditional.type labelAttr="label" valueAttr="id" blank="" localize=true}}
</select>
</div>
</div>
<div class="form-group vertical status-select {{#unless (eq conditional.type 'status')}}not-visible{{/unless}}">
<label>{{localize "EFFECT.FIELDS.changes.element.key.label"}}</label> <label>{{localize "EFFECT.FIELDS.changes.element.key.label"}}</label>
<div class="form-fields"> <div class="form-fields">
@ -20,7 +28,7 @@
</div> </div>
</div> </div>
<div class="form-group attribute-auto {{#unless (eq conditional.type 'attribute')}}not-visible{{/unless}}"> <div class="form-group vertical attribute-auto {{#unless (eq conditional.type 'attribute')}}not-visible{{/unless}}">
<label>{{localize "EFFECT.FIELDS.changes.element.key.label"}}</label> <label>{{localize "EFFECT.FIELDS.changes.element.key.label"}}</label>
<div class="form-fields"> <div class="form-fields">
@ -28,7 +36,7 @@
</div> </div>
</div> </div>
<div class="form-group value {{#unless conditional.usesValue}}not-visible{{/unless}}"> <div class="form-group vertical value {{#unless conditional.usesValue}}not-visible{{/unless}}">
<label>{{localize "EFFECT.FIELDS.changes.element.value.label"}}</label> <label>{{localize "EFFECT.FIELDS.changes.element.value.label"}}</label>
<div class="form-fields"> <div class="form-fields">
@ -36,7 +44,7 @@
</div> </div>
</div> </div>
<div class="form-group comparator {{#unless conditional.usesComparator}}not-visible{{/unless}}"> <div class="form-group vertical comparator {{#unless conditional.usesComparator}}not-visible{{/unless}}">
<label>{{localize "DAGGERHEART.GENERAL.comparator"}}</label> <label>{{localize "DAGGERHEART.GENERAL.comparator"}}</label>
<div class="form-fields"> <div class="form-fields">