From a65719b31426e0cbfa06f0f86bc7bfb75336f112 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Fri, 20 Jun 2025 01:36:03 +0200 Subject: [PATCH] Added homebrew settings without action handling for now --- daggerheart.mjs | 3 +- lang/en.json | 4 + .../components/settingsActionsView.mjs | 144 ++++++++++++++++++ .../settings/homebrewSettings.mjs | 105 ++++++++++++- styles/daggerheart.css | 66 ++++++++ styles/settings.less | 80 ++++++++++ .../components/action-view-footer.hbs | 3 + .../components/action-view-header.hbs | 6 + templates/settings/components/action-view.hbs | 18 +++ .../components/settings-item-line.hbs | 22 +++ templates/settings/homebrew-settings.hbs | 32 ++++ 11 files changed, 478 insertions(+), 5 deletions(-) create mode 100644 module/applications/settings/components/settingsActionsView.mjs create mode 100644 templates/settings/components/action-view-footer.hbs create mode 100644 templates/settings/components/action-view-header.hbs create mode 100644 templates/settings/components/action-view.hbs create mode 100644 templates/settings/components/settings-item-line.hbs diff --git a/daggerheart.mjs b/daggerheart.mjs index b4caca2a..8c329868 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -297,6 +297,7 @@ const preloadHandlebarsTemplates = async function () { 'systems/daggerheart/templates/views/actionTypes/roll.hbs', 'systems/daggerheart/templates/views/actionTypes/cost.hbs', 'systems/daggerheart/templates/views/actionTypes/range-target.hbs', - 'systems/daggerheart/templates/views/actionTypes/effect.hbs' + 'systems/daggerheart/templates/views/actionTypes/effect.hbs', + 'systems/daggerheart/templates/settings/components/settings-item-line.hbs' ]); }; diff --git a/lang/en.json b/lang/en.json index d3fa281d..f76e9ccc 100755 --- a/lang/en.json +++ b/lang/en.json @@ -87,6 +87,10 @@ } }, "Homebrew": { + "NewDowntimeMove": "Downtime Move", + "DowntimeMoves": "Downtime Moves", + "ResetMovesTitle": "Reset {type} Downtime Moves", + "ResetMovesText": "Are you sure you want to reset?", "FIELDS": { "maxFear": { "label": "Max Fear" }, "traitArray": { "label": "Initial Trait Modifiers" } diff --git a/module/applications/settings/components/settingsActionsView.mjs b/module/applications/settings/components/settingsActionsView.mjs new file mode 100644 index 00000000..9b223ec5 --- /dev/null +++ b/module/applications/settings/components/settingsActionsView.mjs @@ -0,0 +1,144 @@ +import { actionsTypes } from '../../../data/_module.mjs'; +import DHActionConfig from '../../config/Action.mjs'; + +const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; + +export default class DhSettingsActionView extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(resolve, reject, title, name, img, description, actions) { + super({}); + + this.resolve = resolve; + this.reject = reject; + this.viewTitle = title; + this.name = name; + 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: '400', 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.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, img, description } = foundry.utils.expandObject(formData.object); + this.name = name; + this.description = description; + + this.render(); + } + + static async saveForm(event) { + this.resolve({ + name: this.name, + 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 FilePicker({ + 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/views/actionType.hbs', + { types: SYSTEM.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(SYSTEM.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 e1b60726..d59bc35c 100644 --- a/module/applications/settings/homebrewSettings.mjs +++ b/module/applications/settings/homebrewSettings.mjs @@ -1,8 +1,9 @@ import { DhHomebrew } from '../../data/settings/_module.mjs'; +import DhSettingsActionView from './components/settingsActionsView.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; -export default class DhAutomationSettings extends HandlebarsApplicationMixin(ApplicationV2) { +export default class DhHomebrewSettings extends HandlebarsApplicationMixin(ApplicationV2) { constructor() { super({}); @@ -19,7 +20,10 @@ export default class DhAutomationSettings extends HandlebarsApplicationMixin(App classes: ['daggerheart', 'setting', 'dh-style'], position: { width: '600', height: 'auto' }, actions: { - reset: this.reset, + addItem: this.addItem, + editItem: this.editItem, + removeItem: this.removeItem, + resetMoves: this.resetMoves, save: this.save }, form: { handler: this.updateData, submitOnChange: true } @@ -48,8 +52,101 @@ export default class DhAutomationSettings extends HandlebarsApplicationMixin(App this.render(); } - static async reset() { - this.settings = new DhHomebrew(); + static async addItem(_, target) { + await this.settings.updateSource({ + [`restMoves.${target.dataset.type}.moves.${foundry.utils.randomID()}`]: { + name: game.i18n.localize('DAGGERHEART.Settings.Homebrew.NewDowntimeMove'), + img: 'icons/magic/life/cross-worn-green.webp', + description: '', + actions: [] + } + }); + this.render(); + } + + 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.img, + move.description, + move.actions + ).render(true); + }).then(data => this.updateAction.bind(this)(data, target.dataset.type, target.dataset.id)); + } + + async updateAction(data, type, id) { + await this.settings.updateSource({ + [`restMoves.${type}.moves.${id}`]: { + name: data.name, + img: data.img, + description: data.description + } + }); + this.render(); + } + + static async removeItem(_, target) { + await this.settings.updateSource({ + [`restMoves.${target.dataset.type}.moves.-=${target.dataset.id}`]: null + }); + this.render(); + } + + static async resetMoves(_, target) { + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.format('DAGGERHEART.Settings.Homebrew.ResetMovesTitle', { + type: game.i18n.localize( + `DAGGERHEART.Downtime.${target.dataset.type === 'shortRest' ? 'ShortRest' : 'LongRest'}.title` + ) + }) + }, + content: game.i18n.localize('DAGGERHEART.Settings.Homebrew.ResetMovesText') + }); + + if (!confirmed) return; + + const fields = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Homebrew).schema.fields; + + const removeUpdate = Object.keys(this.settings.restMoves[target.dataset.type].moves).reduce((acc, key) => { + acc[`-=${key}`] = null; + + return acc; + }, {}); + + const updateBase = + target.dataset.type === 'shortRest' + ? fields.restMoves.fields.shortRest.fields + : fields.restMoves.fields.longRest.fields; + const update = { + nrChoices: updateBase.nrChoices.initial, + moves: Object.keys(updateBase.moves.initial).reduce((acc, key) => { + const move = updateBase.moves.initial[key]; + acc[key] = { + ...move, + name: game.i18n.localize(move.name), + description: game.i18n.localize(move.description) + }; + + return acc; + }, {}) + }; + + await this.settings.updateSource({ + [`restMoves.${target.dataset.type}`]: { + ...update, + moves: { + ...removeUpdate, + ...update.moves + } + } + }); + this.render(); } diff --git a/styles/daggerheart.css b/styles/daggerheart.css index 29b6ab27..b4500faf 100755 --- a/styles/daggerheart.css +++ b/styles/daggerheart.css @@ -3178,6 +3178,72 @@ div.daggerheart.views.multiclass { #resources:has(.fear-bar) { min-width: 200px; } +.daggerheart.dh-style.setting fieldset.two-columns { + display: grid; + grid-template-columns: 1fr 2fr; + gap: 10px; +} +.daggerheart.dh-style.setting fieldset.two-columns.even { + grid-template-columns: 1fr 1fr; +} +.daggerheart.dh-style.setting .settings-items { + display: flex; + flex-direction: column; + gap: 8px; +} +.daggerheart.dh-style.setting .settings-items .settings-item { + display: flex; + align-items: center; + justify-content: space-between; + border: 1px solid; + border-radius: 8px; + padding: 0 8px 0 0; +} +.daggerheart.dh-style.setting .settings-items .settings-item .settings-sub-item { + display: flex; + align-items: center; + gap: 8px; +} +.daggerheart.dh-style.setting .settings-items .settings-item .settings-sub-item img { + width: 60px; + border-radius: 8px 0 0 8px; +} +.daggerheart.dh-style.setting .settings-items .settings-item .settings-sub-item i { + font-size: 18px; +} +.daggerheart.dh-style.setting .settings-item-header { + display: flex; + align-items: center; +} +.daggerheart.dh-style.setting .settings-item-header .profile { + height: 100px; + width: 100px; + object-fit: cover; + box-sizing: border-box; + cursor: pointer; +} +.daggerheart.dh-style.setting .settings-item-header .item-info { + display: flex; + flex-direction: column; + align-items: center; + gap: 5px; + text-align: center; + width: 80%; +} +.daggerheart.dh-style.setting .settings-item-header .item-info .item-name input[type='text'] { + font-size: 32px; + height: 42px; + text-align: center; + width: 90%; + transition: all 0.3s ease; + outline: 2px solid transparent; + border: 1px solid transparent; +} +.daggerheart.dh-style.setting .settings-item-header .item-info .item-name input[type='text']:hover[type='text'], +.daggerheart.dh-style.setting .settings-item-header .item-info .item-name input[type='text']:focus[type='text'] { + box-shadow: none; + outline: 2px solid light-dark(#18162e, #f3c267); +} .daggerheart.dh-style.setting .settings-col { display: flex; flex-direction: column; diff --git a/styles/settings.less b/styles/settings.less index e37f9891..f11f41c7 100644 --- a/styles/settings.less +++ b/styles/settings.less @@ -1,4 +1,84 @@ .daggerheart.dh-style.setting { + fieldset { + &.two-columns { + display: grid; + grid-template-columns: 1fr 2fr; + gap: 10px; + + &.even { + grid-template-columns: 1fr 1fr; + } + } + } + + .settings-items { + display: flex; + flex-direction: column; + gap: 8px; + + .settings-item { + display: flex; + align-items: center; + justify-content: space-between; + border: 1px solid; + border-radius: 8px; + padding: 0 8px 0 0; + + .settings-sub-item { + display: flex; + align-items: center; + gap: 8px; + + img { + width: 60px; + border-radius: 8px 0 0 8px; + } + + i { + font-size: 18px; + } + } + } + } + + .settings-item-header { + display: flex; + align-items: center; + + .profile { + height: 100px; + width: 100px; + object-fit: cover; + box-sizing: border-box; + cursor: pointer; + } + + .item-info { + display: flex; + flex-direction: column; + align-items: center; + gap: 5px; + text-align: center; + width: 80%; + + .item-name input[type='text'] { + font-size: 32px; + height: 42px; + text-align: center; + width: 90%; + transition: all 0.3s ease; + outline: 2px solid transparent; + border: 1px solid transparent; + + &:hover[type='text'], + &:focus[type='text'] { + box-shadow: none; + outline: 2px solid light-dark(@dark-blue, @golden); + } + } + } + } + .settings-col { display: flex; flex-direction: column; diff --git a/templates/settings/components/action-view-footer.hbs b/templates/settings/components/action-view-footer.hbs new file mode 100644 index 00000000..309f383f --- /dev/null +++ b/templates/settings/components/action-view-footer.hbs @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/templates/settings/components/action-view-header.hbs b/templates/settings/components/action-view-header.hbs new file mode 100644 index 00000000..a5269faf --- /dev/null +++ b/templates/settings/components/action-view-header.hbs @@ -0,0 +1,6 @@ +
+ +
+

+
+
\ No newline at end of file diff --git a/templates/settings/components/action-view.hbs b/templates/settings/components/action-view.hbs new file mode 100644 index 00000000..6c01b072 --- /dev/null +++ b/templates/settings/components/action-view.hbs @@ -0,0 +1,18 @@ +
+
+ {{localize "Description"}} + + + {{{ enrichedDescription }}} + +
+ {{!--
+ {{localize "Actions"}} + +
+ {{#each this.actions as |action index|}} + {{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" action type="actions" id=index }} + {{/each}} +
+
--}} +
\ No newline at end of file diff --git a/templates/settings/components/settings-item-line.hbs b/templates/settings/components/settings-item-line.hbs new file mode 100644 index 00000000..039301ec --- /dev/null +++ b/templates/settings/components/settings-item-line.hbs @@ -0,0 +1,22 @@ +
+
+ +
{{this.name}}
+
+ +
\ No newline at end of file diff --git a/templates/settings/homebrew-settings.hbs b/templates/settings/homebrew-settings.hbs index aa866378..6380f726 100644 --- a/templates/settings/homebrew-settings.hbs +++ b/templates/settings/homebrew-settings.hbs @@ -10,6 +10,38 @@ {{/each}} + +
+ {{localize "DAGGERHEART.Settings.Homebrew.DowntimeMoves"}} + +
+ + {{localize "DAGGERHEART.Downtime.LongRest.title"}} + + + + +
+ {{#each settingFields._source.restMoves.longRest.moves as |move id|}} + {{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" this type="longRest" id=id }} + {{/each}} +
+
+ +
+ + {{localize "DAGGERHEART.Downtime.ShortRest.title"}} + + + + +
+ {{#each settingFields._source.restMoves.shortRest.moves as |move id|}} + {{> "systems/daggerheart/templates/settings/components/settings-item-line.hbs" this type="shortRest" id=id }} + {{/each}} +
+
+