worldbuilding/module/item-sheet.js
Matt Smith a490448a15 9: Add formula (derived) attributes
- Added a new "Formula" attribute type for both actors and items.
- When iterating through attributes, formula attributes are noted in an
  array so that they can be iterated through in a second pass to
  evaluate their referenced attributes. The second iteration just
  handles attribute replacement rather than rolling the formula, that's
  deferred until the attributes are used in an actual roll formula.
- Items also have the same behavior. If an attribute isn't on the item,
  it will fall back to check the parent actor instead.
- Likely areas with issues would be formula attributes that reference
  other formula attributes, or item formula attributes that reference
  actor formula attributes.
2020-08-10 03:42:55 +00:00

115 lines
3.5 KiB
JavaScript

/**
* Extend the basic ItemSheet with some very simple modifications
* @extends {ItemSheet}
*/
export class SimpleItemSheet extends ItemSheet {
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: ["worldbuilding", "sheet", "item"],
template: "systems/worldbuilding/templates/item-sheet.html",
width: 520,
height: 480,
tabs: [{navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description"}]
});
}
/* -------------------------------------------- */
/** @override */
getData() {
const data = super.getData();
data.dtypes = ["String", "Number", "Boolean", "Formula"];
for ( let attr of Object.values(data.data.attributes) ) {
attr.isCheckbox = attr.dtype === "Boolean";
}
return data;
}
/* -------------------------------------------- */
/** @override */
setPosition(options={}) {
const position = super.setPosition(options);
const sheetBody = this.element.find(".sheet-body");
const bodyHeight = position.height - 192;
sheetBody.css("height", bodyHeight);
return position;
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
// Add or Remove Attribute
html.find(".attributes").on("click", ".attribute-control", this._onClickAttributeControl.bind(this));
}
/* -------------------------------------------- */
/**
* Listen for click events on an attribute control to modify the composition of attributes in the sheet
* @param {MouseEvent} event The originating left click event
* @private
*/
async _onClickAttributeControl(event) {
event.preventDefault();
const a = event.currentTarget;
const action = a.dataset.action;
const attrs = this.object.data.data.attributes;
const form = this.form;
// Add new attribute
if ( action === "create" ) {
const nk = Object.keys(attrs).length + 1;
let newKey = document.createElement("div");
newKey.innerHTML = `<input type="text" name="data.attributes.attr${nk}.key" value="attr${nk}"/>`;
newKey = newKey.children[0];
form.appendChild(newKey);
await this._onSubmit(event);
}
// Remove existing attribute
else if ( action === "delete" ) {
const li = a.closest(".attribute");
li.parentElement.removeChild(li);
await this._onSubmit(event);
}
}
/* -------------------------------------------- */
/** @override */
_updateObject(event, formData) {
// Handle the free-form attributes list
const formAttrs = expandObject(formData).data.attributes || {};
const attributes = Object.values(formAttrs).reduce((obj, v) => {
let k = v["key"].trim();
if ( /[\s\.]/.test(k) ) return ui.notifications.error("Attribute keys may not contain spaces or periods");
delete v["key"];
obj[k] = v;
return obj;
}, {});
// Remove attributes which are no longer used
for ( let k of Object.keys(this.object.data.data.attributes) ) {
if ( !attributes.hasOwnProperty(k) ) attributes[`-=${k}`] = null;
}
// Re-combine formData
formData = Object.entries(formData).filter(e => !e[0].startsWith("data.attributes")).reduce((obj, e) => {
obj[e[0]] = e[1];
return obj;
}, {_id: this.object._id, "data.attributes": attributes});
// Update the Item
return this.object.update(formData);
}
}