diff --git a/lang/en.json b/lang/en.json index 29a444f7..81cb2d49 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1472,6 +1472,7 @@ "Appearance": { "FIELDS": { "displayFear": { "label": "Fear Display" }, + "dualityColorScheme": { "label": "Chat Style" }, "showGenericStatusEffects": { "label": "Show Foundry Status Effects" } }, "fearDisplay": { @@ -1552,7 +1553,7 @@ "duality": "Duality Rolls", "diceSoNice": { "title": "Dice So Nice", - "hint": "Coloration of Hope and Fear dice if the Dice So Nice module is used.", + "hint": "Coloration of Duality dice if the Dice So Nice module is used.", "foreground": "Foreground", "background": "Background", "outline": "Outline", diff --git a/module/applications/dialogs/downtime.mjs b/module/applications/dialogs/downtime.mjs index 163af03e..d0da7d72 100644 --- a/module/applications/dialogs/downtime.mjs +++ b/module/applications/dialogs/downtime.mjs @@ -64,8 +64,8 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV context.selectedActivity = this.selectedActivity; context.moveData = this.moveData; - const shortRestMovesSelected = this.#nrSelectedMoves('shortRest'); - const longRestMovesSelected = this.#nrSelectedMoves('longRest'); + const shortRestMovesSelected = this.nrSelectedMoves('shortRest'); + const longRestMovesSelected = this.nrSelectedMoves('longRest'); context.nrChoices = { ...this.nrChoices, shortRest: { @@ -89,7 +89,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV static selectMove(_, target) { const { category, move } = target.dataset; - const nrSelected = this.#nrSelectedMoves(category); + const nrSelected = this.nrSelectedMoves(category); if (nrSelected + this.nrChoices[category].taken >= this.nrChoices[category].max) { ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noMoreMoves')); @@ -176,7 +176,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV this.render(); } - #nrSelectedMoves(category) { + nrSelectedMoves(category) { return Object.values(this.moveData[category].moves).reduce((acc, x) => acc + (x.selected ?? 0), 0); } } diff --git a/module/applications/settings/appearanceSettings.mjs b/module/applications/settings/appearanceSettings.mjs index 78f067c7..a03bef9a 100644 --- a/module/applications/settings/appearanceSettings.mjs +++ b/module/applications/settings/appearanceSettings.mjs @@ -36,10 +36,37 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App } }; + /** @inheritdoc */ + static TABS = { + diceSoNice: { + tabs: [ + { 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' } + ], + initial: 'hope' + } + }; + + changeTab(tab, group, options) { + super.changeTab(tab, group, options); + + this.render(); + } + async _prepareContext(_options) { const context = await super._prepareContext(_options); context.settingFields = this.settings; + context.diceSoNiceTextures = game.dice3d?.exports?.TEXTURELIST ?? {}; + context.diceSoNiceColorsets = game.dice3d?.exports?.COLORSETS ?? {}; + context.diceTab = { + key: this.tabGroups.diceSoNice, + source: this.settings._source.diceSoNice[this.tabGroups.diceSoNice], + fields: this.settings.schema.fields.diceSoNice.fields[this.tabGroups.diceSoNice].fields + }; + return context; } @@ -65,4 +92,13 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App this.close(); } + + _getTabs(tabs) { + for (const v of Object.values(tabs)) { + v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active; + v.cssClass = v.active ? 'active' : ''; + } + + return tabs; + } } diff --git a/module/applications/settings/components/settingsActionsView.mjs b/module/applications/settings/components/settingsActionsView.mjs deleted file mode 100644 index f77c5fce..00000000 --- a/module/applications/settings/components/settingsActionsView.mjs +++ /dev/null @@ -1,148 +0,0 @@ -import { actionsTypes } from '../../../data/action/_module.mjs'; -import DHActionConfig from '../../sheets-configs/action-config.mjs'; - -const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; - -export default class DhSettingsActionView extends HandlebarsApplicationMixin(ApplicationV2) { - constructor(resolve, reject, title, name, icon, img, description, actions) { - super({}); - - this.resolve = resolve; - this.reject = reject; - this.viewTitle = title; - this.name = name; - this.icon = icon; - this.img = img; - this.description = description; - this.actions = actions; - } - - get title() { - return this.viewTitle; - } - - static DEFAULT_OPTIONS = { - tag: 'form', - classes: ['daggerheart', 'setting', 'dh-style'], - position: { width: 440, height: 'auto' }, - actions: { - editImage: this.onEditImage, - addItem: this.addItem, - editItem: this.editItem, - removeItem: this.removeItem, - resetMoves: this.resetMoves, - saveForm: this.saveForm - }, - form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false } - }; - - static PARTS = { - header: { template: 'systems/daggerheart/templates/settings/components/action-view-header.hbs' }, - main: { - template: 'systems/daggerheart/templates/settings/components/action-view.hbs' - }, - footer: { template: 'systems/daggerheart/templates/settings/components/action-view-footer.hbs' } - }; - - async _prepareContext(_options) { - const context = await super._prepareContext(_options); - context.name = this.name; - context.icon = this.icon; - context.img = this.img; - context.description = this.description; - context.enrichedDescription = await foundry.applications.ux.TextEditor.enrichHTML(context.description); - context.actions = this.actions; - - return context; - } - - static async updateData(event, element, formData) { - const { name, icon, description } = foundry.utils.expandObject(formData.object); - this.name = name; - this.icon = icon; - this.description = description; - - this.render(); - } - - static async saveForm(event) { - this.resolve({ - name: this.name, - icon: this.icon, - img: this.img, - description: this.description, - actions: this.actions - }); - this.close(true); - } - - static close(fromSave) { - if (!fromSave) { - this.reject(); - } - - super.close(); - } - - static onEditImage() { - const fp = new foundry.applications.apps.FilePicker.implementation({ - current: this.img, - type: 'image', - callback: async path => { - this.img = path; - this.render(); - }, - top: this.position.top + 40, - left: this.position.left + 10 - }); - return fp.browse(); - } - - async selectActionType() { - const content = await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/actionTypes/actionType.hbs', - { types: CONFIG.DH.ACTIONS.actionTypes } - ), - title = 'Select Action Type', - type = 'form', - data = {}; - return Dialog.prompt({ - title, - label: title, - content, - type, - callback: html => { - const form = html[0].querySelector('form'), - fd = new foundry.applications.ux.FormDataExtended(form); - foundry.utils.mergeObject(data, fd.object, { inplace: true }); - return data; - }, - rejectClose: false - }); - } - - static async addItem() { - const actionType = await this.selectActionType(); - const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack, - action = new cls({ - _id: foundry.utils.randomID(), - type: actionType.type, - name: game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[actionType.type].name), - ...cls.getSourceConfig(this.document) - }); - - this.actions.push(action); - this.render(); - } - - static async editItem(_, button) { - await new DHActionConfig(this.actions[button.dataset.id]).render(true); - } - - static removeItem(event, button) { - this.actions = this.actions.filter((_, index) => index !== Number.parseInt(button.dataset.id)); - this.render(); - } - - static resetMoves() {} -} diff --git a/module/applications/settings/homebrewSettings.mjs b/module/applications/settings/homebrewSettings.mjs index d2861595..2aa2660e 100644 --- a/module/applications/settings/homebrewSettings.mjs +++ b/module/applications/settings/homebrewSettings.mjs @@ -1,6 +1,4 @@ import { DhHomebrew } from '../../data/settings/_module.mjs'; -import DhSettingsActionView from './components/settingsActionsView.mjs'; - const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; export default class DhHomebrewSettings extends HandlebarsApplicationMixin(ApplicationV2) { @@ -73,23 +71,21 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli static async editItem(_, target) { const move = this.settings.restMoves[target.dataset.type].moves[target.dataset.id]; - new Promise((resolve, reject) => { - new DhSettingsActionView( - resolve, - reject, - game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.downtimeMoves'), - move.name, - move.icon, - move.img, - move.description, - move.actions - ).render(true); - }).then(data => this.updateAction.bind(this)(data, target.dataset.type, target.dataset.id)); + const path = `restMoves.${target.dataset.type}.moves.${target.dataset.id}`; + const editedMove = await game.system.api.applications.sheetConfigs.DowntimeConfig.configure( + move, + path, + this.settings + ); + if (!editedMove) return; + + await this.updateAction.bind(this)(editedMove, target.dataset.type, target.dataset.id); } async updateAction(data, type, id) { await this.settings.updateSource({ [`restMoves.${type}.moves.${id}`]: { + actions: data.actions, name: data.name, icon: data.icon, img: data.img, @@ -139,7 +135,11 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli acc[key] = { ...move, name: game.i18n.localize(move.name), - description: game.i18n.localize(move.description) + description: game.i18n.localize(move.description), + actions: move.actions.map(action => ({ + ...action, + name: game.i18n.localize(action.name) + })) }; return acc; diff --git a/module/applications/sheets-configs/_module.mjs b/module/applications/sheets-configs/_module.mjs index fafb1fcf..49cc74b0 100644 --- a/module/applications/sheets-configs/_module.mjs +++ b/module/applications/sheets-configs/_module.mjs @@ -1,6 +1,7 @@ export { default as ActionConfig } from './action-config.mjs'; export { default as AdversarySettings } from './adversary-settings.mjs'; export { default as CompanionSettings } from './companion-settings.mjs'; +export { default as DowntimeConfig } from './downtimeConfig.mjs'; export { default as EnvironmentSettings } from './environment-settings.mjs'; export { default as ActiveEffectConfig } from './activeEffectConfig.mjs'; export { default as DhTokenConfig } from './token-config.mjs'; diff --git a/module/applications/sheets-configs/action-config.mjs b/module/applications/sheets-configs/action-config.mjs index 92aa9dcd..070db23f 100644 --- a/module/applications/sheets-configs/action-config.mjs +++ b/module/applications/sheets-configs/action-config.mjs @@ -2,10 +2,11 @@ import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs'; const { ApplicationV2 } = foundry.applications.api; export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { - constructor(action) { + constructor(action, sheetUpdate) { super({}); this.action = action; + this.sheetUpdate = sheetUpdate; this.openSection = null; } @@ -171,6 +172,8 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { const submitData = this._prepareSubmitData(event, formData), data = foundry.utils.mergeObject(this.action.toObject(), submitData); this.action = await this.action.update(data); + + this.sheetUpdate?.(this.action); this.render(); } @@ -195,7 +198,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { if (!this.action.damage.parts) return; const data = this.action.toObject(), part = {}; - if(this.action.actor?.isNPC) part.value = { multiplier: 'flat' }; + 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) }); } diff --git a/module/applications/sheets-configs/downtimeConfig.mjs b/module/applications/sheets-configs/downtimeConfig.mjs new file mode 100644 index 00000000..80aab900 --- /dev/null +++ b/module/applications/sheets-configs/downtimeConfig.mjs @@ -0,0 +1,162 @@ +import { actionsTypes } from '../../data/action/_module.mjs'; +import DHActionConfig from './action-config.mjs'; + +const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; + +export default class DowntimeConfig extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(move, movePath, settings, options) { + super(options); + + this.move = move; + + this.movePath = movePath; + this.actionsPath = `${movePath}.actions`; + this.settings = settings; + } + + get title() { + return game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.downtimeMoves'); + } + + static DEFAULT_OPTIONS = { + tag: 'form', + classes: ['daggerheart', 'setting', 'dh-style'], + position: { width: 440, height: 'auto' }, + window: { + icon: 'fa-solid fa-gears' + }, + actions: { + editImage: this.onEditImage, + addItem: this.addItem, + editItem: this.editItem, + removeItem: this.removeItem, + resetMoves: this.resetMoves, + saveForm: this.saveForm + }, + form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false } + }; + + static PARTS = { + header: { template: 'systems/daggerheart/templates/settings/downtime-config/header.hbs' }, + tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, + main: { template: 'systems/daggerheart/templates/settings/downtime-config/main.hbs' }, + actions: { template: 'systems/daggerheart/templates/settings/downtime-config/actions.hbs' }, + footer: { template: 'systems/daggerheart/templates/settings/downtime-config/footer.hbs' } + }; + + /** @inheritdoc */ + static TABS = { + primary: { + tabs: [{ id: 'main' }, { id: 'actions' }], + initial: 'main', + labelPrefix: 'DAGGERHEART.GENERAL.Tabs' + } + }; + + async _prepareContext(_options) { + const context = await super._prepareContext(_options); + context.move = this.move; + context.move.enrichedDescription = await foundry.applications.ux.TextEditor.enrichHTML( + context.move.description + ); + + return context; + } + + static async updateData(event, element, formData) { + const data = foundry.utils.expandObject(formData.object); + foundry.utils.mergeObject(this.move, data); + + this.render(); + } + + static async saveForm() { + this.close({ submitted: true }); + } + + static onEditImage() { + const fp = new foundry.applications.apps.FilePicker.implementation({ + current: this.img, + type: 'image', + callback: async path => { + this.move.img = path; + this.render(); + }, + top: this.position.top + 40, + left: this.position.left + 10 + }); + return fp.browse(); + } + + async selectActionType() { + return ( + (await foundry.applications.api.DialogV2.input({ + window: { title: game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType') }, + content: await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/actionTypes/actionType.hbs', + { types: CONFIG.DH.ACTIONS.actionTypes } + ), + ok: { + label: game.i18n.format('DOCUMENT.Create', { + type: game.i18n.localize('DAGGERHEART.GENERAL.Action.single') + }) + } + })) ?? {} + ); + } + + static async addItem() { + const { type: actionType } = await this.selectActionType(); + if (!actionType) return; + + const cls = actionsTypes[actionType] ?? actionsTypes.attack, + action = new cls( + { + type: actionType, + name: game.i18n.localize(CONFIG.DH.ACTIONS.actionTypes[actionType].name), + img: 'icons/magic/life/cross-worn-green.webp', + actionType: 'action', + systemPath: this.actionsPath + }, + { + parent: this.settings + } + ); + + await this.settings.updateSource({ [`${this.actionsPath}.${action.id}`]: action }); + this.move = foundry.utils.getProperty(this.settings, this.movePath); + + this.render(); + } + + static async editItem(_, target) { + const actionId = target.dataset.id; + const action = this.move.actions.get(actionId); + await new DHActionConfig(action, async updatedMove => { + await this.settings.updateSource({ [`${this.actionsPath}.${actionId}`]: updatedMove }); + this.move = foundry.utils.getProperty(this.settings, this.movePath); + this.render(); + }).render(true); + } + + static async removeItem(_, target) { + await this.settings.updateSource({ [`${this.actionsPath}.-=${target.dataset.id}`]: null }); + this.move = foundry.utils.getProperty(this.settings, this.movePath); + this.render(); + } + + static resetMoves() {} + + /** @override */ + _onClose(options = {}) { + if (!options.submitted) this.move = null; + } + + static async configure(move, movePath, settings, options = {}) { + return new Promise(resolve => { + const app = new this(move, movePath, settings, options); + app.addEventListener('close', () => resolve(app.move), { once: true }); + app.render({ force: true }); + }); + } +} diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 390435b4..a03a0e2a 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -138,8 +138,8 @@ export const defaultRestOptions = { icon: 'fa-solid fa-bandage', img: 'icons/magic/life/cross-worn-green.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.description'), - actions: [ - { + actions: { + tendToWounds: { type: 'healing', name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.name'), img: 'icons/magic/life/cross-worn-green.webp', @@ -154,7 +154,7 @@ export const defaultRestOptions = { } } } - ] + } }, clearStress: { id: 'clearStress', @@ -162,8 +162,8 @@ export const defaultRestOptions = { icon: 'fa-regular fa-face-surprise', img: 'icons/magic/perception/eye-ringed-green.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.description'), - actions: [ - { + actions: { + clearStress: { type: 'healing', name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.name'), img: 'icons/magic/perception/eye-ringed-green.webp', @@ -178,7 +178,7 @@ export const defaultRestOptions = { } } } - ] + } }, repairArmor: { id: 'repairArmor', @@ -186,8 +186,8 @@ export const defaultRestOptions = { icon: 'fa-solid fa-hammer', img: 'icons/skills/trades/smithing-anvil-silver-red.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.description'), - actions: [ - { + actions: { + repairArmor: { type: 'healing', name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.name'), img: 'icons/skills/trades/smithing-anvil-silver-red.webp', @@ -202,7 +202,7 @@ export const defaultRestOptions = { } } } - ] + } }, prepare: { id: 'prepare', @@ -210,7 +210,7 @@ export const defaultRestOptions = { icon: 'fa-solid fa-dumbbell', img: 'icons/skills/trades/academics-merchant-scribe.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.prepare.description'), - actions: [] + actions: {} } }), longRest: () => ({ @@ -220,7 +220,23 @@ export const defaultRestOptions = { icon: 'fa-solid fa-bandage', img: 'icons/magic/life/cross-worn-green.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.tendToWounds.description'), - actions: [] + actions: { + tendToWounds: { + type: 'healing', + name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.tendToWounds.name'), + img: 'icons/magic/life/cross-worn-green.webp', + actionType: 'action', + healing: { + applyTo: healingTypes.hitPoints.id, + value: { + custom: { + enabled: true, + formula: '@system.resources.hitPoints.max' + } + } + } + } + } }, clearStress: { id: 'clearStress', @@ -228,7 +244,23 @@ export const defaultRestOptions = { icon: 'fa-regular fa-face-surprise', img: 'icons/magic/perception/eye-ringed-green.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.clearStress.description'), - actions: [] + actions: { + clearStress: { + type: 'healing', + name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.clearStress.name'), + img: 'icons/magic/perception/eye-ringed-green.webp', + actionType: 'action', + healing: { + applyTo: healingTypes.stress.id, + value: { + custom: { + enabled: true, + formula: '@system.resources.stress.max' + } + } + } + } + } }, repairArmor: { id: 'repairArmor', @@ -236,7 +268,23 @@ export const defaultRestOptions = { icon: 'fa-solid fa-hammer', img: 'icons/skills/trades/smithing-anvil-silver-red.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.repairArmor.description'), - actions: [] + actions: { + repairArmor: { + type: 'healing', + name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.repairArmor.name'), + img: 'icons/skills/trades/smithing-anvil-silver-red.webp', + actionType: 'action', + healing: { + applyTo: healingTypes.armorStack.id, + value: { + custom: { + enabled: true, + formula: '@system.armorScore' + } + } + } + } + } }, prepare: { id: 'prepare', @@ -244,7 +292,7 @@ export const defaultRestOptions = { icon: 'fa-solid fa-dumbbell', img: 'icons/skills/trades/academics-merchant-scribe.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.prepare.description'), - actions: [] + actions: {} }, workOnAProject: { id: 'workOnAProject', @@ -252,7 +300,7 @@ export const defaultRestOptions = { icon: 'fa-solid fa-diagram-project', img: 'icons/skills/social/thumbsup-approval-like.webp', description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.workOnAProject.description'), - actions: [] + actions: {} } }) }; diff --git a/module/data/fields/actionField.mjs b/module/data/fields/actionField.mjs index 74379ac1..dd2b4dea 100644 --- a/module/data/fields/actionField.mjs +++ b/module/data/fields/actionField.mjs @@ -1,5 +1,5 @@ -import DHActionConfig from "../../applications/sheets-configs/action-config.mjs"; -import MappingField from "./mappingField.mjs"; +import DHActionConfig from '../../applications/sheets-configs/action-config.mjs'; +import MappingField from './mappingField.mjs'; /** * Specialized collection type for stored actions. @@ -7,52 +7,52 @@ import MappingField from "./mappingField.mjs"; * @param {Action[]} entries The actions to store. */ export class ActionCollection extends Collection { - constructor(model, entries) { - super(); - this.#model = model; - for ( const entry of entries ) { - if ( !(entry instanceof game.system.api.models.actions.actionsTypes.base) ) continue; - this.set(entry._id, entry); + constructor(model, entries) { + super(); + this.#model = model; + for (const entry of entries) { + if (!(entry instanceof game.system.api.models.actions.actionsTypes.base)) continue; + this.set(entry._id, entry); + } } - } - /* -------------------------------------------- */ - /* Properties */ - /* -------------------------------------------- */ + /* -------------------------------------------- */ + /* Properties */ + /* -------------------------------------------- */ - /** - * The parent DataModel to which this ActionCollection belongs. - * @type {DataModel} - */ - #model; + /** + * The parent DataModel to which this ActionCollection belongs. + * @type {DataModel} + */ + #model; - /* -------------------------------------------- */ + /* -------------------------------------------- */ - /* -------------------------------------------- */ - /* Methods */ - /* -------------------------------------------- */ + /* -------------------------------------------- */ + /* Methods */ + /* -------------------------------------------- */ - /* -------------------------------------------- */ + /* -------------------------------------------- */ - /** - * Test the given predicate against every entry in the Collection. - * @param {function(*, number, ActionCollection): boolean} predicate The predicate. - * @returns {boolean} - */ - every(predicate) { - return this.reduce((pass, v, i) => pass && predicate(v, i, this), true); - } + /** + * Test the given predicate against every entry in the Collection. + * @param {function(*, number, ActionCollection): boolean} predicate The predicate. + * @returns {boolean} + */ + every(predicate) { + return this.reduce((pass, v, i) => pass && predicate(v, i, this), true); + } - /* -------------------------------------------- */ + /* -------------------------------------------- */ - /** - * Convert the ActionCollection to an array of simple objects. - * @param {boolean} [source=true] Draw data for contained Documents from the underlying data source? - * @returns {object[]} The extracted array of primitive objects. - */ - toObject(source=true) { - return this.map(doc => doc.toObject(source)); - } + /** + * Convert the ActionCollection to an array of simple objects. + * @param {boolean} [source=true] Draw data for contained Documents from the underlying data source? + * @returns {object[]} The extracted array of primitive objects. + */ + toObject(source = true) { + return this.map(doc => doc.toObject(source)); + } } /* -------------------------------------------- */ @@ -61,17 +61,17 @@ export class ActionCollection extends Collection { * Field that stores actions. */ export class ActionsField extends MappingField { - constructor(options) { - super(new ActionField(), options); - } + constructor(options) { + super(new ActionField(), options); + } - /* -------------------------------------------- */ + /* -------------------------------------------- */ - /** @inheritDoc */ - initialize(value, model, options) { - const actions = Object.values(super.initialize(value, model, options)); - return new ActionCollection(model, actions); - } + /** @inheritDoc */ + initialize(value, model, options) { + const actions = Object.values(super.initialize(value, model, options)); + return new ActionCollection(model, actions); + } } /* -------------------------------------------- */ @@ -81,7 +81,10 @@ export class ActionsField extends MappingField { */ export class ActionField extends foundry.data.fields.ObjectField { getModel(value) { - return game.system.api.models.actions.actionsTypes[value.type] ?? game.system.api.models.actions.actionsTypes.attack; + return ( + game.system.api.models.actions.actionsTypes[value.type] ?? + game.system.api.models.actions.actionsTypes.attack + ); } /* -------------------------------------------- */ @@ -122,138 +125,146 @@ export class ActionField extends foundry.data.fields.ObjectField { export function ActionMixin(Base) { class Action extends Base { static metadata = Object.freeze({ - name: "Action", - label: "DAGGERHEART.GENERAL.Action.single", - sheetClass: DHActionConfig + name: 'Action', + label: 'DAGGERHEART.GENERAL.Action.single', + sheetClass: DHActionConfig }); static _sheets = new Map(); - + static get documentName() { - return this.metadata.name; + return this.metadata.name; } get documentName() { - return this.constructor.documentName; + return this.constructor.documentName; } static defaultName() { - return this.documentName; + return this.documentName; } - + get relativeUUID() { - return `.Item.${this.item.id}.Action.${this.id}`; + return `.Item.${this.item.id}.Action.${this.id}`; } get uuid() { - return `${this.item.uuid}.${this.documentName}.${this.id}`; + return `${this.item.uuid}.${this.documentName}.${this.id}`; } get sheet() { - if(!this.constructor._sheets.has(this.uuid)) { - const sheet = new this.constructor.metadata.sheetClass(this); - this.constructor._sheets.set(this.uuid, sheet); - } - return this.constructor._sheets.get(this.uuid); + if (!this.constructor._sheets.has(this.uuid)) { + const sheet = new this.constructor.metadata.sheetClass(this); + this.constructor._sheets.set(this.uuid, sheet); + } + return this.constructor._sheets.get(this.uuid); } get inCollection() { - return foundry.utils.getProperty(this.parent, this.systemPath) instanceof Collection; + return foundry.utils.getProperty(this.parent, this.systemPath) instanceof Collection; } - static async create(data, operation={}) { - const { parent, renderSheet } = operation; - let { type } = data; - if(!type || !game.system.api.models.actions.actionsTypes[type]) { - ({ type } = - (await foundry.applications.api.DialogV2.input({ - window: { title: 'Select Action Type' }, - position: { width: 300 }, - classes: ['daggerheart', 'dh-style'], - content: await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/actionTypes/actionType.hbs', - { - types: CONFIG.DH.ACTIONS.actionTypes, - itemName: parent.parent?.name + static async create(data, operation = {}) { + const { parent, renderSheet } = operation; + let { type } = data; + if (!type || !game.system.api.models.actions.actionsTypes[type]) { + ({ type } = + (await foundry.applications.api.DialogV2.input({ + window: { title: game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType') }, + position: { width: 300 }, + classes: ['daggerheart', 'dh-style'], + content: await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/actionTypes/actionType.hbs', + { types: CONFIG.DH.ACTIONS.actionTypes } + ), + ok: { + label: game.i18n.format('DOCUMENT.Create', { + type: game.i18n.localize('DAGGERHEART.GENERAL.Action.single') + }) } - ), - ok: { - label: game.i18n.format('DOCUMENT.Create', { - type: game.i18n.localize('DAGGERHEART.GENERAL.Action.single') - }) - } - })) ?? {}); - } - if (!type) return; - - const cls = game.system.api.models.actions.actionsTypes[type]; - const action = new cls( - { - type, - ...cls.getSourceConfig(parent) - }, - { - parent - } - ); - const created = await parent.parent.update({ [`system.actions.${action.id}`]: action.toObject() }); - const newAction = parent.actions.get(action.id); - if(!newAction) return null; - if( renderSheet ) newAction.sheet.render({ force: true }); - return newAction; + })) ?? {}); + } + if (!type) return; + + const cls = game.system.api.models.actions.actionsTypes[type]; + const action = new cls( + { + type, + ...cls.getSourceConfig(parent) + }, + { + parent + } + ); + const created = await parent.parent.update({ [`system.actions.${action.id}`]: action.toObject() }); + const newAction = parent.actions.get(action.id); + if (!newAction) return null; + if (renderSheet) newAction.sheet.render({ force: true }); + return newAction; } - async update(updates, options={}) { - const path = this.inCollection ? `system.${this.systemPath}.${this.id}` : `system.${this.systemPath}`, - result = await this.item.update({[path]: updates}, options); - return this.inCollection ? foundry.utils.getProperty(result, `system.${this.systemPath}`).get(this.id) : foundry.utils.getProperty(result, `system.${this.systemPath}`); + async update(updates, options = {}) { + const isSetting = !this.parent.parent; + const basePath = isSetting ? this.systemPath : `system.${this.systemPath}`; + const path = this.inCollection ? `${basePath}.${this.id}` : basePath; + let result = null; + if (isSetting) { + await this.parent.updateSource({ [path]: updates }, options); + result = this.parent; + } else { + result = await this.item.update({ [path]: updates }, options); + } + + return this.inCollection + ? foundry.utils.getProperty(result, basePath).get(this.id) + : foundry.utils.getProperty(result, basePath); } delete() { - if(!this.inCollection) return this.item; - const action = foundry.utils.getProperty(this.item, `system.${this.systemPath}`)?.get(this.id); - if ( !action ) return this.item; - this.item.update({ [`system.${this.systemPath}.-=${this.id}`]: null }); - this.constructor._sheets.get(this.uuid)?.close(); + if (!this.inCollection) return this.item; + const action = foundry.utils.getProperty(this.item, `system.${this.systemPath}`)?.get(this.id); + if (!action) return this.item; + this.item.update({ [`system.${this.systemPath}.-=${this.id}`]: null }); + this.constructor._sheets.get(this.uuid)?.close(); } async deleteDialog() { - const confirmed = await foundry.applications.api.DialogV2.confirm({ - window: { - title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { - type: game.i18n.localize(`DAGGERHEART.GENERAL.Action.single`), - name: this.name - }) - }, - content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { - name: this.name - }) - }); - if (!confirmed) return; - return this.delete(); + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.title', { + type: game.i18n.localize(`DAGGERHEART.GENERAL.Action.single`), + name: this.name + }) + }, + content: game.i18n.format('DAGGERHEART.APPLICATIONS.DeleteConfirmation.text', { + name: this.name + }) + }); + if (!confirmed) return; + return this.delete(); } async toChat(origin) { - const cls = getDocumentClass('ChatMessage'); - const systemData = { - title: game.i18n.localize('DAGGERHEART.CONFIG.ActionType.action'), - origin: origin, - img: this.img, - name: this.name, - description: this.description, - actions: [] - }; - const msg = { - type: 'abilityUse', - user: game.user.id, - system: systemData, - content: await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/ui/chat/ability-use.hbs', - systemData - ) - }; + const cls = getDocumentClass('ChatMessage'); + const systemData = { + title: game.i18n.localize('DAGGERHEART.CONFIG.ActionType.action'), + origin: origin, + img: this.img, + name: this.name, + description: this.description, + actions: [] + }; + const msg = { + type: 'abilityUse', + user: game.user.id, + system: systemData, + content: await foundry.applications.handlebars.renderTemplate( + 'systems/daggerheart/templates/ui/chat/ability-use.hbs', + systemData + ) + }; - cls.create(msg); + cls.create(msg); } } diff --git a/module/data/settings/Appearance.mjs b/module/data/settings/Appearance.mjs index d8b4c687..ccdf3c0b 100644 --- a/module/data/settings/Appearance.mjs +++ b/module/data/settings/Appearance.mjs @@ -13,7 +13,8 @@ export default class DhAppearance extends foundry.abstract.DataModel { dualityColorScheme: new fields.StringField({ required: true, choices: DualityRollColor, - initial: DualityRollColor.normal.value + initial: DualityRollColor.normal.value, + label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.dualityColorScheme.label' }), diceSoNice: new fields.SchemaField({ hope: new fields.SchemaField({ diff --git a/module/data/settings/Homebrew.mjs b/module/data/settings/Homebrew.mjs index 008cd73c..5619898e 100644 --- a/module/data/settings/Homebrew.mjs +++ b/module/data/settings/Homebrew.mjs @@ -1,4 +1,5 @@ import { defaultRestOptions } from '../../config/generalConfig.mjs'; +import { ActionsField } from '../fields/actionField.mjs'; export default class DhHomebrew extends foundry.abstract.DataModel { static LOCALIZATION_PREFIXES = ['DAGGERHEART.SETTINGS.Homebrew']; // Doesn't work for some reason @@ -61,7 +62,7 @@ export default class DhHomebrew extends foundry.abstract.DataModel { base64: false }), description: new fields.HTMLField(), - actions: new fields.ArrayField(new fields.ObjectField()) + actions: new ActionsField() }), { initial: defaultRestOptions.longRest() } ) @@ -78,7 +79,7 @@ export default class DhHomebrew extends foundry.abstract.DataModel { base64: false }), description: new fields.HTMLField(), - actions: new fields.ArrayField(new fields.ObjectField()) + actions: new ActionsField() }), { initial: defaultRestOptions.shortRest() } ) diff --git a/styles/less/global/tab-form-footer.less b/styles/less/global/tab-form-footer.less index 14794204..bd084ee8 100644 --- a/styles/less/global/tab-form-footer.less +++ b/styles/less/global/tab-form-footer.less @@ -1,7 +1,15 @@ -.sheet.daggerheart.dh-style { +.daggerheart.dh-style { .tab-form-footer { display: flex; - padding: 0 10px; + gap: 8px; + + &.spaced { + margin-top: 8px; + } + + &.padded { + padding: 0 10px; + } button { flex: 1; diff --git a/styles/less/ui/settings/settings.less b/styles/less/ui/settings/settings.less index 580c79ca..0ba6f094 100644 --- a/styles/less/ui/settings/settings.less +++ b/styles/less/ui/settings/settings.less @@ -29,6 +29,7 @@ } .settings-items { + width: 100%; display: flex; flex-direction: column; gap: 8px; diff --git a/templates/actionTypes/healing.hbs b/templates/actionTypes/healing.hbs index 9091469e..4b8023d7 100644 --- a/templates/actionTypes/healing.hbs +++ b/templates/actionTypes/healing.hbs @@ -24,7 +24,7 @@ {{else}} {{> formula fields=fields.value.fields source=source.value target="value"}} {{/if}} - {{formField fields.applyTo value=source.type name="healing.applyTo" localize=true}} + {{formField fields.applyTo value=source.applyTo name="healing.applyTo" localize=true}} {{#*inline "formula"}} diff --git a/templates/settings/appearance-settings.hbs b/templates/settings/appearance-settings.hbs index f297a28f..59f6345e 100644 --- a/templates/settings/appearance-settings.hbs +++ b/templates/settings/appearance-settings.hbs @@ -5,110 +5,48 @@ {{formGroup settingFields.schema.fields.displayFear value=settingFields._source.displayFear localize=true}} {{formGroup settingFields.schema.fields.showGenericStatusEffects value=settingFields._source.showGenericStatusEffects localize=true}} + {{formGroup settingFields.schema.fields.dualityColorScheme value=settingFields._source.dualityColorScheme localize=true}} +