worldbuilding/module/actor-sheet.js
Matt Smith bac1d023c3 9: Add POC for actor templates
- This commit adds a proof-of-concept for retrieving actor template
  types. These are actors using the `worldbuilding.isTemplate` flag set
  to true. If there are multiple actors with that flag set to true,
  clicking the create actor button will pull up a prompt to choose from
  one of those types, which will then create an actor by duplicating
  the template actor's data. The flag will be unset on the new actor.
2020-08-31 02:09:21 +00:00

153 lines
4.9 KiB
JavaScript

import { ATTRIBUTE_TYPES } from "./constants.js";
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
export class SimpleActorSheet extends ActorSheet {
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: ["worldbuilding", "sheet", "actor"],
template: "systems/worldbuilding/templates/actor-sheet.html",
width: 600,
height: 600,
tabs: [{navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description"}],
dragDrop: [{dragSelector: ".item-list .item", dropSelector: null}]
});
}
/* -------------------------------------------- */
/** @override */
getData() {
const data = super.getData();
data.dtypes = ATTRIBUTE_TYPES;
for ( let attr of Object.values(data.data.attributes) ) {
attr.isCheckbox = attr.dtype === "Boolean";
attr.isResource = attr.dtype === "Resource";
}
data.shorthand = !!game.settings.get("worldbuilding", "macroShorthand");
return data;
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
// Handle rollable attributes.
html.find('.items .rollable').click(ev => {
let button = $(ev.currentTarget);
let r = new Roll(button.data('roll'), this.actor.getRollData());
const li = button.parents(".item");
const item = this.actor.getOwnedItem(li.data("itemId"));
r.roll().toMessage({
user: game.user._id,
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
flavor: `<h2>${item.name}</h2><h3>${button.text()}</h3>`
});
});
// Everything below here is only needed if the sheet is editable
if (!this.options.editable) return;
// Update Inventory Item
html.find('.item-edit').click(ev => {
const li = $(ev.currentTarget).parents(".item");
const item = this.actor.getOwnedItem(li.data("itemId"));
item.sheet.render(true);
});
// Delete Inventory Item
html.find('.item-delete').click(ev => {
const li = $(ev.currentTarget).parents(".item");
this.actor.deleteOwnedItem(li.data("itemId"));
li.slideUp(200, () => this.render(false));
});
// Add or Remove Attribute
html.find(".attributes").on("click", ".attribute-control", this._onClickAttributeControl.bind(this));
}
/* -------------------------------------------- */
/** @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;
}
/* -------------------------------------------- */
/**
* 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 objKeys = Object.keys(attrs);
let nk = Object.keys(attrs).length + 1;
let newValue = `attr${nk}`;
let newKey = document.createElement("div");
while ( objKeys.includes(newValue) ) {
++nk;
newValue = `attr${nk}`;
};
newKey.innerHTML = `<input type="text" name="data.attributes.attr${nk}.key" value="${newValue}"/>`;
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 Actor
return this.object.update(formData);
}
}