280 lines
8.0 KiB
JavaScript
280 lines
8.0 KiB
JavaScript
import { AcksActor } from "./entity.js";
|
|
import { AcksEntityTweaks } from "../dialog/entity-tweaks.js";
|
|
|
|
export class AcksActorSheet extends ActorSheet {
|
|
constructor(...args) {
|
|
super(...args);
|
|
}
|
|
/* -------------------------------------------- */
|
|
|
|
getData() {
|
|
const data = super.getData();
|
|
|
|
data.config = CONFIG.ACKS;
|
|
// Settings
|
|
data.config.ascendingAC = game.settings.get("acks", "ascendingAC");
|
|
data.config.encumbrance = game.settings.get("acks", "encumbranceOption");
|
|
|
|
// Prepare owned items
|
|
this._prepareItems(data);
|
|
|
|
return data;
|
|
}
|
|
|
|
_createEditor(target, editorOptions, initialContent) {
|
|
// remove some controls to the editor as the space is lacking
|
|
if (target == "data.details.description") {
|
|
editorOptions.toolbar = "styleselect bullist hr table removeFormat save";
|
|
}
|
|
super._createEditor(target, editorOptions, initialContent);
|
|
}
|
|
|
|
/**
|
|
* Organize and classify Owned Items for Character sheets
|
|
* @private
|
|
*/
|
|
_prepareItems(data) {
|
|
// Partition items by category
|
|
let [items, weapons, armors, abilities, spells] = data.items.reduce(
|
|
(arr, item) => {
|
|
// Classify items into types
|
|
if (item.type === "item") arr[0].push(item);
|
|
else if (item.type === "weapon") arr[1].push(item);
|
|
else if (item.type === "armor") arr[2].push(item);
|
|
else if (item.type === "ability") arr[3].push(item);
|
|
else if (item.type === "spell") arr[4].push(item);
|
|
return arr;
|
|
},
|
|
[[], [], [], [], []]
|
|
);
|
|
|
|
// Sort spells by level
|
|
var sortedSpells = {};
|
|
var slots = {};
|
|
for (var i = 0; i < spells.length; i++) {
|
|
let lvl = spells[i].data.lvl;
|
|
if (!sortedSpells[lvl]) sortedSpells[lvl] = [];
|
|
if (!slots[lvl]) slots[lvl] = 0;
|
|
slots[lvl] += spells[i].data.memorized;
|
|
sortedSpells[lvl].push(spells[i]);
|
|
}
|
|
data.slots = {
|
|
used: slots,
|
|
};
|
|
// Assign and return
|
|
data.owned = {
|
|
items: items,
|
|
weapons: weapons,
|
|
armors: armors,
|
|
};
|
|
data.abilities = abilities;
|
|
data.spells = sortedSpells;
|
|
}
|
|
|
|
_onItemSummary(event) {
|
|
event.preventDefault();
|
|
let li = $(event.currentTarget).parents(".item"),
|
|
item = this.actor.getOwnedItem(li.data("item-id")),
|
|
description = TextEditor.enrichHTML(item.data.data.description);
|
|
// Toggle summary
|
|
if (li.hasClass("expanded")) {
|
|
let summary = li.parents(".item-entry").children(".item-summary");
|
|
summary.slideUp(200, () => summary.remove());
|
|
} else {
|
|
// Add item tags
|
|
let div = $(
|
|
`<div class="item-summary"><ol class="tag-list">${item.getTags()}</ol><div>${description}</div></div>`
|
|
);
|
|
li.parents(".item-entry").append(div.hide());
|
|
div.slideDown(200);
|
|
}
|
|
li.toggleClass("expanded");
|
|
}
|
|
|
|
async _onSpellChange(event) {
|
|
event.preventDefault();
|
|
const itemId = event.currentTarget.closest(".item").dataset.itemId;
|
|
const item = this.actor.getOwnedItem(itemId);
|
|
if (event.target.dataset.field == "cast") {
|
|
return item.update({ "data.cast": parseInt(event.target.value) });
|
|
} else if (event.target.dataset.field == "memorize") {
|
|
return item.update({
|
|
"data.memorized": parseInt(event.target.value),
|
|
});
|
|
}
|
|
}
|
|
|
|
async _resetSpells(event) {
|
|
let spells = $(event.currentTarget)
|
|
.closest(".inventory.spells")
|
|
.find(".item");
|
|
spells.each((_, el) => {
|
|
let itemId = el.dataset.itemId;
|
|
const item = this.actor.getOwnedItem(itemId);
|
|
item.update({
|
|
_id: item.id,
|
|
"data.cast": item.data.data.memorized,
|
|
});
|
|
});
|
|
}
|
|
|
|
activateListeners(html) {
|
|
super.activateListeners(html);
|
|
|
|
|
|
// Item summaries
|
|
html
|
|
.find(".item .item-name h4")
|
|
.click((event) => this._onItemSummary(event));
|
|
|
|
html.find(".item .item-controls .item-show").click(async (ev) => {
|
|
const li = $(ev.currentTarget).parents(".item");
|
|
const item = this.actor.getOwnedItem(li.data("itemId"));
|
|
item.show();
|
|
});
|
|
|
|
html.find(".saving-throw .attribute-name a").click((ev) => {
|
|
let actorObject = this.actor;
|
|
let element = event.currentTarget;
|
|
let save = element.parentElement.parentElement.dataset.save;
|
|
actorObject.rollSave(save, { event: event });
|
|
});
|
|
|
|
html.find(".item .item-rollable .item-image").click(async (ev) => {
|
|
const li = $(ev.currentTarget).parents(".item");
|
|
const item = this.actor.getOwnedItem(li.data("itemId"));
|
|
if (item.type == "weapon") {
|
|
if (this.actor.data.type === "monster") {
|
|
item.update({
|
|
data: { counter: { value: item.data.data.counter.value - 1 } },
|
|
});
|
|
}
|
|
item.rollWeapon({ skipDialog: ev.ctrlKey });
|
|
} else if (item.type == "spell") {
|
|
item.spendSpell({ skipDialog: ev.ctrlKey });
|
|
} else {
|
|
item.rollFormula({ skipDialog: ev.ctrlKey });
|
|
}
|
|
});
|
|
|
|
html
|
|
.find(".memorize input")
|
|
.click((ev) => ev.target.select())
|
|
.change(this._onSpellChange.bind(this));
|
|
|
|
html.find(".attack a").click((ev) => {
|
|
let actorObject = this.actor;
|
|
let element = event.currentTarget;
|
|
let attack = element.parentElement.parentElement.dataset.attack;
|
|
const rollData = {
|
|
actor: this.data,
|
|
roll: {},
|
|
};
|
|
actorObject.targetAttack(rollData, attack, {
|
|
type: attack,
|
|
skipDialog: ev.ctrlKey,
|
|
});
|
|
|
|
html.find(".spells .item-reset").click((ev) => {
|
|
this._resetSpells(ev);
|
|
});
|
|
});
|
|
|
|
html.find(".hit-dice .attribute-name a").click((ev) => {
|
|
let actorObject = this.actor;
|
|
actorObject.rollHitDice({ event: event });
|
|
});
|
|
|
|
html.find(".bhr .attribute-name a").click((ev) => {
|
|
let actorObject = this.actor;
|
|
actorObject.rollBHR({ event: event });
|
|
});
|
|
|
|
// Everything below here is only needed if the sheet is editable
|
|
if (!this.options.editable) return;
|
|
|
|
html
|
|
.find(".memorize input")
|
|
.click((ev) => ev.target.select())
|
|
.change(this._onSpellChange.bind(this));
|
|
|
|
|
|
html.find(".spells .item-reset").click((ev) => {
|
|
this._resetSpells(ev);
|
|
});
|
|
}
|
|
|
|
// Override to set resizable initial size
|
|
async _renderInner(...args) {
|
|
const html = await super._renderInner(...args);
|
|
this.form = html[0];
|
|
|
|
// Resize resizable classes
|
|
let resizable = html.find(".resizable");
|
|
if (resizable.length == 0) {
|
|
return;
|
|
}
|
|
resizable.each((_, el) => {
|
|
let heightDelta = this.position.height - this.options.height;
|
|
el.style.height = `${heightDelta + parseInt(el.dataset.baseSize)}px`;
|
|
});
|
|
return html;
|
|
}
|
|
|
|
async _onResize(event) {
|
|
super._onResize(event);
|
|
|
|
let html = $(this.form);
|
|
let resizable = html.find(".resizable");
|
|
if (resizable.length == 0) {
|
|
return;
|
|
}
|
|
// Resize divs
|
|
resizable.each((_, el) => {
|
|
let heightDelta = this.position.height - this.options.height;
|
|
el.style.height = `${heightDelta + parseInt(el.dataset.baseSize)}px`;
|
|
});
|
|
// Resize editors
|
|
let editors = html.find(".editor");
|
|
editors.each((id, editor) => {
|
|
let container = editor.closest(".resizable-editor");
|
|
if (container) {
|
|
let heightDelta = this.position.height - this.options.height;
|
|
editor.style.height = `${
|
|
heightDelta + parseInt(container.dataset.editorSize)
|
|
}px`;
|
|
}
|
|
});
|
|
}
|
|
|
|
_onConfigureActor(event) {
|
|
event.preventDefault();
|
|
new AcksEntityTweaks(this.actor, {
|
|
top: this.position.top + 40,
|
|
left: this.position.left + (this.position.width - 400) / 2,
|
|
}).render(true);
|
|
}
|
|
|
|
/**
|
|
* Extend and override the sheet header buttons
|
|
* @override
|
|
*/
|
|
_getHeaderButtons() {
|
|
let buttons = super._getHeaderButtons();
|
|
|
|
// Token Configuration
|
|
const canConfigure = game.user.isGM || this.actor.owner;
|
|
if (this.options.editable && canConfigure) {
|
|
buttons = [
|
|
{
|
|
label: game.i18n.localize("ACKS.dialog.tweaks"),
|
|
class: "configure-actor",
|
|
icon: "fas fa-code",
|
|
onclick: (ev) => this._onConfigureActor(ev),
|
|
},
|
|
].concat(buttons);
|
|
}
|
|
return buttons;
|
|
}
|
|
}
|