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 e516be03..47278bbb 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) { @@ -70,23 +68,20 @@ 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 editedMove = await game.system.api.applications.sheetConfigs.DowntimeConfig.configure( + move, + target.dataset.id, + 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, @@ -136,7 +131,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 3f915e41..039e7a13 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; } @@ -183,14 +184,34 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) { if (!newActions.findSplice(x => x._id === data._id, data)) newActions.push(data); } else newActions = data; - const updates = await this.action.parent.parent.update({ [`system.${this.action.systemPath}`]: newActions }); - if (!updates) return; - this.action = Array.isArray(container) - ? foundry.utils.getProperty(updates.system, this.action.systemPath)[this.action.index] - : foundry.utils.getProperty(updates.system, this.action.systemPath); + const action = await this.updateParent(newActions, container); + if (!action) return; + + this.action = action; + + this.sheetUpdate?.(this.action); this.render(); } + async updateParent(newActions, container) { + const isSystemPath = this.action.parent.parent?.update; + if (isSystemPath) { + const updates = await this.action.parent.parent.update({ + [`system.${this.action.systemPath}`]: newActions + }); + if (!updates) return null; + + return Array.isArray(container) + ? foundry.utils.getProperty(updates.system, this.action.systemPath)[this.action.index] + : foundry.utils.getProperty(updates.system, this.action.systemPath); + } else { + await this.action.parent.updateSource({ [this.action.systemPath]: newActions }); + + const updates = foundry.utils.getProperty(this.action.parent, this.action.systemPath); + return Array.isArray(container) ? updates[this.action.index] : updates; + } + } + static addElement(event) { const data = this.action.toObject(), key = event.target.closest('[data-key]').dataset.key; diff --git a/module/applications/sheets-configs/downtimeConfig.mjs b/module/applications/sheets-configs/downtimeConfig.mjs new file mode 100644 index 00000000..2a501209 --- /dev/null +++ b/module/applications/sheets-configs/downtimeConfig.mjs @@ -0,0 +1,167 @@ +import { actionsTypes } from '../../data/action/_module.mjs'; +import DHActionConfig from './action-config.mjs'; + +const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; + +export default class DhSettingsActionView extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(move, moveId, settings, options) { + super(options); + + this.move = move; + + this.actionsPath = `restMoves.shortRest.moves.${moveId}.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() { + 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), + img: 'icons/magic/life/cross-worn-green.webp', + actionType: 'action', + systemPath: this.actionsPath + }, + { + parent: this.settings + } + ); + + this.move.actions.push(action); + await this.settings.updateSource({ [this.actionsPath]: this.move.actions }); + + this.render(); + } + + static async editItem(_, target) { + const editIndex = Number(target.dataset.id); + const action = this.move.actions.find((_, index) => index === editIndex); + await new DHActionConfig(action, async updatedMove => { + this.move.actions[editIndex] = updatedMove; + await this.settings.updateSource({ [this.actionsPath]: this.move.actions }); + this.render(); + }).render(true); + } + + static async removeItem(_, target) { + this.move.actions = this.move.actions.filter((_, index) => index !== Number(target.dataset.id)); + await this.settings.updateSource({ [this.actionsPath]: this.move.actions }); + + this.render(); + } + + static resetMoves() {} + + /** @override */ + _onClose(options = {}) { + if (!options.submitted) this.move = null; + } + + static async configure(move, moveId, settings, options = {}) { + return new Promise(resolve => { + const app = new this(move, moveId, 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..ec190ef2 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -140,6 +140,8 @@ export const defaultRestOptions = { description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.description'), actions: [ { + _id: foundry.utils.randomID(), + systemPath: 'restMoves.shortRest.moves.tendToWounds.actions', type: 'healing', name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.name'), img: 'icons/magic/life/cross-worn-green.webp', @@ -164,6 +166,8 @@ export const defaultRestOptions = { description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.description'), actions: [ { + _id: foundry.utils.randomID(), + systemPath: 'restMoves.shortRest.moves.tendToWounds.actions', type: 'healing', name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.name'), img: 'icons/magic/perception/eye-ringed-green.webp', @@ -188,6 +192,8 @@ export const defaultRestOptions = { description: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.description'), actions: [ { + _id: foundry.utils.randomID(), + systemPath: 'restMoves.shortRest.moves.tendToWounds.actions', type: 'healing', name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.name'), img: 'icons/skills/trades/smithing-anvil-silver-red.webp', diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index ad442951..16a90a76 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -112,7 +112,7 @@ export default class DHBaseAction extends foundry.abstract.DataModel { prepareData() {} get index() { - return foundry.utils.getProperty(this.parent, this.systemPath).indexOf(this); + return foundry.utils.getProperty(this.parent, this.systemPath).findIndex(x => x.id === this.id); } get id() { diff --git a/module/data/settings/Homebrew.mjs b/module/data/settings/Homebrew.mjs index 008cd73c..35843a90 100644 --- a/module/data/settings/Homebrew.mjs +++ b/module/data/settings/Homebrew.mjs @@ -1,4 +1,5 @@ import { defaultRestOptions } from '../../config/generalConfig.mjs'; +import ActionField 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 fields.ArrayField(new ActionField()) }), { 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 fields.ArrayField(new ActionField()) }), { 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 3bd200a2..b27a07ed 100644 --- a/styles/less/ui/settings/settings.less +++ b/styles/less/ui/settings/settings.less @@ -25,6 +25,7 @@ } .settings-items { + width: 100%; display: flex; flex-direction: column; gap: 8px; diff --git a/templates/settings/components/action-view.hbs b/templates/settings/components/action-view.hbs deleted file mode 100644 index 99e43122..00000000 --- a/templates/settings/components/action-view.hbs +++ /dev/null @@ -1,17 +0,0 @@ -
-
- - -
- -
-
- -
- {{localize "Description"}} - - - {{{ enrichedDescription }}} - -
-
\ No newline at end of file diff --git a/templates/settings/downtime-config/actions.hbs b/templates/settings/downtime-config/actions.hbs new file mode 100644 index 00000000..56d15ca2 --- /dev/null +++ b/templates/settings/downtime-config/actions.hbs @@ -0,0 +1,15 @@ +
+
+ {{localize "DAGGERHEART.GENERAL.Action.plural"}} + +
+ {{#each move.actions as |action index|}} + {{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" id=index }} + {{/each}} +
+
+
\ No newline at end of file diff --git a/templates/settings/components/action-view-footer.hbs b/templates/settings/downtime-config/footer.hbs similarity index 52% rename from templates/settings/components/action-view-footer.hbs rename to templates/settings/downtime-config/footer.hbs index 309f383f..5e5f31dd 100644 --- a/templates/settings/components/action-view-footer.hbs +++ b/templates/settings/downtime-config/footer.hbs @@ -1,3 +1,4 @@ - \ No newline at end of file diff --git a/templates/sheets/global/tabs/tab-actions.hbs b/templates/sheets/global/tabs/tab-actions.hbs index 584cf782..0e74019c 100644 --- a/templates/sheets/global/tabs/tab-actions.hbs +++ b/templates/sheets/global/tabs/tab-actions.hbs @@ -4,6 +4,11 @@ data-group='{{tabs.actions.group}}' > +
+
{{localize (concat 'DAGGERHEART.ACTIONS.TYPES.' item.type '.name')}}
+
{{localize (concat 'DAGGERHEART.CONFIG.ActionType.' item.actionType)}}
+
+ {{> 'daggerheart.inventory-items' title='DAGGERHEART.GENERAL.Action.plural' collection=document.system.actions diff --git a/templates/sheets/global/tabs/tab-form-footer.hbs b/templates/sheets/global/tabs/tab-form-footer.hbs index 96babf82..70c8d273 100644 --- a/templates/sheets/global/tabs/tab-form-footer.hbs +++ b/templates/sheets/global/tabs/tab-form-footer.hbs @@ -1,3 +1,3 @@ -