Merge branch 'main' into feature/granular-action-outcomes

This commit is contained in:
WBHarry 2026-05-10 18:21:10 +02:00
commit f260d221a8
36 changed files with 540 additions and 348 deletions

View file

@ -439,10 +439,13 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
'system.domain': { key: 'system.domain', value: this.setup.class?.system.domains ?? null }
};
if (type === 'subclasses')
if (type === 'subclasses') {
const classItem = this.setup.class;
const uuid = classItem?._stats.compendiumSource ?? classItem?.uuid;
presets.filter = {
'system.linkedClass.uuid': { key: 'system.linkedClass.uuid', value: this.setup.class?.uuid }
'system.linkedClass': { key: 'system.linkedClass', value: uuid }
};
}
if (equipment.includes(type))
presets.filter = {
@ -610,7 +613,8 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
[foundry.utils.randomID()]: {}
};
} else if (item.type === 'subclass' && event.target.closest('.subclass-card')) {
if (this.setup.class.system.subclasses.every(subclass => subclass.uuid !== item.uuid)) {
const classSubclasses = await this.setup.class.system.fetchSubclasses();
if (classSubclasses.every(subclass => subclass.uuid !== item.uuid)) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.subclassNotInClass'));
return;
}

View file

@ -209,8 +209,9 @@ export default class CharacterSheet extends DHBaseActorSheet {
context.attributes = Object.keys(this.document.system.traits).reduce((acc, key) => {
acc[key] = {
...this.document.system.traits[key],
name: game.i18n.localize(CONFIG.DH.ACTOR.abilities[key].name),
verbs: CONFIG.DH.ACTOR.abilities[key].verbs.map(x => game.i18n.localize(x))
label: _loc(CONFIG.DH.ACTOR.abilities[key].label),
verbs: CONFIG.DH.ACTOR.abilities[key].verbs.map(x => game.i18n.localize(x)),
isSpellcasting: this.document.system.spellcastModifierTrait?.key === key
};
return acc;
@ -227,7 +228,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
context.resources.stress.max < maxResource ? maxResource - context.resources.stress.max : 0;
context.equippedItems = sortBy(
this.document.items.filter(i => i.system.equipped),
this.document.items.filter(i => i.system.equipped && (i.type === 'weapon' || i.usable)),
i => (i.type === 'weapon' ? (i.system.secondary ? 1 : 0) : 2)
);

View file

@ -85,6 +85,14 @@ export default class Party extends DHBaseActorSheet {
/* Prepare Context */
/* -------------------------------------------- */
async _prepareContext(options) {
const context = await super._prepareContext(options);
const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming);
context.showStats =
settings.hidePartyStats === 'never' || (settings.hidePartyStats === 'players' && game.user.isGM);
return context;
}
async _preparePartContext(partId, context, options) {
context = await super._preparePartContext(partId, context, options);
switch (partId) {

View file

@ -104,9 +104,10 @@ export default class ClassSheet extends DHBaseItemSheet {
}
/**@inheritdoc */
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
async _prepareContext(options) {
const context = await super._prepareContext(options);
context.domains = this.document.system.domains;
context.subclasses = await this.document.system.fetchSubclasses();
return context;
}
@ -128,20 +129,8 @@ export default class ClassSheet extends DHBaseItemSheet {
const item = await fromUuid(data.uuid);
const itemType = data.type === 'ActiveEffect' ? data.type : item.type;
const target = event.target.closest('fieldset.drop-section');
if (itemType === 'subclass') {
if (item.system.linkedClass) {
return ui.notifications.warn(
game.i18n.format('DAGGERHEART.UI.Notifications.subclassAlreadyLinked', {
name: item.name,
class: this.document.name
})
);
}
await item.update({ 'system.linkedClass': this.document.uuid });
await this.document.update({
'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid]
});
} else if (['feature', 'ActiveEffect'].includes(itemType)) {
if (['feature', 'ActiveEffect'].includes(itemType)) {
super._onDrop(event);
} else if (this.document.parent?.type !== 'character') {
if (itemType === 'weapon') {
@ -200,12 +189,6 @@ export default class ClassSheet extends DHBaseItemSheet {
static async #removeItemFromCollection(_event, element) {
const { uuid, target } = element.dataset;
const prop = foundry.utils.getProperty(this.document.system, target);
if (target === 'subclasses') {
const subclass = await foundry.utils.fromUuid(uuid);
await subclass?.update({ 'system.linkedClass': null });
}
await this.document.update({ [`system.${target}`]: prop.filter(i => i && i.uuid !== uuid).map(x => x.uuid) });
}

View file

@ -40,4 +40,36 @@ export default class SubclassSheet extends DHBaseItemSheet {
get relatedDocs() {
return this.document.system.features.map(x => x.item);
}
async _prepareContext(options) {
const context = await super._prepareContext(options);
if (this.document.system.linkedClass) {
const classData = await fromUuid(this.document.system.linkedClass);
context.class = classData ?? {
name: _loc('DAGGERHEART.GENERAL.missingX', { x: _loc('TYPES.Item.class') }),
missing: true
};
}
return context;
}
async _onDrop(event) {
event.stopPropagation();
const data = TextEditor.getDragEventData(event);
const item = await fromUuid(data.uuid);
const itemType = data.type === 'ActiveEffect' ? data.type : item.type;
if (itemType === 'class') {
const uuid = item._stats.compendiumSource ?? item.uuid;
if (this.document.system.linkedClass !== uuid) {
await this.document.update({ 'system.linkedClass': uuid });
// Re-render all class sheets for instant feedback
for (const app of foundry.applications.instances.values()) {
if (app.document?.type === 'class') app.render();
}
}
return;
}
return super._onDrop(event);
}
}

View file

@ -277,7 +277,7 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
(await foundry.applications.ux.TextEditor.implementation.enrichHTML(item.description));
}
this.fieldFilter = this._createFieldFilter();
this.fieldFilter = await this._createFieldFilter();
if (this.presets?.filter) {
Object.entries(this.presets.filter).forEach(([k, v]) => {
@ -355,12 +355,12 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
);
}
_createFieldFilter() {
async _createFieldFilter() {
const filters = ItemBrowser.getFolderConfig(this.selectedMenu.data, 'filters');
filters.forEach(f => {
for (const f of filters) {
if (typeof f.field === 'string') f.field = foundry.utils.getProperty(game, f.field);
else if (typeof f.choices === 'function') {
f.choices = f.choices(this.items);
f.choices = await f.choices(this.items);
}
// Clear field label so template uses our custom label parameter
@ -370,7 +370,8 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
f.name ??= f.key;
f.value = this.presets?.filter?.[f.name]?.value ?? null;
});
}
return filters;
}