/** * Base Sheet for Actor and Npc */ export class BaseSheetL5r5e extends ActorSheet { /** * Commons options */ static get defaultOptions() { return foundry.utils.mergeObject(super.defaultOptions, { classes: ["l5r5e", "sheet", "actor"], // template: CONFIG.l5r5e.paths.templates + "actors/character-sheet.html", width: 600, height: 800, tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "skills" }], dragDrop: [{ dragSelector: ".item-list .item", dropSelector: null }], }); } /** * Add the SendToChat button on top of sheet * @override */ _getHeaderButtons() { let buttons = super._getHeaderButtons(); // Send To Chat buttons.unshift({ label: game.i18n.localize("l5r5e.global.send_to_chat"), class: "send-to-chat", icon: "fas fa-comment-dots", onclick: () => game.l5r5e.HelpersL5r5e.debounce( "send2chat-" + this.object.id, () => game.l5r5e.HelpersL5r5e.sendToChat(this.object), 2000, true )(), }); return buttons; } /** @inheritdoc */ getData(options = {}) { const sheetData = super.getData(options); sheetData.data.dtypes = ["String", "Number", "Boolean"]; // Sort Items by name sheetData.items.sort((a, b) => { return a.name.localeCompare(b.name); }); return sheetData; } /** * Return a light sheet if in "limited" state * @override */ get template() { if (!game.user.isGM && this.actor.limited) { return `${CONFIG.l5r5e.paths.templates}actors/limited-sheet.html`; } return this.options.template; } /** * Activate a named TinyMCE text editor * @param {string} name The named data field which the editor modifies. * @param {object} options TinyMCE initialization options passed to TextEditor.create * @param {string} initialContent Initial text content for the editor area. * @override */ activateEditor(name, options = {}, initialContent = "") { if (["data.notes", "data.description"].includes(name) && initialContent) { initialContent = game.l5r5e.HelpersL5r5e.convertSymbols(initialContent, false); } super.activateEditor(name, options, initialContent); } /** * This method is called upon form submission after form data is validated * @param event {Event} The initial triggering submission event * @param formData {Object} The object of validated form data with which to update the object * @returns {Promise} A Promise which resolves once the update operation has completed * @override */ async _updateObject(event, formData) { ["data.notes", "data.description"].forEach((name) => { if (!formData[name]) { return; } formData[name] = game.l5r5e.HelpersL5r5e.convertSymbols(formData[name], true); }); return super._updateObject(event, formData); } /** * Subscribe to events from the sheet. * @param {jQuery} html HTML content of the sheet. */ activateListeners(html) { super.activateListeners(html); // Commons game.l5r5e.HelpersL5r5e.commonListeners(html, this.actor); // *** Everything below here is only needed if the sheet is editable *** if (!this.isEditable) { return; } // On focus on one numeric element, select all text for better experience html.find(".select-on-focus").on("focus", (event) => { event.preventDefault(); event.stopPropagation(); event.target.select(); }); // *** Items : add, edit, delete *** html.find(".item-add").on("click", this._addSubItem.bind(this)); html.find(`.item-edit`).on("click", this._editSubItem.bind(this)); html.find(`.item-delete`).on("click", this._deleteSubItem.bind(this)); } /** * Add a generic item with sub type * @param {string} type Item sub type (armor, weapon, bond...) * @return {Promise} * @private */ async _createSubItem({ type }) { if (!type) { return; } const created = await this.actor.createEmbeddedDocuments("Item", [ { name: game.i18n.localize(`ITEM.Type${type.capitalize()}`), type: type, img: `${CONFIG.l5r5e.paths.assets}icons/items/${type}.svg`, }, ]); if (created?.length < 1) { return; } const item = this.actor.items.get(created[0].id); item.sheet.render(true); } /** * Add a generic item with sub type * @param {Event} event * @private */ async _addSubItem(event) { event.preventDefault(); event.stopPropagation(); const type = $(event.currentTarget).data("item-type"); if (!type) { return; } return this._createSubItem({ type }); } /** * Edit a generic item with sub type * @param {Event} event * @private */ _editSubItem(event) { event.preventDefault(); event.stopPropagation(); game.l5r5e.HelpersL5r5e.getEmbedItemByEvent(event, this.actor).then((item) => { if (item) { item.sheet.render(true); } }); } /** * Delete a generic item with sub type * @param {Event} event * @private */ _deleteSubItem(event) { event.preventDefault(); event.stopPropagation(); const itemId = $(event.currentTarget).data("item-id"); if (!itemId) { return; } const tmpItem = this.actor.items.get(itemId); if (!tmpItem) { return; } const callback = async () => { return this.actor.deleteEmbeddedDocuments("Item", [itemId]); }; // Holing Ctrl = without confirm if (event.ctrlKey) { return callback(); } game.l5r5e.HelpersL5r5e.confirmDeleteDialog( game.i18n.format("l5r5e.global.delete_confirm", { name: tmpItem.name }), callback ); } }