Merge branch 'main' into feature/chat-message-styles

This commit is contained in:
WBHarry 2025-07-28 20:54:02 +02:00
commit 094e0740dd
270 changed files with 13798 additions and 982 deletions

View file

@ -123,13 +123,15 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
);
const compendiumBeastforms = await game.packs.get(`daggerheart.beastforms`)?.getDocuments();
const beastformTiers = [...(compendiumBeastforms ? compendiumBeastforms : []), ...game.items].reduce(
const beastformTiers = [...game.items, ...(compendiumBeastforms ? compendiumBeastforms : [])].reduce(
(acc, x) => {
const tier = CONFIG.DH.GENERAL.tiers[x.system.tier];
if (x.type !== 'beastform' || tier.id > this.configData.tierLimit) return acc;
if (!acc[tier.id]) acc[tier.id] = { label: game.i18n.localize(tier.label), values: {} };
if (Object.values(acc[tier.id].values).find(existing => existing.value.name === x.name)) return acc;
acc[tier.id].values[x.uuid] = {
selected: this.selected?.uuid == x.uuid,
value: x,

View file

@ -91,9 +91,10 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
context.roll = this.roll;
context.rollType = this.roll?.constructor.name;
context.rallyDie = this.roll.rallyChoices;
context.experiences = Object.keys(this.config.data.experiences).map(id => ({
const experiences = this.config.data?.experiences || {};
context.experiences = Object.keys(experiences).map(id => ({
id,
...this.config.data.experiences[id]
...experiences[id]
}));
context.selectedExperiences = this.config.experiences;
context.advantage = this.config.roll?.advantage;

View file

@ -38,17 +38,15 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
};
get title() {
return game.i18n.localize('DAGGERHEART.EFFECTS.ApplyLocations.damageRoll.name');
return game.i18n.localize(`DAGGERHEART.EFFECTS.ApplyLocations.${this.config.isHealing ? 'healing' : 'damage'}Roll.name`);
}
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.config = CONFIG.DH;
context.title = this.config.title
? this.config.title
: game.i18n.localize('DAGGERHEART.EFFECTS.ApplyLocations.damageRoll.name');
// context.extraFormula = this.config.extraFormula;
context.title = this.config.title ?? this.title;
context.formula = this.roll.constructFormula(this.config);
context.isHealing = this.config.isHealing;
context.directDamage = this.config.directDamage;
context.selectedRollMode = this.config.selectedRollMode;
context.rollModes = Object.entries(CONFIG.Dice.rollModes).map(([action, { label, icon }]) => ({

View file

@ -1,6 +1,6 @@
import { damageKeyToNumber, getDamageLabel } from '../../helpers/utils.mjs';
const { DialogV2, ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class DamageReductionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(resolve, reject, actor, damage, damageType) {
@ -53,10 +53,6 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
);
}
get title() {
return game.i18n.localize('DAGGERHEART.APPLICATIONS.DamageReduction.title');
}
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'views', 'damage-reduction'],

View file

@ -1,3 +1,5 @@
// TO DELETE ?
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class DamageSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {

View file

@ -13,8 +13,9 @@ export default class DhpDeathMove extends HandlebarsApplicationMixin(Application
}
static DEFAULT_OPTIONS = {
classes: ['daggerheart', 'views', 'death-move'],
position: { width: 800, height: 'auto' },
classes: ['daggerheart', 'dh-style', 'dialog', 'views', 'death-move'],
position: { width: 'auto', height: 'auto' },
window: { icon: 'fa-solid fa-skull' },
actions: {
selectMove: this.selectMove,
takeMove: this.takeMove
@ -32,6 +33,7 @@ export default class DhpDeathMove extends HandlebarsApplicationMixin(Application
const context = await super._prepareContext(_options);
context.selectedMove = this.selectedMove;
context.options = CONFIG.DH.GENERAL.deathMoves;
context.title = game.i18n.localize('DAGGERHEART.APPLICATIONS.DeathMove.takeMove');
return context;
}

View file

@ -66,12 +66,9 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD {
if (!status) continue;
if (status._id) {
if (status._id !== effect.id) continue;
} else {
if (effect.statuses.size !== 1) continue;
}
status.isActive = true;
if (effect.getFlag('core', 'overlay')) status.isOverlay = true;
break;
}
}

View file

@ -43,7 +43,7 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
{ id: 'hope', label: 'DAGGERHEART.GENERAL.hope' },
{ id: 'fear', label: 'DAGGERHEART.GENERAL.fear' },
{ id: 'advantage', label: 'DAGGERHEART.GENERAL.Advantage.full' },
{ id: 'disadvantage', label: 'DAGGERHEART.GENERAL.Advantage.full' }
{ id: 'disadvantage', label: 'DAGGERHEART.GENERAL.Disadvantage.full' }
],
initial: 'hope'
}
@ -59,8 +59,20 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
const context = await super._prepareContext(_options);
context.settingFields = this.settings;
context.diceSoNiceTextures = game.dice3d?.exports?.TEXTURELIST ?? {};
context.diceSoNiceColorsets = game.dice3d?.exports?.COLORSETS ?? {};
context.showDiceSoNice = game.modules.get('dice-so-nice')?.active;
if (game.dice3d) {
context.diceSoNiceTextures = game.dice3d.exports.TEXTURELIST;
context.diceSoNiceColorsets = game.dice3d.exports.COLORSETS;
context.diceSoNiceMaterials = Object.keys(game.dice3d.DiceFactory.material_options).map(key => ({
key: key,
name: `DICESONICE.Material${key.capitalize()}`
}));
context.diceSoNiceSystems = [];
for (const [key, system] of game.dice3d.DiceFactory.systems.entries()) {
context.diceSoNiceSystems.push({ key, name: system.name });
}
}
context.diceTab = {
key: this.tabGroups.diceSoNice,
source: this.settings._source.diceSoNice[this.tabGroups.diceSoNice],

View file

@ -136,10 +136,14 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
...move,
name: game.i18n.localize(move.name),
description: game.i18n.localize(move.description),
actions: move.actions.map(action => ({
...action,
name: game.i18n.localize(action.name)
}))
actions: move.actions.reduce((acc, key) => {
const action = move.actions[key];
acc[key] = {
...action,
name: game.i18n.localize(action.name)
};
return acc;
}, {})
};
return acc;
@ -165,8 +169,18 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
}
static async reset() {
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
title: game.i18n.format('DAGGERHEART.SETTINGS.ResetSettings.resetConfirmationTitle')
},
content: game.i18n.format('DAGGERHEART.SETTINGS.ResetSettings.resetConfirmationText', {
settings: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.homebrew.name')
})
});
if (!confirmed) return;
const resetSettings = new DhHomebrew();
let localizedSettings = this.localizeObject(resetSettings);
let localizedSettings = this.localizeObject(resetSettings.toObject());
this.settings.updateSource(localizedSettings);
this.render();
}

View file

@ -110,6 +110,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
context.costOptions = this.getCostOptions();
context.disableOption = this.disableOption.bind(this);
context.isNPC = this.action.actor?.isNPC;
context.baseSaveDifficulty = this.action.actor?.baseSaveDifficulty;
context.hasRoll = this.action.hasRoll;
const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers;
@ -117,7 +118,6 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
{ key: 1, label: game.i18n.localize('DAGGERHEART.GENERAL.Tiers.1') },
...Object.values(settingsTiers).map(x => ({ key: x.tier, label: x.name }))
];
return context;
}

View file

@ -99,7 +99,17 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
async _preparePartContext(partId, context) {
const partContext = await super._preparePartContext(partId, context);
switch (partId) {
case 'changes':
case 'details':
const useGeneric = game.settings.get(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.appearance
).showGenericStatusEffects;
if (!useGeneric) {
partContext.statuses = Object.values(CONFIG.DH.GENERAL.conditions).map(status => ({
value: status.id,
label: game.i18n.localize(status.name)
}));
}
break;
}

View file

@ -75,7 +75,7 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings {
* @returns
*/
static async #deleteAdversary(_event, target) {
const doc = getDocFromElement(target);
const doc = await getDocFromElement(target);
const { category } = target.dataset;
const path = `system.potentialAdversaries.${category}.adversaries`;

View file

@ -4,7 +4,7 @@ import { abilities } from '../../../config/actorConfig.mjs';
import DhCharacterlevelUp from '../../levelup/characterLevelup.mjs';
import DhCharacterCreation from '../../characterCreation/characterCreation.mjs';
import FilterMenu from '../../ux/filter-menu.mjs';
import { getDocFromElement, itemAbleRollParse } from '../../../helpers/utils.mjs';
import { getDocFromElement, getDocFromElementSync } from '../../../helpers/utils.mjs';
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
@ -55,7 +55,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
},
{
handler: CharacterSheet.#getItemContextOptions,
selector: '[data-item-uuid][data-type="consumable"], [data-item-uuid][data-type="miscellaneous"]',
selector: '[data-item-uuid][data-type="consumable"], [data-item-uuid][data-type="loot"]',
options: {
parentClassHooks: false,
fixed: true
@ -258,19 +258,27 @@ export default class CharacterSheet extends DHBaseActorSheet {
{
name: 'toLoadout',
icon: 'fa-solid fa-arrow-up',
condition: target => getDocFromElement(target).system.inVault,
callback: target => {
const doc = getDocFromElement(target),
actorLoadout = doc.actor.system.loadoutSlot;
if(actorLoadout.available) return doc.update({ 'system.inVault': false });
ui.notifications.warn(game.i18n.format('DAGGERHEART.UI.Notifications.loadoutMaxReached', { max: actorLoadout.max }))
condition: target => {
const doc = getDocFromElementSync(target);
return doc && system.inVault;
},
callback: async target => {
const doc = await getDocFromElement(target);
const actorLoadout = doc.actor.system.loadoutSlot;
if (actorLoadout.available) return doc.update({ 'system.inVault': false });
ui.notifications.warn(
game.i18n.format('DAGGERHEART.UI.Notifications.loadoutMaxReached', { max: actorLoadout.max })
);
}
},
{
name: 'toVault',
icon: 'fa-solid fa-arrow-down',
condition: target => !getDocFromElement(target).system.inVault,
callback: target => getDocFromElement(target).update({ 'system.inVault': true })
condition: target => {
const doc = getDocFromElementSync(target);
return doc && !doc.system.inVault;
},
callback: async target => (await getDocFromElement(target)).update({ 'system.inVault': true })
}
].map(option => ({
...option,
@ -292,13 +300,19 @@ export default class CharacterSheet extends DHBaseActorSheet {
{
name: 'equip',
icon: 'fa-solid fa-hands',
condition: target => !getDocFromElement(target).system.equipped,
condition: target => {
const doc = getDocFromElementSync(target);
return doc && !doc.system.equipped;
},
callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target)
},
{
name: 'unequip',
icon: 'fa-solid fa-hands',
condition: target => getDocFromElement(target).system.equipped,
condition: target => {
const doc = getDocFromElementSync(target);
return doc && doc.system.equipped;
},
callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target)
}
].map(option => ({
@ -311,7 +325,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
}
/**
* Get the set of ContextMenu options for Consumable and Miscellaneous.
* 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
@ -407,11 +421,11 @@ export default class CharacterSheet extends DHBaseActorSheet {
* @param {HTMLElement} html The container to filter items from.
* @protected
*/
_onSearchFilterInventory(event, query, rgx, html) {
async _onSearchFilterInventory(_event, query, rgx, html) {
this.#filteredItems.inventory.search.clear();
for (const li of html.querySelectorAll('.inventory-item')) {
const item = getDocFromElement(li);
const item = await getDocFromElement(li);
const matchesSearch = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name);
if (matchesSearch) this.#filteredItems.inventory.search.add(item.id);
const { menu } = this.#filteredItems.inventory;
@ -427,11 +441,11 @@ export default class CharacterSheet extends DHBaseActorSheet {
* @param {HTMLElement} html The container to filter items from.
* @protected
*/
_onSearchFilterCard(event, query, rgx, html) {
async _onSearchFilterCard(_event, query, rgx, html) {
this.#filteredItems.loadout.search.clear();
for (const li of html.querySelectorAll('.items-list .inventory-item, .card-list .card-item')) {
const item = getDocFromElement(li);
const item = await getDocFromElement(li);
const matchesSearch = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name);
if (matchesSearch) this.#filteredItems.loadout.search.add(item.id);
const { menu } = this.#filteredItems.loadout;
@ -478,11 +492,11 @@ export default class CharacterSheet extends DHBaseActorSheet {
* @param {HTMLElement} html
* @param {import('../ux/filter-menu.mjs').FilterItem[]} filters
*/
_onMenuFilterInventory(event, html, filters) {
async _onMenuFilterInventory(_event, html, filters) {
this.#filteredItems.inventory.menu.clear();
for (const li of html.querySelectorAll('.inventory-item')) {
const item = getDocFromElement(li);
const item = await getDocFromElement(li);
const matchesMenu =
filters.length === 0 || filters.some(f => foundry.applications.ux.SearchFilter.evaluateFilter(item, f));
@ -499,11 +513,11 @@ export default class CharacterSheet extends DHBaseActorSheet {
* @param {HTMLElement} html
* @param {import('../ux/filter-menu.mjs').FilterItem[]} filters
*/
_onMenuFilterLoadout(event, html, filters) {
async _onMenuFilterLoadout(_event, html, filters) {
this.#filteredItems.loadout.menu.clear();
for (const li of html.querySelectorAll('.items-list .inventory-item, .card-list .card-item')) {
const item = getDocFromElement(li);
const item = await getDocFromElement(li);
const matchesMenu =
filters.length === 0 || filters.some(f => foundry.applications.ux.SearchFilter.evaluateFilter(item, f));
@ -519,7 +533,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
/* -------------------------------------------- */
async updateItemResource(event) {
const item = getDocFromElement(event.currentTarget);
const item = await getDocFromElement(event.currentTarget);
if (!item) return;
const max = event.currentTarget.max ? Number(event.currentTarget.max) : null;
@ -529,7 +543,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
}
async updateItemQuantity(event) {
const item = getDocFromElement(event.currentTarget);
const item = await getDocFromElement(event.currentTarget);
if (!item) return;
await item.update({ 'system.quantity': event.currentTarget.value });
@ -609,7 +623,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
* @type {ApplicationClickAction}
*/
static async #toggleEquipItem(_event, button) {
const item = getDocFromElement(button);
const item = await getDocFromElement(button);
if (!item) return;
if (item.system.equipped) {
await item.update({ 'system.equipped': false });
@ -664,7 +678,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
* @type {ApplicationClickAction}
*/
static async #toggleVault(_event, button) {
const doc = getDocFromElement(button);
const doc = await getDocFromElement(button);
await doc?.update({ 'system.inVault': !doc.system.inVault });
}
@ -673,7 +687,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
* @type {ApplicationClickAction}
*/
static async #toggleResourceDice(event, target) {
const item = getDocFromElement(target);
const item = await getDocFromElement(target);
const { dice } = event.target.closest('.item-resource').dataset;
const diceState = item.system.resource.diceStates[dice];
@ -688,7 +702,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
* @type {ApplicationClickAction}
*/
static async #handleResourceDice(_, target) {
const item = getDocFromElement(target);
const item = await getDocFromElement(target);
if (!item) return;
const rollValues = await game.system.api.applications.dialogs.ResourceDiceDialog.create(item, this.document);
@ -709,7 +723,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
}
async _onDragStart(event) {
const item = getDocFromElement(event.target);
const item = await getDocFromElement(event.target);
const dragData = {
type: item.documentName,

View file

@ -1,6 +1,5 @@
const { HandlebarsApplicationMixin } = foundry.applications.api;
import { getDocFromElement, tagifyElement } from '../../../helpers/utils.mjs';
import DHActionConfig from '../../sheets-configs/action-config.mjs';
import { getDocFromElement, getDocFromElementSync, tagifyElement } from '../../../helpers/utils.mjs';
/**
* @typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction
@ -124,7 +123,14 @@ export default function DHApplicationMixin(Base) {
/**@inheritdoc */
async _onFirstRender(context, options) {
await super._onFirstRender(context, options);
this.relatedDocs.filter(doc => doc).map(doc => (doc.apps[this.id] = this));
const docs = [];
for (var docData of this.relatedDocs) {
const doc = await foundry.utils.fromUuid(docData.uuid);
docs.push(doc);
}
docs.filter(doc => doc).map(doc => (doc.apps[this.id] = this));
if (!!this.options.contextMenus.length) this._createContextMenus();
}
@ -259,14 +265,20 @@ export default function DHApplicationMixin(Base) {
{
name: 'disableEffect',
icon: 'fa-solid fa-lightbulb',
condition: target => !getDocFromElement(target).disabled,
callback: target => getDocFromElement(target).update({ disabled: true })
condition: target => {
const doc = getDocFromElementSync(target);
return doc && !doc.disabled;
},
callback: async target => (await getDocFromElement(target)).update({ disabled: true })
},
{
name: 'enableEffect',
icon: 'fa-regular fa-lightbulb',
condition: target => getDocFromElement(target).disabled,
callback: target => getDocFromElement(target).update({ disabled: false })
condition: target => {
const doc = getDocFromElementSync(target);
return doc && doc.disabled;
},
callback: async target => (await getDocFromElement(target)).update({ disabled: false })
}
].map(option => ({
...option,
@ -299,10 +311,10 @@ export default function DHApplicationMixin(Base) {
name: 'CONTROLS.CommonEdit',
icon: 'fa-solid fa-pen-to-square',
condition: target => {
const doc = getDocFromElement(target);
return !doc.hasOwnProperty('systemPath') || doc.inCollection;
const doc = getDocFromElementSync(target);
return !doc || !doc.hasOwnProperty('systemPath') || doc.inCollection;
},
callback: target => getDocFromElement(target).sheet.render({ force: true })
callback: async target => (await getDocFromElement(target)).sheet.render({ force: true })
}
];
@ -311,25 +323,25 @@ export default function DHApplicationMixin(Base) {
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem',
icon: 'fa-solid fa-burst',
condition: target => {
const doc = getDocFromElement(target);
return !(doc.type === 'domainCard' && doc.system.inVault)
const doc = getDocFromElementSync(target);
return doc && !(doc.type === 'domainCard' && doc.system.inVault);
},
callback: (target, event) => getDocFromElement(target).use(event)
callback: async (target, event) => (await getDocFromElement(target)).use(event)
});
if (toChat)
options.unshift({
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
icon: 'fa-solid fa-message',
callback: target => getDocFromElement(target).toChat(this.document.id)
callback: async target => (await getDocFromElement(target)).toChat(this.document.id)
});
if (deletable)
options.push({
name: 'CONTROLS.CommonDelete',
icon: 'fa-solid fa-trash',
callback: (target, event) => {
const doc = getDocFromElement(target);
callback: async (target, event) => {
const doc = await getDocFromElement(target);
if (event.shiftKey) return doc.delete();
else return doc.deleteDialog();
}
@ -371,7 +383,7 @@ export default function DHApplicationMixin(Base) {
if (!actionId && !itemUuid) return;
const doc = itemUuid
? getDocFromElement(extensibleElement)
? await getDocFromElement(extensibleElement)
: this.document.system.attack?.id === actionId
? this.document.system.attack
: this.document.system.actions?.get(actionId);
@ -429,8 +441,8 @@ export default function DHApplicationMixin(Base) {
* Renders an embedded document.
* @type {ApplicationClickAction}
*/
static #editDoc(_event, target) {
const doc = getDocFromElement(target);
static async #editDoc(_event, target) {
const doc = await getDocFromElement(target);
if (doc) return doc.sheet.render({ force: true });
}
@ -439,7 +451,7 @@ export default function DHApplicationMixin(Base) {
* @type {ApplicationClickAction}
*/
static async #deleteDoc(event, target) {
const doc = getDocFromElement(target);
const doc = await getDocFromElement(target);
if (doc) {
if (event.shiftKey) return doc.delete();
else return await doc.deleteDialog();
@ -451,7 +463,7 @@ export default function DHApplicationMixin(Base) {
* @type {ApplicationClickAction}
*/
static async #toChat(_event, target) {
let doc = getDocFromElement(target);
let doc = await getDocFromElement(target);
return doc.toChat(this.document.id);
}
@ -460,7 +472,7 @@ export default function DHApplicationMixin(Base) {
* @type {ApplicationClickAction}
*/
static async #useItem(event, target) {
let doc = getDocFromElement(target);
let doc = await getDocFromElement(target);
await doc.use(event);
}
@ -469,7 +481,7 @@ export default function DHApplicationMixin(Base) {
* @type {ApplicationClickAction}
*/
static async #toggleEffect(_, target) {
const doc = getDocFromElement(target);
const doc = await getDocFromElement(target);
await doc.update({ disabled: !doc.disabled });
}
@ -492,7 +504,7 @@ export default function DHApplicationMixin(Base) {
const t = extensible?.classList.toggle('extended');
const descriptionElement = extensible?.querySelector('.invetory-description');
if (t && !!descriptionElement) this.#prepareInventoryDescription(extensible, descriptionElement);
if (t && !!descriptionElement) await this.#prepareInventoryDescription(extensible, descriptionElement);
}
}

View file

@ -117,7 +117,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
name: 'CONTROLS.CommonDelete',
icon: '<i class="fa-solid fa-trash"></i>',
callback: async target => {
const feature = getDocFromElement(target);
const feature = await getDocFromElement(target);
if (!feature) return;
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
@ -168,7 +168,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
*/
static async #deleteFeature(_, element) {
const target = element.closest('[data-item-uuid]');
const feature = getDocFromElement(target);
const feature = await getDocFromElement(target);
if (!feature) return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing'));
await this.document.update({
'system.features': this.document.system.features
@ -249,12 +249,20 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
const target = event.target.closest('fieldset.drop-section');
const item = await fromUuid(data.uuid);
if (item?.type === 'feature') {
await this.document.update({
'system.features': [...this.document.system.features, { type: target.dataset.type, item }].map(x => ({
...x,
item: x.item?.uuid
}))
});
if (target.dataset.type) {
await this.document.update({
'system.features': [...this.document.system.features, { type: target.dataset.type, item }].map(
x => ({
...x,
item: x.item?.uuid
})
)
});
} else {
await this.document.update({
'system.features': [...this.document.system.features, item].map(x => x.uuid)
});
}
}
}
}

View file

@ -6,6 +6,6 @@ export { default as Community } from './community.mjs';
export { default as Consumable } from './consumable.mjs';
export { default as DomainCard } from './domainCard.mjs';
export { default as Feature } from './feature.mjs';
export { default as Miscellaneous } from './miscellaneous.mjs';
export { default as Loot } from './loot.mjs';
export { default as Subclass } from './subclass.mjs';
export { default as Weapon } from './weapon.mjs';

View file

@ -81,6 +81,7 @@ export default class BeastformSheet extends DHBaseItemSheet {
case 'effects':
context.effects.actives = context.effects.actives.map(effect => {
const data = effect.toObject();
data.uuid = effect.uuid;
data.id = effect.id;
if (effect.type === 'beastform') data.mandatory = true;

View file

@ -132,14 +132,14 @@ export default class ClassSheet extends DHBaseItemSheet {
});
}
} else if (target.classList.contains('choice-a-section')) {
if (item.type === 'miscellaneous' || item.type === 'consumable') {
if (item.type === 'loot' || item.type === 'consumable') {
const filteredChoiceA = this.document.system.inventory.choiceA;
if (filteredChoiceA.length < 2)
await this.document.update({
'system.inventory.choiceA': [...filteredChoiceA.map(x => x.uuid), item.uuid]
});
}
} else if (item.type === 'miscellaneous') {
} else if (item.type === 'loot') {
if (target.classList.contains('take-section')) {
const filteredTake = this.document.system.inventory.take.filter(x => x);
if (filteredTake.length < 3)

View file

@ -1,15 +1,15 @@
import DHBaseItemSheet from '../api/base-item.mjs';
export default class MiscellaneousSheet extends DHBaseItemSheet {
export default class LootSheet extends DHBaseItemSheet {
/**@inheritdoc */
static DEFAULT_OPTIONS = {
classes: ['miscellaneous'],
classes: ['loot'],
position: { width: 550 }
};
/**@override */
static PARTS = {
header: { template: 'systems/daggerheart/templates/sheets/items/miscellaneous/header.hbs' },
header: { template: 'systems/daggerheart/templates/sheets/items/loot/header.hbs' },
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
actions: {
@ -17,7 +17,7 @@ export default class MiscellaneousSheet extends DHBaseItemSheet {
scrollable: ['.actions']
},
settings: {
template: 'systems/daggerheart/templates/sheets/items/miscellaneous/settings.hbs',
template: 'systems/daggerheart/templates/sheets/items/loot/settings.hbs',
scrollable: ['.settings']
},
effects: {

View file

@ -1,3 +1,5 @@
import { emitAsGM, GMUpdateEvent } from "../../systemRegistration/socket.mjs";
export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog {
constructor(options) {
super(options);
@ -17,9 +19,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
html.querySelectorAll('.duality-action-damage').forEach(element =>
element.addEventListener('click', event => this.onRollDamage(event, data.message))
);
html.querySelectorAll('.duality-action-healing').forEach(element =>
element.addEventListener('click', event => this.onRollHealing(event, data.message))
);
html.querySelectorAll('.target-save-container').forEach(element =>
element.addEventListener('click', event => this.onRollSave(event, data.message))
);
@ -66,8 +65,8 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
super.close(options);
}
async getActor(id) {
return await fromUuid(id);
async getActor(uuid) {
return await foundry.utils.fromUuid(uuid);
}
getAction(actor, itemId, actionId) {
@ -92,17 +91,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
}
}
async onRollHealing(event, message) {
event.stopPropagation();
const actor = await this.getActor(message.system.source.actor);
if (!actor || !game.user.isGM) return true;
if (message.system.source.item && message.system.source.action) {
const action = this.getAction(actor, message.system.source.item, message.system.source.action);
if (!action || !action?.rollHealing) return;
await action.rollHealing(event, message);
}
}
async onRollSave(event, message) {
event.stopPropagation();
const actor = await this.getActor(message.system.source.actor),
@ -112,17 +100,41 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
if (message.system.source.item && message.system.source.action) {
const action = this.getAction(actor, message.system.source.item, message.system.source.action);
if (!action || !action?.hasSave) return;
action.rollSave(token, event, message);
action.rollSave(token.actor, event, message).then(result => emitAsGM(
GMUpdateEvent.UpdateSaveMessage,
action.updateSaveMessage.bind(action, result, message, token.id),
{
action: action.uuid,
message: message._id,
token: token.id,
result
}
));
}
}
onRollAllSave(event, _message) {
async onRollAllSave(event, message) {
event.stopPropagation();
if(!game.user.isGM) return;
const targets = event.target.parentElement.querySelectorAll(
'.target-section > [data-token] .target-save-container'
);
targets.forEach(el => {
el.dispatchEvent(new PointerEvent('click', { shiftKey: true }));
const actor = await this.getActor(message.system.source.actor),
action = this.getAction(actor, message.system.source.item, message.system.source.action);
targets.forEach(async el => {
const tokenId = el.closest('[data-token]')?.dataset.token,
token = game.canvas.tokens.get(tokenId);
if(!token.actor) return;
if(game.user === token.actor.owner)
el.dispatchEvent(new PointerEvent('click', { shiftKey: true }));
else {
token.actor.owner.query('reactionRoll', {
actionId: action.uuid,
actorId: token.actor.uuid,
event,
message
}).then(result => action.updateSaveMessage(result, message, token.id));
}
});
}
@ -160,7 +172,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
return {
isHit,
targets: isHit
? message.system.targets.filter(t => t.hit === true).map(target => game.canvas.tokens.get(target.id))
? message.system.targets.filter(t => t.hit === true).map(target => game.canvas.tokens.documentCollection.find(t => t.actor.uuid === target.actorId))
: Array.from(game.user.targets)
};
}
@ -222,19 +234,10 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
});
}
target.actor.takeDamage(damages);
}
}
async onHealing(event, message) {
event.stopPropagation();
const targets = Array.from(game.user.targets);
if (targets.length === 0)
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected'));
for (var target of targets) {
target.actor.takeHealing(message.system.roll);
if(message.system.hasHealing)
target.actor.takeHealing(damages);
else
target.actor.takeDamage(damages);
}
}