Merged with v14-Dev

This commit is contained in:
WBHarry 2026-03-15 19:13:37 +01:00
commit 88be00567e
650 changed files with 6323 additions and 4508 deletions

View file

@ -554,7 +554,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
experiences: {
...this.setup.experiences,
...Object.keys(this.character.system.experiences).reduce((acc, key) => {
acc[`-=${key}`] = null;
acc[`${key}`] = _del;
return acc;
}, {})
}

View file

@ -77,8 +77,8 @@ export default class CharacterResetDialog extends HandlebarsApplicationMixin(App
if (!this.data.optional.portrait.keep) {
foundry.utils.setProperty(update, 'img', this.actor.schema.fields.img.initial(this.actor));
foundry.utils.setProperty(update, 'prototypeToken.==texture', {});
foundry.utils.setProperty(update, 'prototypeToken.==ring', {});
foundry.utils.setProperty(update, 'prototypeToken.texture', _replace({}));
foundry.utils.setProperty(update, 'prototypeToken.ring', _replace({}));
}
if (this.data.optional.biography.keep)
@ -89,7 +89,7 @@ export default class CharacterResetDialog extends HandlebarsApplicationMixin(App
const { system, ...rest } = update;
await this.actor.update({
...rest,
'==system': system ?? {}
system: _replace(system ?? {})
});
const inventoryItemTypes = ['weapon', 'armor', 'consumable', 'loot'];

View file

@ -1,4 +1,4 @@
import { refreshIsAllowed } from '../../helpers/utils.mjs';
import { expireActiveEffects, refreshIsAllowed } from '../../helpers/utils.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
@ -264,6 +264,8 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
await feature.update({ 'system.resource.value': resetValue });
}
expireActiveEffects(this.actor, [this.shortRest ? 'shortRest' : 'longRest']);
this.close();
} else {
this.render();

View file

@ -245,19 +245,21 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
if (error) return error;
await this.party.update({
'system.==tagTeam': new game.system.api.data.TagTeamData({
...this.party.system.tagTeam.toObject(),
initiator: this.initiator,
members: this.partyMembers.reduce((acc, member) => {
if (member.selected)
acc[member.id] = {
name: member.name,
img: member.img,
rollType: CONFIG.DH.GENERAL.tagTeamRollTypes.trait.id
};
return acc;
}, {})
})
'system.tagTeam': _replace(
new game.system.api.data.TagTeamData({
...this.party.system.tagTeam.toObject(),
initiator: this.initiator,
members: this.partyMembers.reduce((acc, member) => {
if (member.selected)
acc[member.id] = {
name: member.name,
img: member.img,
rollType: CONFIG.DH.GENERAL.tagTeamRollTypes.trait.id
};
return acc;
}, {})
})
)
});
const hookData = { openForAllPlayers: this.openForAllPlayers, partyId: this.party.id };
@ -566,7 +568,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
return mainRoll;
}
static async #onCancelRoll(options = { confirm: true }) {
static async #onCancelRoll(_event, _button, options = { confirm: true }) {
this.cancelRoll(options);
}
@ -584,9 +586,9 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio
await this.updatePartyData(
{
'system.==tagTeam': {
'system.tagTeam': {
initiator: null,
members: {}
members: _replace({})
}
},
{ render: false }

View file

@ -67,7 +67,7 @@ export default class DhCompanionLevelUp extends BaseLevelUp {
break;
case 'summary':
const levelKeys = Object.keys(this.levelup.levels);
const actorDamageDice = this.actor.system.attack.damage.parts[0].value.dice;
const actorDamageDice = this.actor.system.attack.damage.parts.hitPoints.value.dice;
const actorRange = this.actor.system.attack.range;
let achievementExperiences = [];

View file

@ -477,7 +477,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
const secondaryData = Object.keys(
foundry.utils.getProperty(this.levelup, `${target.dataset.path}.secondaryData`)
).reduce((acc, key) => {
acc[`-=${key}`] = null;
acc[key] = _del;
return acc;
}, {});
await this.levelup.updateSource({
@ -511,9 +511,9 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
const current = foundry.utils.getProperty(this.levelup, `${basePath}.${button.dataset.option}`);
if (Number(button.dataset.cost) > 1 || Object.keys(current).length === 1) {
// Simple handling that doesn't cover potential Custom LevelTiers.
update[`${basePath}.-=${button.dataset.option}`] = null;
update[`${basePath}.${button.dataset.option}`] = _del;
} else {
update[`${basePath}.${button.dataset.option}.-=${button.dataset.checkboxNr}`] = null;
update[`${basePath}.${button.dataset.option}.${button.dataset.checkboxNr}`] = _del;
}
} else {
if (this.levelup.levels[this.levelup.currentLevel].nrSelections.available < Number(button.dataset.cost)) {

View file

@ -62,7 +62,15 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S
}
async _onDrop(event) {
event.stopPropagation();
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event);
if (data.type === 'Level') {
const level = await foundry.documents.Level.fromDropData(data);
if (level?.parent === this.document) return this._onSortLevel(event, level);
return;
}
const item = await foundry.utils.fromUuid(data.uuid);
if (item instanceof game.system.api.documents.DhpActor && item.type === 'environment') {
let sceneUuid = data.uuid;
@ -114,7 +122,7 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S
for (const key of Object.keys(this.document._source.flags.daggerheart?.sceneEnvironments ?? {})) {
if (!submitData.flags.daggerheart.sceneEnvironments[key]) {
submitData.flags.daggerheart.sceneEnvironments[`-=${key}`] = null;
submitData.flags.daggerheart.sceneEnvironments[key] = _del;
}
}

View file

@ -12,7 +12,7 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
static DEFAULT_OPTIONS = {
tag: 'form',
id: 'daggerheart-appearance-settings',
classes: ['daggerheart', 'dialog', 'dh-style', 'setting'],
classes: ['daggerheart', 'dialog', 'dh-style', 'setting', 'appearance-settings'],
position: { width: '600', height: 'auto' },
window: {
title: 'DAGGERHEART.SETTINGS.Menu.title',
@ -70,6 +70,14 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
}
}
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
htmlElement
.querySelector('.default-animations-input')
?.addEventListener('change', this.toggleSFXOverride.bind(this));
}
/** @inheritdoc */
_configureRenderParts(options) {
const parts = super._configureRenderParts(options);
@ -83,15 +91,20 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
/**@inheritdoc */
async _prepareContext(options) {
const context = await super._prepareContext(options);
if (options.isFirstRender)
if (options.isFirstRender) {
this.setting = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance);
this.globalOverrides = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides);
}
context.setting = this.setting;
context.globalOverrides = this.globalOverrides;
context.fields = this.setting.schema.fields;
context.tabs = this._prepareTabs('general');
context.dsnTabs = this._prepareTabs('diceSoNice');
context.isGM = game.user.isGM;
return context;
}
@ -120,6 +133,9 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
* @protected
*/
async prepareDiceSoNiceContext(context) {
context.animationEvents = CONFIG.DH.GENERAL.daggerheartDiceAnimationEvents;
context.previewAnimation = this.previewAnimation;
context.diceSoNiceTextures = Object.entries(game.dice3d.exports.TEXTURELIST).reduce(
(acc, [k, v]) => ({
...acc,
@ -146,6 +162,13 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
);
context.diceSoNiceFonts = game.dice3d.exports.Utils.prepareFontList();
const getAnimationsOptions = key => {
const fields = context.fields.diceSoNice.fields[key].fields.sfx.fields;
return {
higher: fields.higher.fields.class.choices
};
};
foundry.utils.mergeObject(
context.dsnTabs,
['hope', 'fear', 'advantage', 'disadvantage'].reduce(
@ -153,7 +176,8 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
...acc,
[key]: {
values: this.setting.diceSoNice[key],
fields: this.setting.schema.getField(`diceSoNice.${key}`).fields
fields: this.setting.schema.getField(`diceSoNice.${key}`).fields,
animations: ['hope', 'fear'].includes(key) ? getAnimationsOptions(key) : {}
}
}),
{}
@ -169,13 +193,20 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
* @param {foundry.applications.ux.FormDataExtended} formData
* @returns {Promise<void>}
*/
static async #onSubmit(event, form, formData) {
static async #onSubmit(_event, _form, formData) {
const data = this.setting.schema.clean(foundry.utils.expandObject(formData.object));
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance, data);
}
/* -------------------------------------------- */
async toggleSFXOverride(event) {
await this.globalOverrides.diceSoNiceSFXUpdate(this.setting, event.target.checked);
this.globalOverrides = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.GlobalOverrides);
this.render();
}
/**
* Submit the configuration form.
* @this {DHAppearanceSettings}
@ -183,13 +214,25 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
*/
static async #onPreview(_, target) {
const formData = new foundry.applications.ux.FormDataExtended(target.closest('form'));
const { diceSoNice } = foundry.utils.expandObject(formData.object);
const { diceSoNice, ...rest } = foundry.utils.expandObject(formData.object);
const { key } = target.dataset;
const faces = ['advantage', 'disadvantage'].includes(key) ? 'd6' : 'd12';
const preset = await getDiceSoNicePreset(diceSoNice[key], faces);
const diceSoNiceRoll = await new foundry.dice.Roll(`1${faces}`).evaluate();
diceSoNiceRoll.dice[0].options.appearance = preset.appearance;
diceSoNiceRoll.dice[0].options.modelFile = preset.modelFile;
const previewAnimation = rest[`${key}PreviewAnimation`];
const events = CONFIG.DH.GENERAL.daggerheartDiceAnimationEvents;
if (previewAnimation) {
if (previewAnimation === events.critical.id && diceSoNice.sfx.critical.class) {
diceSoNiceRoll.dice[0].options.sfx = { specialEffect: diceSoNice.sfx.critical.class };
}
if (previewAnimation === events.higher.id && diceSoNice[key].sfx.higher) {
diceSoNiceRoll.dice[0].options.sfx = { specialEffect: diceSoNice[key].sfx.higher.class };
}
}
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, false);
}

View file

@ -298,7 +298,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
const isDowntime = ['shortRest', 'longRest'].includes(type);
const path = isDowntime ? `restMoves.${type}.moves` : `itemFeatures.${type}`;
await this.settings.updateSource({
[`${path}.-=${id}`]: null
[`${path}.${id}`]: _del
});
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
@ -322,7 +322,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
const fields = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).schema.fields;
const removeUpdate = Object.keys(this.settings.restMoves[target.dataset.type].moves).reduce((acc, key) => {
acc[`-=${key}`] = null;
acc[key] = _del;
return acc;
}, {});
@ -382,7 +382,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
[`itemFeatures.${target.dataset.type}`]: Object.keys(
this.settings.itemFeatures[target.dataset.type]
).reduce((acc, key) => {
acc[`-=${key}`] = null;
acc[key] = _del;
return acc;
}, {})
@ -455,12 +455,12 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
if (!confirmed) return;
await this.settings.updateSource({
[`domains.-=${this.selected.domain}`]: null
[`domains.${this.selected.domain}`]: _del
});
const currentSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew);
if (currentSettings.domains[this.selected.domain]) {
await currentSettings.updateSource({ [`domains.-=${this.selected.domain}`]: null });
await currentSettings.updateSource({ [`domains.${this.selected.domain}`]: _del });
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, currentSettings);
}
@ -507,7 +507,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
static async deleteAdversaryType(_, target) {
const { key } = target.dataset;
await this.settings.updateSource({ [`adversaryTypes.-=${key}`]: null });
await this.settings.updateSource({ [`adversaryTypes.${key}`]: _del });
this.selected.adversaryType = this.selected.adversaryType === key ? null : this.selected.adversaryType;
this.render();
@ -563,7 +563,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
const { actorType, resourceKey } = target.dataset;
await this.settings.updateSource({
[`resources.${actorType}.resources.-=${resourceKey}`]: null
[`resources.${actorType}.resources.${resourceKey}`]: _del
});
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());

View file

@ -1,3 +1,4 @@
import { getUnusedDamageTypes } from '../../helpers/utils.mjs';
import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs';
const { ApplicationV2 } = foundry.applications.api;
@ -104,7 +105,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
}
};
static CLEAN_ARRAYS = ['damage.parts', 'cost', 'effects', 'summon'];
static CLEAN_ARRAYS = ['cost', 'effects', 'summon'];
_getTabs(tabs) {
for (const v of Object.values(tabs)) {
@ -172,6 +173,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
revealed: this.openTrigger === index
};
});
context.allDamageTypesUsed = !getUnusedDamageTypes(this.action.damage.parts).length;
const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers;
context.tierOptions = [
@ -291,18 +293,61 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2)
static addDamage(_event) {
if (!this.action.damage.parts) return;
const data = this.action.toObject(),
part = {};
if (this.action.actor?.isNPC) part.value = { multiplier: 'flat' };
data.damage.parts.push(part);
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
const choices = getUnusedDamageTypes(this.action.damage.parts);
const content = new foundry.data.fields.StringField({
label: game.i18n.localize('Damage Type'),
choices,
required: true
}).toFormGroup(
{},
{
name: 'type',
localize: true,
nameAttr: 'value',
labelAttr: 'label'
}
).outerHTML;
const callback = (_, button) => {
const data = this.action.toObject();
const type = choices[button.form.elements.type.value].value;
const part = { applyTo: type };
if (this.action.actor?.isNPC) part.value = { multiplier: 'flat' };
data.damage.parts[type] = part;
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
};
const typeDialog = new foundry.applications.api.DialogV2({
buttons: [
foundry.utils.mergeObject(
{
action: 'ok',
label: 'Confirm',
icon: 'fas fa-check',
default: true
},
{ callback: callback }
)
],
content: content,
rejectClose: false,
modal: false,
window: {
title: game.i18n.localize('Add Damage')
},
position: { width: 300 }
});
typeDialog.render(true);
}
static removeDamage(_event, button) {
if (!this.action.damage.parts) return;
const data = this.action.toObject(),
index = button.dataset.index;
data.damage.parts.splice(index, 1);
const data = this.action.toObject();
const key = button.dataset.key;
delete data.damage.parts[key];
data.damage.parts[`${key}`] = _del;
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
}

View file

@ -18,6 +18,7 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
settings: { template: 'systems/daggerheart/templates/sheets/activeEffect/settings.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' }
@ -173,8 +174,77 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
}));
}
break;
case 'settings':
const groups = {
time: _loc('EFFECT.DURATION.UNITS.GROUPS.time'),
combat: _loc('EFFECT.DURATION.UNITS.GROUPS.combat')
};
partContext.durationUnits = CONST.ACTIVE_EFFECT_DURATION_UNITS.map(value => ({
value,
label: _loc(`EFFECT.DURATION.UNITS.${value}`),
group: CONST.ACTIVE_EFFECT_TIME_DURATION_UNITS.includes(value) ? groups.time : groups.combat
}));
break;
case 'changes':
const fields = this.document.system.schema.fields.changes.element.fields;
partContext.changes = await Promise.all(
foundry.utils
.deepClone(context.source.changes)
.map((c, i) => this._prepareChangeContext(c, i, fields))
);
break;
}
return partContext;
}
_prepareChangeContext(change, index, fields) {
if (typeof change.value !== 'string') change.value = JSON.stringify(change.value);
const defaultPriority = game.system.api.documents.DhActiveEffect.CHANGE_TYPES[change.type]?.defaultPriority;
Object.assign(
change,
['key', 'type', 'value', 'priority'].reduce((paths, fieldName) => {
paths[`${fieldName}Path`] = `system.changes.${index}.${fieldName}`;
return paths;
}, {})
);
return (
game.system.api.documents.DhActiveEffect.CHANGE_TYPES[change.type].render?.(
change,
index,
defaultPriority
) ??
foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/sheets/activeEffect/change.hbs',
{
change,
index,
defaultPriority,
fields
}
)
);
}
/** @inheritDoc */
_onChangeForm(_formConfig, event) {
if (foundry.utils.isElementInstanceOf(event.target, 'select') && event.target.name === 'system.duration.type') {
const durationSection = this.element.querySelector('.custom-duration-section');
if (event.target.value === 'custom') durationSection.classList.add('visible');
else durationSection.classList.remove('visible');
const durationDescription = this.element.querySelector('.duration-description');
if (event.target.value === 'temporary') durationDescription.classList.add('visible');
else durationDescription.classList.remove('visible');
}
}
/** @inheritDoc */
_processFormData(event, form, formData) {
const submitData = super._processFormData(event, form, formData);
if (submitData.start && !submitData.start.time) submitData.start.time = '0';
else if (!submitData) submitData.start = null;
return submitData;
}
}

View file

@ -95,7 +95,7 @@ export default class DHAdversarySettings extends DHBaseActorSettings {
});
if (!confirmed) return;
await this.actor.update({ [`system.experiences.-=${target.dataset.experience}`]: null });
await this.actor.update({ [`system.experiences.${target.dataset.experience}`]: _del });
}
async _onDragStart(event) {

View file

@ -101,8 +101,8 @@ export default class DHCharacterSettings extends DHBaseActorSettings {
if (relinkAchievementData.length > 0) {
relinkAchievementData.forEach(data => {
updates[`system.levelData.levelups.${data.levelKey}.achievements.experiences.-=${data.experience}`] =
null;
updates[`system.levelData.levelups.${data.levelKey}.achievements.experiences.${data.experience}`] =
_del;
});
} else if (relinkSelectionData.length > 0) {
relinkSelectionData.forEach(data => {
@ -137,7 +137,7 @@ export default class DHCharacterSettings extends DHBaseActorSettings {
await this.actor.update({
...updates,
[`system.experiences.-=${target.dataset.experience}`]: null
[`system.experiences.${target.dataset.experience}`]: _del
});
}
}

View file

@ -117,6 +117,6 @@ export default class DHCompanionSettings extends DHBaseActorSettings {
});
if (!confirmed) return;
await this.actor.update({ [`system.experiences.-=${target.dataset.experience}`]: null });
await this.actor.update({ [`system.experiences.${target.dataset.experience}`]: _del });
}
}

View file

@ -68,9 +68,9 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings {
*/
static async #addCategory() {
await this.actor.update({
[`system.potentialAdversaries.${foundry.utils.randomID()}.label`]: game.i18n.localize(
'DAGGERHEART.ACTORS.Environment.newAdversary'
)
[`system.potentialAdversaries.${foundry.utils.randomID()}`]: {
label: game.i18n.localize('DAGGERHEART.ACTORS.Environment.newAdversary')
}
});
}
@ -79,7 +79,7 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings {
* @type {ApplicationClickAction}
*/
static async #removeCategory(_, target) {
await this.actor.update({ [`system.potentialAdversaries.-=${target.dataset.categoryId}`]: null });
await this.actor.update({ [`system.potentialAdversaries.${target.dataset.categoryId}`]: _del });
}
/**
@ -138,4 +138,8 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings {
this.render();
}
}
async _onDropItem(event, item) {
console.log(item);
}
}

View file

@ -205,7 +205,7 @@ export default class SettingFeatureConfig extends HandlebarsApplicationMixin(App
}
});
} else {
await this.updateMove({ [`${this.actionsPath}.-=${target.dataset.id}`]: null });
await this.updateMove({ [`${this.actionsPath}.${target.dataset.id}`]: _del });
}
this.render();

View file

@ -67,9 +67,9 @@ export default function DHTokenConfigMixin(Base) {
changes.height = tokenSize;
}
const deletions = { '-=actorId': null, '-=actorLink': null };
const deletions = { actorId: _del, actorLink: _del };
const mergeOptions = { inplace: false, performDeletions: true };
this._preview.updateSource(mergeObject(changes, deletions, mergeOptions));
this._preview.updateSource(foundry.utils.mergeObject(changes, deletions, mergeOptions));
if (this._preview?.object?.destroyed === false) {
this._preview.object.initializeSources();

View file

@ -72,19 +72,16 @@ const typeSettingsMap = {
*/
export default function DHApplicationMixin(Base) {
class DHSheetV2 extends HandlebarsApplicationMixin(Base) {
#nonHeaderAttribution = ['environment', 'ancestry', 'community', 'domainCard'];
/**
* @param {DHSheetV2Configuration} [options={}]
*/
constructor(options = {}) {
super(options);
/**
* @type {foundry.applications.ux.DragDrop[]}
* @private
*/
this._dragDrop = this._createDragDropHandlers();
}
#nonHeaderAttribution = ['environment', 'ancestry', 'community', 'domainCard'];
this._setupDragDrop();
}
/**
* The default options for the sheet.
@ -177,7 +174,9 @@ export default function DHApplicationMixin(Base) {
/**@inheritdoc */
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
this._dragDrop.forEach(d => d.bind(htmlElement));
/* Core dragDrop from ActorDocument is always only 1. Possible we could refactor our own */
if (Array.isArray(this._dragDrop)) this._dragDrop.forEach(d => d.bind(htmlElement));
// Handle delta inputs
for (const deltaInput of htmlElement.querySelectorAll('input[data-allow-delta]')) {
@ -355,14 +354,19 @@ export default function DHApplicationMixin(Base) {
* @returns {foundry.applications.ux.DragDrop[]}
* @private
*/
_createDragDropHandlers() {
return this.options.dragDrop.map(d => {
d.callbacks = {
dragstart: this._onDragStart.bind(this),
drop: this._onDrop.bind(this)
};
return new foundry.applications.ux.DragDrop.implementation(d);
});
_setupDragDrop() {
if (this._dragDrop) {
this._dragDrop.callbacks.dragStart = this._onDragStart;
this._dragDrop.callback.drop = this._onDrop;
} else {
this._dragDrop = this.options.dragDrop.map(d => {
d.callbacks = {
dragstart: this._onDragStart.bind(this),
drop: this._onDrop.bind(this)
};
return new foundry.applications.ux.DragDrop.implementation(d);
});
}
}
/**
@ -499,7 +503,10 @@ export default function DHApplicationMixin(Base) {
icon: 'fa-solid fa-explosion',
condition: target => {
const doc = getDocFromElementSync(target);
return doc?.system?.attack?.damage.parts.length || doc?.damage?.parts.length;
return (
!foundry.utils.isEmpty(doc?.system?.attack?.damage.parts) ||
!foundry.utils.isEmpty(doc?.damage?.parts)
);
},
callback: async (target, event) => {
const doc = await getDocFromElement(target),

View file

@ -102,7 +102,7 @@ export default class BeastformSheet extends DHBaseItemSheet {
async advantageOnRemove(event) {
await this.document.update({
[`system.advantageOn.-=${event.detail.data.value}`]: null
[`system.advantageOn.${event.detail.data.value}`]: _del
});
}
}

View file

@ -108,14 +108,15 @@ export default class DhRollTableSheet extends foundry.applications.sheets.RollTa
getSystemFlagUpdate() {
const deleteUpdate = Object.keys(this.document._source.flags.daggerheart?.altFormula ?? {}).reduce(
(acc, formulaKey) => {
if (!this.daggerheartFlag.altFormula[formulaKey]) acc.altFormula[`-=${formulaKey}`] = null;
if (!this.daggerheartFlag.altFormula[formulaKey]) acc.altFormula[formulaKey] = _del;
return acc;
},
{ altFormula: {} }
);
return { ['flags.daggerheart']: foundry.utils.mergeObject(this.daggerheartFlag.toObject(), deleteUpdate) };
const flagData = this.daggerheartFlag.toObject();
return { ...flagData, altFormula: { ...flagData.altFormula, ...deleteUpdate.altFormula } };
}
static async #addFormula() {
@ -127,7 +128,7 @@ export default class DhRollTableSheet extends foundry.applications.sheets.RollTa
static async #removeFormula(_event, target) {
await this.daggerheartFlag.updateSource({
[`altFormula.-=${target.dataset.key}`]: null
[`altFormula.${target.dataset.key}`]: _del
});
this.render({ internalRefresh: true });
}

View file

@ -1,52 +1,19 @@
export default class DhSidebar extends foundry.applications.sidebar.Sidebar {
static buildTabs() {
const { settings, ...tabs } = super.TABS;
return {
...tabs,
daggerheartMenu: {
tooltip: 'DAGGERHEART.UI.Sidebar.daggerheartMenu.title',
img: 'systems/daggerheart/assets/logos/FoundryBorneLogoWhite.svg',
gmOnly: true
},
settings
};
}
/** @override */
static TABS = {
chat: {
documentName: 'ChatMessage'
},
combat: {
documentName: 'Combat'
},
scenes: {
documentName: 'Scene',
gmOnly: true
},
actors: {
documentName: 'Actor'
},
items: {
documentName: 'Item'
},
journal: {
documentName: 'JournalEntry',
tooltip: 'SIDEBAR.TabJournal'
},
tables: {
documentName: 'RollTable'
},
cards: {
documentName: 'Cards'
},
macros: {
documentName: 'Macro'
},
playlists: {
documentName: 'Playlist'
},
compendium: {
tooltip: 'SIDEBAR.TabCompendium',
icon: 'fa-solid fa-book-atlas'
},
daggerheartMenu: {
tooltip: 'DAGGERHEART.UI.Sidebar.daggerheartMenu.title',
img: 'systems/daggerheart/assets/logos/FoundryBorneLogoWhite.svg',
gmOnly: true
},
settings: {
tooltip: 'SIDEBAR.TabSettings',
icon: 'fa-solid fa-gears'
}
};
static TABS = DhSidebar.buildTabs();
/** @override */
static PARTS = {

View file

@ -1,4 +1,5 @@
import { AdversaryBPPerEncounter } from '../../config/encounterConfig.mjs';
import { expireActiveEffects } from '../../helpers/utils.mjs';
export default class DhCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
static DEFAULT_OPTIONS = {
@ -177,6 +178,8 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
if (autoPoints) {
update.system.actionTokens = Math.max(combatant.system.actionTokens - 1, 0);
}
if (combatant.actor) expireActiveEffects(combatant.actor, [CONFIG.DH.GENERAL.activeEffectDurations.act.id]);
}
await this.viewed.update({

View file

@ -233,6 +233,6 @@ export default class CountdownEdit extends HandlebarsApplicationMixin(Applicatio
}
if (this.editingCountdowns.has(countdownId)) this.editingCountdowns.delete(countdownId);
this.updateSetting({ [`countdowns.-=${countdownId}`]: null });
this.updateSetting({ [`countdowns.${countdownId}`]: _del });
}
}

View file

@ -52,10 +52,6 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
}
};
get element() {
return document.body.querySelector('.daggerheart.dh-style.countdowns');
}
/**@inheritdoc */
async _renderFrame(options) {
const frame = await super._renderFrame(options);
@ -68,6 +64,7 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
const header = frame.querySelector('.window-header');
header.querySelector('button[data-action="close"]').remove();
header.querySelector('button[data-action="toggleControls"]').remove();
if (game.user.isGM) {
const editTooltip = game.i18n.localize('DAGGERHEART.APPLICATIONS.CountdownEdit.editTitle');
@ -278,10 +275,8 @@ export default class DhCountdowns extends HandlebarsApplicationMixin(Application
return acc;
}, {})
};
await emitAsGM(GMUpdateEvent.UpdateCountdowns,
DhCountdowns.gmSetSetting.bind(settings),
settings, null, {
refreshType: RefreshType.Countdown
await emitAsGM(GMUpdateEvent.UpdateCountdowns, DhCountdowns.gmSetSetting.bind(settings), settings, null, {
refreshType: RefreshType.Countdown
});
}

View file

@ -1,3 +1,4 @@
import { getIconVisibleActiveEffects } from '../../helpers/utils.mjs';
import { RefreshType } from '../../systemRegistration/socket.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
@ -72,7 +73,7 @@ export default class DhEffectsDisplay extends HandlebarsApplicationMixin(Applica
? game.user.character
: null
: canvas.tokens.controlled[0].actor;
return actor?.getActiveEffects() ?? [];
return getIconVisibleActiveEffects(actor?.getActiveEffects() ?? []);
};
toggleHidden(token, focused) {

View file

@ -251,10 +251,10 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) {
/* If any noticeable slowdown occurs, consider replacing with enriching description on clicking to expand descriptions */
for (const item of this.items) {
if (["weapon", "armor"].includes(item.type)) {
if (['weapon', 'armor'].includes(item.type)) {
item.system.enrichedTags = await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/sheets/global/partials/item-tags.hbs',
item.system,
item.system
);
}
item.system.enrichedDescription =

View file

@ -31,7 +31,7 @@ export default class DhSceneNavigation extends foundry.applications.ui.SceneNavi
const environments = daggerheartInfo.sceneEnvironments.filter(
x => x && x.testUserPermission(game.user, 'LIMITED')
);
const hasEnvironments = environments.length > 0 && x.isView;
const hasEnvironments = environments.length > 0 && x.active;
return {
...x,
hasEnvironments,
@ -39,9 +39,10 @@ export default class DhSceneNavigation extends foundry.applications.ui.SceneNavi
environments: environments
};
});
context.scenes.active = extendScenes(context.scenes.active);
context.scenes.inactive = extendScenes(context.scenes.inactive);
context.scenes.viewed = context.scenes.viewed ? extendScenes([context.scenes.viewed])[0] : null;
return context;
}

View file

@ -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