Merged main

This commit is contained in:
WBHarry 2026-03-13 00:25:50 +01:00
commit b87e630a0a
84 changed files with 2046 additions and 452 deletions

View file

@ -1,4 +1,5 @@
export { default as DhAppearanceSettings } from './appearanceSettings.mjs';
export { default as DhAutomationSettings } from './automationSettings.mjs';
export { default as DhHomebrewSettings } from './homebrewSettings.mjs';
export { default as DhMetagamingSettings } from './metagamingSettings.mjs';
export { default as DhVariantRuleSettings } from './variantRuleSettings.mjs';

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

@ -31,8 +31,8 @@ export default class DhAutomationSettings extends HandlebarsApplicationMixin(App
};
static PARTS = {
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
header: { template: 'systems/daggerheart/templates/settings/automation-settings/header.hbs' },
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
general: { template: 'systems/daggerheart/templates/settings/automation-settings/general.hbs' },
rules: { template: 'systems/daggerheart/templates/settings/automation-settings/deathMoves.hbs' },
roll: { template: 'systems/daggerheart/templates/settings/automation-settings/roll.hbs' },

View file

@ -1,4 +1,5 @@
import { DhHomebrew } from '../../data/settings/_module.mjs';
import { Resource } from '../../data/settings/Homebrew.mjs';
import { slugify } from '../../helpers/utils.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
@ -44,6 +45,9 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
addAdversaryType: this.addAdversaryType,
deleteAdversaryType: this.deleteAdversaryType,
selectAdversaryType: this.selectAdversaryType,
addResource: this.addResource,
removeResource: this.removeResource,
resetResourceImage: this.resetResourceImage,
save: this.save,
resetTokenSizes: this.resetTokenSizes,
reset: this.reset
@ -56,6 +60,10 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
settings: { template: 'systems/daggerheart/templates/settings/homebrew-settings/settings.hbs' },
domains: { template: 'systems/daggerheart/templates/settings/homebrew-settings/domains.hbs' },
types: { template: 'systems/daggerheart/templates/settings/homebrew-settings/types.hbs' },
resources: {
template: 'systems/daggerheart/templates/settings/homebrew-settings/resources.hbs',
scrollable: ['.resource-types-container']
},
itemTypes: { template: 'systems/daggerheart/templates/settings/homebrew-settings/itemFeatures.hbs' },
downtime: { template: 'systems/daggerheart/templates/settings/homebrew-settings/downtime.hbs' },
footer: { template: 'systems/daggerheart/templates/settings/homebrew-settings/footer.hbs' }
@ -64,7 +72,14 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
/** @inheritdoc */
static TABS = {
main: {
tabs: [{ id: 'settings' }, { id: 'domains' }, { id: 'types' }, { id: 'itemFeatures' }, { id: 'downtime' }],
tabs: [
{ id: 'settings' },
{ id: 'domains' },
{ id: 'types' },
{ id: 'resources' },
{ id: 'itemFeatures' },
{ id: 'downtime' }
],
initial: 'settings',
labelPrefix: 'DAGGERHEART.GENERAL.Tabs'
}
@ -77,9 +92,17 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
this.render();
}
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
for (const element of htmlElement.querySelectorAll('.path-field input'))
element.addEventListener('change', this.toggleResourceIsIcon.bind(this));
}
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.settingFields = this.settings;
context.schemaFields = context.settingFields.schema.fields;
return context;
}
@ -103,6 +126,8 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
? { id: this.selected.adversaryType, ...this.settings.adversaryTypes[this.selected.adversaryType] }
: null;
break;
case 'resources':
break;
case 'downtime':
context.restOptions = {
shortRest: CONFIG.DH.GENERAL.defaultRestOptions.shortRest(),
@ -124,6 +149,33 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
this.render();
}
async toggleResourceIsIcon(event) {
const element = event.target.closest('.resource-icon-container');
const { actorType, resourceKey, imageKey } = element.dataset;
const current = this.settings.resources[actorType].resources[resourceKey].images[imageKey];
await this.settings.updateSource({
[`resources.${actorType}.resources.${resourceKey}.images.${imageKey}`]: {
isIcon: !current.isIcon,
value: ''
}
});
this.render();
}
static async resetResourceImage(_event, button) {
const element = button.closest('.resource-icon-container');
const { actorType, resourceKey, imageKey } = element.dataset;
await this.settings.updateSource({
[`resources.${actorType}.resources.${resourceKey}.images.${imageKey}`]:
Resource.getDefaultImageData(imageKey)
});
this.render();
}
static async changeCurrencyIcon(_, target) {
const type = target.dataset.currency;
const currentIcon = this.settings.currency[type].icon;
@ -466,6 +518,58 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
this.render();
}
static async addResource(_, target) {
const { actorType } = target.dataset;
const content = new foundry.data.fields.StringField({
label: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.resources.resourceIdentifier'),
required: true
}).toFormGroup({}, { name: 'identifier', localize: true }).outerHTML;
async function callback(_, button) {
const identifier = button.form.elements.identifier.value;
if (!identifier) return;
const sluggedIdentifier = slugify(identifier);
await this.settings.updateSource({
[`resources.${actorType}.resources.${sluggedIdentifier}`]: Resource.getDefaultResourceData(identifier)
});
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
this.render();
}
await foundry.applications.api.DialogV2.prompt({
content: content,
rejectClose: false,
modal: true,
ok: { callback: callback.bind(this) },
window: {
title: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.resources.setResourceIdentifier')
},
position: { width: 400 }
});
}
static async removeResource(_, target) {
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
title: game.i18n.localize(`DAGGERHEART.SETTINGS.Homebrew.deleteResourceTitle`)
},
content: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.deleteResourceText')
});
if (!confirmed) return;
const { actorType, resourceKey } = target.dataset;
await this.settings.updateSource({
[`resources.${actorType}.resources.${resourceKey}`]: _del
});
game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
this.render();
}
static async save() {
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew, this.settings.toObject());
this.close();

View file

@ -0,0 +1,62 @@
import { DhMetagaming } from '../../data/settings/_module.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
export default class DhMetagamingSettings extends HandlebarsApplicationMixin(ApplicationV2) {
constructor() {
super({});
this.settings = new DhMetagaming(
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming).toObject()
);
}
get title() {
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.title');
}
static DEFAULT_OPTIONS = {
tag: 'form',
id: 'daggerheart-metagaming-settings',
classes: ['daggerheart', 'dh-style', 'dialog', 'setting'],
position: { width: '600', height: 'auto' },
window: {
icon: 'fa-solid fa-eye-low-vision'
},
actions: {
reset: this.reset,
save: this.save
},
form: { handler: this.updateData, submitOnChange: true }
};
static PARTS = {
header: { template: 'systems/daggerheart/templates/settings/metagaming-settings/header.hbs' },
general: { template: 'systems/daggerheart/templates/settings/metagaming-settings/general.hbs' },
footer: { template: 'systems/daggerheart/templates/settings/metagaming-settings/footer.hbs' }
};
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.settingFields = this.settings;
return context;
}
static async updateData(_event, _element, formData) {
const updatedSettings = foundry.utils.expandObject(formData.object);
await this.settings.updateSource(updatedSettings);
this.render();
}
static async reset() {
this.settings = new DhMetagaming();
this.render();
}
static async save() {
await game.settings.set(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Metagaming, this.settings.toObject());
this.close();
}
}