From 9096a70df6a1c741f6895ea430b4bc8371c385eb Mon Sep 17 00:00:00 2001 From: DC23 Date: Tue, 26 Nov 2024 23:01:09 +1100 Subject: [PATCH] work in progress on moving saves customisation into a settings menu so I can have validation and better control #69 --- lang/en.json | 7 +- module/helpers/settings.mjs | 17 ++++ module/settings/saves-settings.mjs | 92 ++++++++++++++++++++++ module/settings/settings.mjs | 109 +++++--------------------- templates/settings/saves-settings.hbs | 25 ++++++ 5 files changed, 158 insertions(+), 92 deletions(-) create mode 100644 module/helpers/settings.mjs create mode 100644 module/settings/saves-settings.mjs create mode 100644 templates/settings/saves-settings.hbs diff --git a/lang/en.json b/lang/en.json index e1ffd4e..7b31965 100644 --- a/lang/en.json +++ b/lang/en.json @@ -160,8 +160,9 @@ "BASICFANTASYRPG.EffectEdit": "Edit Effect", "BASICFANTASYRPG.EffectDelete": "Delete Effect", - "BASICFANTASYRPG.AutoRollTokenHP.name": "Automatically Roll Token HP", - "BASICFANTASYRPG.AutoRollTokenHP.hint": "Based on HD value. If this setting is turned off, HP will be set to TODO WORK OUT WHAT", - + "BASICFANTASYRPG.Settings.SavesMenu.name": "Edit Saving Throw Names", + "BASICFANTASYRPG.Settings.SavesMenu.label": "Saving Throw Settings", + "BASICFANTASYRPG.Settings.SavesMenu.hint": "", "BASICFANTASYRPG.SaveName.hint": "Custom name for this saving throw. The same name is used for all players." + } \ No newline at end of file diff --git a/module/helpers/settings.mjs b/module/helpers/settings.mjs new file mode 100644 index 0000000..04d944b --- /dev/null +++ b/module/helpers/settings.mjs @@ -0,0 +1,17 @@ +// not the greatest approach, but +export function objectsShallowEqual (obj1, obj2) { + const entries1 = Object.entries(obj1) + const entries2 = Object.entries(obj2) + + if (entries1.length !== entries2.length) { + return false + } + + for (let [key, value] of entries1) { + if (obj2[key] !== value) { + return false + } + } + + return true +} diff --git a/module/settings/saves-settings.mjs b/module/settings/saves-settings.mjs new file mode 100644 index 0000000..560662c --- /dev/null +++ b/module/settings/saves-settings.mjs @@ -0,0 +1,92 @@ +// import { Helpers } from './helpers.mjs' +import { objectsShallowEqual } from '../helpers/settings.mjs' +import { SYSTEM_ID, SETTINGS } from './settings.mjs' + +// Helper array of IDs we'll use in a few places +const saves = ['death', 'wands', 'paralysis', 'breath', 'spells'] + +export function registerSavesSettings () { + // The settings menu + game.settings.registerMenu(SYSTEM_ID, SETTINGS.SAVES_MENU, { + name: 'BASICFANTASYRPG.Settings.SavesMenu.name', + label: 'BASICFANTASYRPG.Settings.SavesMenu.label', + hint: 'BASICFANTASYRPG.Settings.SavesMenu.hint', + icon: 'fas fa-cog', + type: SavesSettings, + restricted: true // GM-only + }) + + // the settings object + game.settings.register(SYSTEM_ID, SETTINGS.SAVES_SETTINGS, { + scope: 'world', + config: false, + type: Object, + default: SavesSettings.defaultSaves + }) +} + +class SavesSettings extends FormApplication { + static #defaultSaves = null + static get defaultSaves () { + if (!SavesSettings.#defaultSaves) { + SavesSettings.#defaultSaves = {} + saves.forEach(s => { + SavesSettings.#defaultSaves[s] = game.i18n.localize( + `BASICFANTASYRPG.Save${s.capitalize()}` + ) + }) + } + return SavesSettings.#defaultSaves + } + + static get defaultOptions () { + return foundry.utils.mergeObject(super.defaultOptions, { + popOut: true, + width: 400, + template: `systems/${SYSTEM_ID}/templates/settings/saves-settings.hbs`, + id: SETTINGS.SAVES_MENU, + title: 'BASICFANTASYRPG.Settings.SavesMenu.name' + }) + } + + getData () { + const initialValues = game.settings.get(SYSTEM_ID, SETTINGS.SAVES_SETTINGS) + // repack the current saves names into id, label and values for the form + const data = {} + saves.forEach((v, i) => { + data[i] = { + id: v, + label: SavesSettings.defaultSaves[v], + value: initialValues[v] + } + }) + return data + } + + _updateObject (event, formData) { + const data = foundry.utils.expandObject(formData) + const current = game.settings.get(SYSTEM_ID, SETTINGS.SAVES_SETTINGS) + + if (!objectsShallowEqual(data, current)) { + game.settings.set(SYSTEM_ID, SETTINGS.SAVES_SETTINGS, data) + SettingsConfig.reloadConfirm({ world: true }) + } + } + + activateListeners (html) { + super.activateListeners(html) + html.on('click', '[data-action=reset]', this._handleResetButtonClicked) + } + + async _handleResetButtonClicked (event) { + console.log('BFRPG | Reset save names to default values') + saves.forEach(id => { + const element = $(event.delegateTarget).find(`[name=${id}]`) + if (element && element.length > 0) { + element[0].value = game.i18n.localize( + `BASICFANTASYRPG.Save${id.capitalize()}` + ) + } + }) + } +} diff --git a/module/settings/settings.mjs b/module/settings/settings.mjs index c65ce51..583e06b 100644 --- a/module/settings/settings.mjs +++ b/module/settings/settings.mjs @@ -1,17 +1,21 @@ -import { BASICFANTASYRPG } from "../helpers/config.mjs" +import { BASICFANTASYRPG } from '../helpers/config.mjs' +import { registerSavesSettings } from './saves-settings.mjs' /** * pseudo-enum to make setting ID references less error prone */ export const SETTINGS = { -// AUTO_ROLL_TOKEN_HP: 'autoRollTokenHP', - // use the saves keys as the setting ID to make retrieval easy. + // AUTO_ROLL_TOKEN_HP: 'autoRollTokenHP', + // use the saves keys as the setting ID to make retrieval easy. // So long as the system.key is unique, it's fine. SAVE_DEATH_NAME: 'death', SAVE_WANDS_NAME: 'wands', SAVE_PARALYSIS_NAME: 'paralysis', SAVE_BREATH_NAME: 'breath', SAVE_SPELLS_NAME: 'spells', + + SAVES_MENU: 'savesMenu', + SAVES_SETTINGS: 'savesSettings' } // Use this internally for now. Refactoring the whole system is too big a job! @@ -22,103 +26,30 @@ export const SYSTEM_ID = 'basicfantasyrpg' * non-GM users. We iterate the array in the renderSettingsConfig * hook and remove those settings from the DOM when required. * This workaround is necessary because in Foundry v12, a restricted - * flag to show a setting to GM users is only supported for a setting + * flag to show a setting to GM users is only supported for a setting * menu, not a setting itself. */ const GM_ONLY_SETTINGS = [ - SETTINGS.SAVE_DEATH_NAME, - SETTINGS.SAVE_WANDS_NAME, - SETTINGS.SAVE_PARALYSIS_NAME, - SETTINGS.SAVE_BREATH_NAME, - SETTINGS.SAVE_SPELLS_NAME, +// SETTINGS.SAVE_DEATH_NAME, +// SETTINGS.SAVE_WANDS_NAME, +// SETTINGS.SAVE_PARALYSIS_NAME, +// SETTINGS.SAVE_BREATH_NAME, +// SETTINGS.SAVE_SPELLS_NAME ] Hooks.on('renderSettingsConfig', (app, [html], context) => { - if (game.user.isGM) return + if (game.user.isGM) return - GM_ONLY_SETTINGS.forEach(id => { - html.querySelector(`.form-group[data-setting-id="${SYSTEM_ID}.${id}"]`)?.remove() - }) + GM_ONLY_SETTINGS.forEach(id => { + html + .querySelector(`.form-group[data-setting-id="${SYSTEM_ID}.${id}"]`) + ?.remove() + }) }) - export function registerSettings () { /** * If we have any settings submenus, they'd be defined in separate files and called from here. */ - - /* - Disabled for now based on https://discord.com/channels/735808783493890058/1307906023444582430/1309772596103086081 - Will delete later once confirmed that it's the wrong direction. - game.settings.register(SYSTEM_ID, SETTINGS.AUTO_ROLL_TOKEN_HP, { - name: 'BASICFANTASYRPG.AutoRollTokenHP.name', - hint: 'BASICFANTASYRPG.AutoRollTokenHP.hint', - scope: 'world', - config: true, - type: Boolean, - default: true, - requiresReload: false, - }) -*/ - - /** - * Saving throw customisation. - * If we get too many settings, these could be broken out into a submenu. - * It's more coding work, but keeps things together for the users. - */ - // Death Ray or Poison - game.settings.register(SYSTEM_ID, SETTINGS.SAVE_DEATH_NAME, { - name: 'BASICFANTASYRPG.SaveDeath', - hint: 'BASICFANTASYRPG.SaveName.hint', - scope: 'world', - config: true, - type: String, - default: game.i18n.localize('BASICFANTASYRPG.SaveDeath'), - requiresReload: true, // I assume this will need a reload to ensure everything is re-rendered - }) - - // "BASICFANTASYRPG.SaveWands": "Magic Wands", - game.settings.register(SYSTEM_ID, SETTINGS.SAVE_WANDS_NAME, { - name: 'BASICFANTASYRPG.SaveWands', - hint: 'BASICFANTASYRPG.SaveName.hint', - scope: 'world', - config: true, - type: String, - default: game.i18n.localize('BASICFANTASYRPG.SaveWands'), - requiresReload: true, // I assume this will need a reload to ensure everything is re-rendered - }) - - // "BASICFANTASYRPG.SaveParalysis": "Paralysis or Petrify", - game.settings.register(SYSTEM_ID, SETTINGS.SAVE_PARALYSIS_NAME, { - name: 'BASICFANTASYRPG.SaveParalysis', - hint: 'BASICFANTASYRPG.SaveName.hint', - scope: 'world', - config: true, - type: String, - default: game.i18n.localize('BASICFANTASYRPG.SaveParalysis'), - requiresReload: true, // I assume this will need a reload to ensure everything is re-rendered - }) - - // "BASICFANTASYRPG.SaveBreath": "Dragon Breath", - game.settings.register(SYSTEM_ID, SETTINGS.SAVE_BREATH_NAME, { - name: 'BASICFANTASYRPG.SaveBreath', - hint: 'BASICFANTASYRPG.SaveName.hint', - scope: 'world', - config: true, - type: String, - default: game.i18n.localize('BASICFANTASYRPG.SaveBreath'), - requiresReload: true, // I assume this will need a reload to ensure everything is re-rendered - }) - - // "BASICFANTASYRPG.SaveSpells": "Rods, Staves, and Spells", - game.settings.register(SYSTEM_ID, SETTINGS.SAVE_SPELLS_NAME, { - name: 'BASICFANTASYRPG.SaveSpells', - hint: 'BASICFANTASYRPG.SaveName.hint', - scope: 'world', - config: true, - type: String, - default: game.i18n.localize('BASICFANTASYRPG.SaveSpells'), - requiresReload: true, // I assume this will need a reload to ensure everything is re-rendered - }) - + registerSavesSettings() } diff --git a/templates/settings/saves-settings.hbs b/templates/settings/saves-settings.hbs new file mode 100644 index 0000000..8eaede9 --- /dev/null +++ b/templates/settings/saves-settings.hbs @@ -0,0 +1,25 @@ +{{#*inline "savePartial"}} +
+ + +
+{{/inline}} + +{{log this}} +
+
+ {{localize "BASICFANTASYRPG.Settings.SavesMenu.name"}}: + {{#each this}} + {{> savePartial}} + {{/each}} +
+ + +
\ No newline at end of file