Merge branch 'main' into feature/damage-iterrable-rework

This commit is contained in:
WBHarry 2026-03-06 11:16:17 +01:00
commit f004f575a5
27 changed files with 225 additions and 167 deletions

View file

@ -187,6 +187,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
});
}
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
this.render();
}
@ -227,6 +228,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
}
});
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
this.render();
}
@ -246,6 +248,8 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
await this.settings.updateSource({
[`${path}.-=${id}`]: null
});
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
this.render();
}

View file

@ -4,6 +4,42 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
constructor(options) {
super(options);
this.changeChoices = DhActiveEffectConfig.getChangeChoices();
}
static DEFAULT_OPTIONS = {
classes: ['daggerheart', 'sheet', 'dh-style']
};
static PARTS = {
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: [''] },
settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/settings.hbs' },
changes: {
template: 'systems/daggerheart/templates/sheets/activeEffect/changes.hbs',
scrollable: ['ol[data-changes]']
},
footer: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-form-footer.hbs' }
};
static TABS = {
sheet: {
tabs: [
{ id: 'details', icon: 'fa-solid fa-book' },
{ id: 'settings', icon: 'fa-solid fa-bars', label: 'DAGGERHEART.GENERAL.Tabs.settings' },
{ id: 'changes', icon: 'fa-solid fa-gears' }
],
initial: 'details',
labelPrefix: 'EFFECT.TABS'
}
};
/**
* Get ChangeChoices for the changes autocomplete. Static for use in this class aswell as in settings-active-effect-config.mjs
* @returns {ChangeChoice { value: string, label: string, hint: string, group: string }[]}
*/
static getChangeChoices() {
const ignoredActorKeys = ['config', 'DhEnvironment', 'DhParty'];
const getAllLeaves = (root, group, parentPath = '') => {
@ -23,7 +59,7 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
return leaves;
};
this.changeChoices = Object.keys(game.system.api.models.actors).reduce((acc, key) => {
return Object.keys(game.system.api.models.actors).reduce((acc, key) => {
if (ignoredActorKeys.includes(key)) return acc;
const model = game.system.api.models.actors[key];
@ -62,34 +98,6 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
}, []);
}
static DEFAULT_OPTIONS = {
classes: ['daggerheart', 'sheet', 'dh-style']
};
static PARTS = {
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: [''] },
settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/settings.hbs' },
changes: {
template: 'systems/daggerheart/templates/sheets/activeEffect/changes.hbs',
scrollable: ['ol[data-changes]']
},
footer: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-form-footer.hbs' }
};
static TABS = {
sheet: {
tabs: [
{ id: 'details', icon: 'fa-solid fa-book' },
{ id: 'settings', icon: 'fa-solid fa-bars', label: 'DAGGERHEART.GENERAL.Tabs.settings' },
{ id: 'changes', icon: 'fa-solid fa-gears' }
],
initial: 'details',
labelPrefix: 'EFFECT.TABS'
}
};
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
const changeChoices = this.changeChoices;

View file

@ -7,19 +7,7 @@ export default class SettingActiveEffectConfig extends HandlebarsApplicationMixi
super({});
this.effect = foundry.utils.deepClone(effect);
const ignoredActorKeys = ['config', 'DhEnvironment'];
this.changeChoices = Object.keys(game.system.api.models.actors).reduce((acc, key) => {
if (!ignoredActorKeys.includes(key)) {
const model = game.system.api.models.actors[key];
const attributes = CONFIG.Token.documentClass.getTrackedAttributes(model);
const group = game.i18n.localize(model.metadata.label);
const choices = CONFIG.Token.documentClass
.getTrackedAttributeChoices(attributes, model)
.map(x => ({ ...x, group: group }));
acc.push(...choices);
}
return acc;
}, []);
this.changeChoices = game.system.api.applications.sheetConfigs.ActiveEffectConfig.getChangeChoices();
}
static DEFAULT_OPTIONS = {

View file

@ -73,9 +73,11 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
return context;
}
static async updateData(event, element, formData) {
static async updateData(_event, _element, formData) {
const data = foundry.utils.expandObject(formData.object);
foundry.utils.mergeObject(this.move, data);
await this.updateMove({
[`${this.movePath}`]: data
});
this.render();
}
@ -135,9 +137,7 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
}
);
await this.settings.updateSource({ [`${this.actionsPath}.${action.id}`]: action });
this.move = foundry.utils.getProperty(this.settings, this.movePath);
await this.updateMove({ [`${this.actionsPath}.${action.id}`]: action });
this.render();
}
@ -150,13 +150,12 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
await game.system.api.applications.sheetConfigs.SettingActiveEffectConfig.configure(effect);
if (!updatedEffect) return;
await this.settings.updateSource({
await this.updateMove({
[`${this.movePath}.effects`]: this.move.effects.reduce((acc, effect, index) => {
acc.push(index === effectIndex ? { ...updatedEffect, id: effect.id } : effect);
return acc;
}, [])
});
this.move = foundry.utils.getProperty(this.settings, this.movePath);
this.render();
} else {
const action = this.move.actions.get(id);
@ -171,13 +170,13 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
: existingEffectIndex === -1
? [...currentEffects, effectData]
: currentEffects.with(existingEffectIndex, effectData);
await this.settings.updateSource({
await this.updateMove({
[`${this.movePath}.effects`]: updatedEffects
});
}
await this.settings.updateSource({ [`${this.actionsPath}.${id}`]: updatedMove });
this.move = foundry.utils.getProperty(this.settings, this.movePath);
await this.updateMove({ [`${this.actionsPath}.${id}`]: updatedMove });
this.render();
return updatedEffects;
}).render(true);
@ -199,33 +198,36 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
});
}
}
await this.settings.updateSource({
await this.updateMove({
[this.movePath]: {
effects: move.effects.filter(x => x.id !== id),
actions: move.actions
}
});
} else {
await this.settings.updateSource({ [`${this.actionsPath}.-=${target.dataset.id}`]: null });
await this.updateMove({ [`${this.actionsPath}.-=${target.dataset.id}`]: null });
}
this.move = foundry.utils.getProperty(this.settings, this.movePath);
this.render();
}
static async addEffect(_, target) {
static async addEffect() {
const currentEffects = foundry.utils.getProperty(this.settings, `${this.movePath}.effects`);
await this.settings.updateSource({
await this.updateMove({
[`${this.movePath}.effects`]: [
...currentEffects,
game.system.api.data.activeEffects.BaseEffect.getDefaultObject()
]
});
this.move = foundry.utils.getProperty(this.settings, this.movePath);
this.render();
}
async updateMove(update) {
await this.settings.updateSource(update);
this.move = foundry.utils.getProperty(this.settings, this.movePath);
}
static resetMoves() {}
_filterTabs(tabs) {

View file

@ -6,7 +6,6 @@ import DaggerheartMenu from '../../sidebar/tabs/daggerheartMenu.mjs';
import { socketEvent } from '../../../systemRegistration/socket.mjs';
import GroupRollDialog from '../../dialogs/group-roll-dialog.mjs';
import DhpActor from '../../../documents/actor.mjs';
import DHItem from '../../../documents/item.mjs';
export default class Party extends DHBaseActorSheet {
constructor(options) {
@ -269,15 +268,6 @@ export default class Party extends DHBaseActorSheet {
).render({ force: true });
}
/**
* Get the set of ContextMenu options for Consumable and Loot.
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance
* @this {CharacterSheet}
* @protected
*/
static #getItemContextOptions() {
return this._getContextMenuCommonOptions.call(this, { usable: true, toChat: true });
}
/* -------------------------------------------- */
/* Filter Tracking */
/* -------------------------------------------- */

View file

@ -431,18 +431,18 @@ export default function DHApplicationMixin(Base) {
{
name: 'disableEffect',
icon: 'fa-solid fa-lightbulb',
condition: target => {
const doc = getDocFromElementSync(target);
return doc && !doc.disabled && doc.type !== 'beastform';
condition: element => {
const target = element.closest('[data-item-uuid]');
return !target.dataset.disabled && target.dataset.itemType !== 'beastform';
},
callback: async target => (await getDocFromElement(target)).update({ disabled: true })
},
{
name: 'enableEffect',
icon: 'fa-regular fa-lightbulb',
condition: target => {
const doc = getDocFromElementSync(target);
return doc && doc.disabled && doc.type !== 'beastform';
condition: element => {
const target = element.closest('[data-item-uuid]');
return target.dataset.disabled && target.dataset.itemType !== 'beastform';
},
callback: async target => (await getDocFromElement(target)).update({ disabled: false })
}
@ -539,9 +539,9 @@ export default function DHApplicationMixin(Base) {
options.push({
name: 'CONTROLS.CommonDelete',
icon: 'fa-solid fa-trash',
condition: target => {
const doc = getDocFromElementSync(target);
return doc && doc.type !== 'beastform';
condition: element => {
const target = element.closest('[data-item-uuid]');
return target.dataset.itemType !== 'beastform';
},
callback: async (target, event) => {
const doc = await getDocFromElement(target);

View file

@ -36,7 +36,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
],
dragDrop: [
{ dragSelector: '.inventory-item[data-type="attack"]', dropSelector: null },
{ dragSelector: ".currency[data-currency] .drag-handle", dropSelector: null }
{ dragSelector: '.currency[data-currency] .drag-handle', dropSelector: null }
]
};
@ -92,7 +92,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
value: context.source.system.gold[key]
};
}
context.inventory.hasCurrency = Object.values(context.inventory.currencies).some((c) => c.enabled);
context.inventory.hasCurrency = Object.values(context.inventory.currencies).some(c => c.enabled);
}
return context;
@ -270,7 +270,9 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
currency
});
if (quantity) {
originActor.update({ [`system.gold.${currency}`]: Math.max(0, originActor.system.gold[currency] - quantity) });
originActor.update({
[`system.gold.${currency}`]: Math.max(0, originActor.system.gold[currency] - quantity)
});
this.document.update({ [`system.gold.${currency}`]: this.document.system.gold[currency] + quantity });
}
return;
@ -292,6 +294,15 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
/* Handling transfer of inventoryItems */
if (item.system.metadata.isInventoryItem) {
if (!this.document.testUserPermission(game.user, 'OWNER', { exact: true })) {
return ui.notifications.error(
game.i18n.format('DAGGERHEART.UI.Notifications.lackingItemTransferPermission', {
user: game.user.name,
target: this.document.name
})
);
}
if (item.system.metadata.isQuantifiable) {
const actorItem = originActor.items.get(data.originId);
const quantityTransfered = await game.system.api.applications.dialogs.ItemTransferDialog.configure({
@ -300,14 +311,6 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
});
if (quantityTransfered) {
if (quantityTransfered === actorItem.system.quantity) {
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
} else {
await actorItem.update({
'system.quantity': actorItem.system.quantity - quantityTransfered
});
}
const existingItem = this.document.items.find(x => itemIsIdentical(x, item));
if (existingItem) {
await existingItem.update({
@ -325,10 +328,18 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
}
]);
}
if (quantityTransfered === actorItem.system.quantity) {
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
} else {
await actorItem.update({
'system.quantity': actorItem.system.quantity - quantityTransfered
});
}
}
} else {
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
await this.document.createEmbeddedDocuments('Item', [item.toObject()]);
await originActor.deleteEmbeddedDocuments('Item', [data.originId]);
}
}
}
@ -339,7 +350,7 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
*/
async _onDragStart(event) {
// Handle drag/dropping currencies
const currencyEl = event.currentTarget.closest(".currency[data-currency]");
const currencyEl = event.currentTarget.closest('.currency[data-currency]');
if (currencyEl) {
const currency = currencyEl.dataset.currency;
const data = { type: 'Currency', currency, originActor: this.document.uuid };
@ -359,8 +370,8 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
event.dataTransfer.setData('text/plain', JSON.stringify(attackData));
event.dataTransfer.setDragImage(attackItem.querySelector('img'), 60, 0);
return;
}
}
const item = await getDocFromElement(event.target);
if (item) {
const dragData = {