Add saving and editing of default attributes and their values (closes #29)
This commit is contained in:
parent
5159db3d33
commit
d540cc72f6
6 changed files with 178 additions and 34 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import { __ID__, filePath } from "../consts.mjs";
|
||||
import { attributeSorter } from "../utils/attributeSort.mjs";
|
||||
import { ask } from "../utils/DialogManager.mjs";
|
||||
import { localizer } from "../utils/localizer.mjs";
|
||||
import { toID } from "../utils/toID.mjs";
|
||||
|
||||
|
|
@ -22,6 +23,14 @@ export class AttributeManager extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
},
|
||||
window: {
|
||||
resizable: true,
|
||||
controls: [
|
||||
{
|
||||
icon: `fa-solid fa-globe`,
|
||||
label: `Save As Defaults`,
|
||||
visible: () => game.user.isGM,
|
||||
action: `saveAsDefault`,
|
||||
}
|
||||
],
|
||||
},
|
||||
form: {
|
||||
submitOnChange: false,
|
||||
|
|
@ -31,6 +40,7 @@ export class AttributeManager extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
actions: {
|
||||
addNew: this.#addNew,
|
||||
removeAttribute: this.#remove,
|
||||
saveAsDefault: this.#saveAsDefaults,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -73,16 +83,39 @@ export class AttributeManager extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
new DragDrop.implementation({
|
||||
dragSelector: `.attribute-drag-handle`,
|
||||
dropSelector: `.attributes`,
|
||||
permissions: {
|
||||
dragstart: this._canDragStart.bind(this),
|
||||
drop: this._canDragDrop.bind(this),
|
||||
},
|
||||
callbacks: {
|
||||
dragstart: this._onDragStart.bind(this),
|
||||
drop: this._onDrop.bind(this),
|
||||
},
|
||||
}).bind(this.element);
|
||||
};
|
||||
|
||||
/** @this {AttributeManager} */
|
||||
static async #onSubmit() {
|
||||
const entries = Object.entries(this.#attributes)
|
||||
.map(([id, attr]) => {
|
||||
if (attr == null) {
|
||||
return [ id, attr ];
|
||||
};
|
||||
|
||||
if (attr.isNew) {
|
||||
delete attr.isNew;
|
||||
return [ toID(attr.name), attr ];
|
||||
};
|
||||
|
||||
return [ id, attr ];
|
||||
});
|
||||
const data = Object.fromEntries(entries);
|
||||
this.#attributes = data;
|
||||
|
||||
const diff = diffObject(
|
||||
this.#doc.system.attr,
|
||||
data,
|
||||
{ inner: false, deletionKeys: true },
|
||||
);
|
||||
|
||||
await this.#doc.update({ "system.attr": diff });
|
||||
};
|
||||
// #endregion Lifecycle
|
||||
|
||||
// #region Data Prep
|
||||
|
|
@ -152,48 +185,87 @@ export class AttributeManager extends HandlebarsApplicationMixin(ApplicationV2)
|
|||
static async #remove($e, element) {
|
||||
const attribute = element.closest(`[data-attribute]`)?.dataset.attribute;
|
||||
if (!attribute) { return };
|
||||
delete this.#attributes[attribute];
|
||||
this.#attributes[`-=${attribute}`] = null;
|
||||
if (game.release.generation < 14) {
|
||||
delete this.#attributes[attribute];
|
||||
this.#attributes[`-=${attribute}`] = null;
|
||||
}
|
||||
else {
|
||||
this.#attributes[attribute] = _del;
|
||||
}
|
||||
await this.render({ parts: [ `attributes` ] });
|
||||
};
|
||||
|
||||
/** @this {AttributeManager} */
|
||||
static async #onSubmit() {
|
||||
const entries = Object.entries(this.#attributes)
|
||||
.map(([id, attr]) => {
|
||||
if (attr == null) {
|
||||
return [ id, attr ];
|
||||
};
|
||||
static async #saveAsDefaults() {
|
||||
const attrs = deepClone(this.#attributes);
|
||||
|
||||
if (attr.isNew) {
|
||||
delete attr.isNew;
|
||||
return [ toID(attr.name), attr ];
|
||||
};
|
||||
// Prompt the user for what values they want to save the attributes with
|
||||
const inputs = [];
|
||||
for (const attr of Object.values(attrs)) {
|
||||
const id = toID(attr.name);
|
||||
|
||||
return [ id, attr ];
|
||||
if (attr.isRange) {
|
||||
inputs.push(
|
||||
{
|
||||
type: `collapse`,
|
||||
summary: attr.name,
|
||||
inputs: [
|
||||
{
|
||||
key: `${id}.value`,
|
||||
type: `input`,
|
||||
inputType: `number`,
|
||||
label: `Value`,
|
||||
defaultValue: attr.value,
|
||||
},
|
||||
{
|
||||
key: `${id}.max`,
|
||||
type: `input`,
|
||||
inputType: `number`,
|
||||
label: `Maximum`,
|
||||
defaultValue: attr.max,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ type: `divider` }
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
inputs.push({
|
||||
key: `${id}.value`,
|
||||
type: `input`,
|
||||
inputType: `number`,
|
||||
label: `${attr.name}`,
|
||||
defaultValue: attr.value,
|
||||
});
|
||||
const data = Object.fromEntries(entries);
|
||||
this.#attributes = data;
|
||||
|
||||
const diff = diffObject(
|
||||
this.#doc.system.attr,
|
||||
data,
|
||||
{ inner: false, deletionKeys: true },
|
||||
);
|
||||
inputs.push({ type: `divider` });
|
||||
};
|
||||
|
||||
await this.#doc.update({ "system.attr": diff });
|
||||
const prompt = {
|
||||
id: `${this.#doc.id}-global-attr-saving`,
|
||||
inputs: inputs.slice(0, -1),
|
||||
alwaysUseAnswerObject: true,
|
||||
window: { title: `taf.Apps.AttributeManager.default-attribute-values` },
|
||||
};
|
||||
|
||||
const response = await ask(prompt);
|
||||
switch (response.state) {
|
||||
case `errored`:
|
||||
ui.notifications.error(response.error);
|
||||
case `fronted`:
|
||||
return;
|
||||
};
|
||||
|
||||
if (!response.answers) { return };
|
||||
|
||||
const fullAttrs = mergeObject(attrs, response.answers);
|
||||
game.settings.set(__ID__, `actorDefaultAttributes`, fullAttrs);
|
||||
ui.notifications.success(`taf.notifs.success.saved-default-attributes`);
|
||||
};
|
||||
// #endregion Actions
|
||||
|
||||
// #region Drag & Drop
|
||||
_canDragStart() {
|
||||
return this.#doc.isOwner;
|
||||
};
|
||||
|
||||
_canDragDrop() {
|
||||
return this.#doc.isOwner;
|
||||
};
|
||||
|
||||
_onDragStart(event) {
|
||||
const target = event.currentTarget.closest(`[data-attribute]`);
|
||||
if (`link` in event.target.dataset) { return };
|
||||
|
|
|
|||
|
|
@ -1,6 +1,24 @@
|
|||
import { __ID__ } from "../consts.mjs";
|
||||
|
||||
const { Actor } = foundry.documents;
|
||||
const { hasProperty } = foundry.utils;
|
||||
|
||||
export class TAFActor extends Actor {
|
||||
|
||||
// #region Lifecycle
|
||||
async _preCreate(data, options, user) {
|
||||
|
||||
// Assign the defaults from the world setting if they exist
|
||||
const defaults = game.settings.get(__ID__, `actorDefaultAttributes`) ?? {};
|
||||
if (!hasProperty(data, `system.attr`)) {
|
||||
const value = game.release.generation > 13 ? _replace(defaults) : defaults;
|
||||
this.updateSource({ "system.==attr": value });
|
||||
};
|
||||
|
||||
return super._preCreate(data, options, user);
|
||||
};
|
||||
// #endregion Lifecycle
|
||||
|
||||
async modifyTokenAttribute(attribute, value, isDelta = false, isBar = true) {
|
||||
const attr = foundry.utils.getProperty(this.system, attribute);
|
||||
const current = isBar ? attr.value : attr;
|
||||
|
|
|
|||
38
module/hooks/renderSettingsConfig.mjs
Normal file
38
module/hooks/renderSettingsConfig.mjs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { __ID__ } from "../consts.mjs";
|
||||
|
||||
Hooks.on(`renderSettingsConfig`, (app, html, context, options) => {
|
||||
/*
|
||||
This section is used to insert a button into the settings config that unsets
|
||||
a world setting when it exists but doesn't allow any other form of editing it.
|
||||
*/
|
||||
if (game.user.isGM && game.settings.get(__ID__, `actorDefaultAttributes`)) {
|
||||
const formGroup = document.createElement(`div`);
|
||||
formGroup.classList = `form-group`;
|
||||
|
||||
const label = document.createElement(`div`);
|
||||
label.innerHTML = _loc(`taf.settings.actorDefaultAttributes.name`);
|
||||
|
||||
const formFields = document.createElement(`div`);
|
||||
formFields.classList = `form-fields`;
|
||||
|
||||
const button = document.createElement(`button`);
|
||||
button.type = `button`;
|
||||
button.innerHTML = _loc(`taf.settings.actorDefaultAttributes.label`);
|
||||
button.addEventListener(`click`, () => {
|
||||
game.settings.set(__ID__, `actorDefaultAttributes`, undefined);
|
||||
});
|
||||
|
||||
const hint = document.createElement(`p`);
|
||||
hint.classList = `hint`;
|
||||
hint.innerHTML = _loc(`taf.settings.actorDefaultAttributes.hint`);
|
||||
|
||||
formFields.appendChild(button);
|
||||
formGroup.appendChild(label);
|
||||
formGroup.appendChild(formFields);
|
||||
formGroup.appendChild(hint);
|
||||
|
||||
/** @type {HTMLElement|undefined} */
|
||||
const tab = html.querySelector(`.tab[data-group="categories"][data-tab="system"]`);
|
||||
tab?.insertAdjacentElement(`afterbegin`, formGroup);
|
||||
};
|
||||
});
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import "./api.mjs";
|
||||
import "./hooks/init.mjs";
|
||||
import "./hooks/userConnected.mjs";
|
||||
import "./hooks/renderSettingsConfig.mjs";
|
||||
|
|
|
|||
|
|
@ -59,4 +59,10 @@ export function registerWorldSettings() {
|
|||
}),
|
||||
scope: `world`,
|
||||
});
|
||||
|
||||
game.settings.register(__ID__, `actorDefaultAttributes`, {
|
||||
config: false,
|
||||
type: Object,
|
||||
scope: `world`,
|
||||
});
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue