Some language refactoring Allow toggle even without editing perm Fix DnD on school rank with npc as they don't have it
338 lines
12 KiB
JavaScript
338 lines
12 KiB
JavaScript
/**
|
|
* Base Sheet for Actor and Npc
|
|
*/
|
|
export class BaseSheetL5r5e extends ActorSheet {
|
|
/**
|
|
* Commons options
|
|
*/
|
|
static get defaultOptions() {
|
|
return 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 }],
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Commons datas
|
|
* @override
|
|
*/
|
|
getData() {
|
|
const sheetData = super.getData();
|
|
|
|
sheetData.data.dtypes = ["String", "Number", "Boolean"];
|
|
sheetData.data.stances = CONFIG.l5r5e.stances;
|
|
sheetData.data.techniquesList = CONFIG.l5r5e.techniques;
|
|
|
|
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 (name === "data.notes.value" && 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) {
|
|
if (formData["data.notes.value"]) {
|
|
formData["data.notes.value"] = game.l5r5e.HelpersL5r5e.convertSymbols(formData["data.notes.value"], true);
|
|
}
|
|
return super._updateObject(event, formData);
|
|
}
|
|
|
|
/**
|
|
* Handle dropped data on the Actor sheet
|
|
*/
|
|
async _onDrop(event) {
|
|
// Check item type and subtype
|
|
const item = await game.l5r5e.HelpersL5r5e.getDragnDropTargetObject(event);
|
|
if (
|
|
!item ||
|
|
item.entity !== "Item" ||
|
|
!["item", "armor", "weapon", "technique", "peculiarity", "advancement"].includes(item.data.type)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
// Dropped a item with same "id" as one owned, add qte instead
|
|
if (item.data.data.quantity && this.actor.data.items) {
|
|
const tmpItem = this.actor.data.items.find((e) => e.name === item.name && e.type === item.type);
|
|
if (tmpItem && this._modifyQuantity(tmpItem._id, 1)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Babele and properties specific
|
|
if (item.data.data.properties && typeof Babele !== "undefined") {
|
|
item.data.data.properties = await Promise.all(
|
|
item.data.data.properties.map(async (property) => {
|
|
const gameProp = await game.l5r5e.HelpersL5r5e.getObjectGameOrPack(property.id, "Item");
|
|
if (gameProp) {
|
|
return { id: gameProp._id, name: gameProp.name };
|
|
}
|
|
return property;
|
|
})
|
|
);
|
|
}
|
|
|
|
// Item subtype specific
|
|
switch (item.data.type) {
|
|
case "advancement": // no break
|
|
case "peculiarity":
|
|
// Modify the bought at rank to the current actor rank
|
|
if (this.actor.data.data.identity?.school_rank) {
|
|
item.data.data.bought_at_rank = this.actor.data.data.identity.school_rank;
|
|
}
|
|
break;
|
|
|
|
case "technique":
|
|
// School_ability and mastery_ability, allow only 1 per type
|
|
if (CONFIG.l5r5e.techniques_school.includes(item.data.data.technique_type)) {
|
|
if (
|
|
Array.from(this.actor.items).some(
|
|
(e) =>
|
|
e.type === "technique" && e.data.data.technique_type === item.data.data.technique_type
|
|
)
|
|
) {
|
|
new Dialog({
|
|
title: game.i18n.localize("l5r5e.techniques.title"),
|
|
content: game.i18n.localize("l5r5e.techniques.only_one"),
|
|
buttons: {
|
|
ok: {
|
|
label: game.i18n.localize("l5r5e.global.ok"),
|
|
icon: '<i class="fas fa-check"></i>',
|
|
},
|
|
},
|
|
}).render(true);
|
|
return;
|
|
}
|
|
|
|
// No cost for schools
|
|
item.data.data.xp_cost = 0;
|
|
item.data.data.xp_used = 0;
|
|
item.data.data.in_curriculum = true;
|
|
} else {
|
|
// Check if technique is allowed for this character
|
|
if (!game.user.isGM && !this.actor.data.data.techniques[item.data.data.technique_type]) {
|
|
new Dialog({
|
|
title: game.i18n.localize("l5r5e.techniques.title"),
|
|
content: game.i18n.localize("l5r5e.techniques.not_allowed"),
|
|
buttons: {
|
|
ok: {
|
|
label: game.i18n.localize("l5r5e.global.ok"),
|
|
icon: '<i class="fas fa-check"></i>',
|
|
},
|
|
},
|
|
}).render(true);
|
|
return;
|
|
}
|
|
|
|
// Verify cost
|
|
item.data.data.xp_cost =
|
|
item.data.data.xp_cost > 0 ? item.data.data.xp_cost : CONFIG.l5r5e.xp.techniqueCost;
|
|
item.data.data.xp_used = item.data.data.xp_cost;
|
|
}
|
|
|
|
// Modify the bought at rank to the current actor rank
|
|
if (this.actor.data.data.identity?.school_rank) {
|
|
item.data.data.bought_at_rank = this.actor.data.data.identity.school_rank;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Ok add item - Foundry override cause props
|
|
const allowed = Hooks.call("dropActorSheetData", this.actor, this, item);
|
|
if (allowed === false) {
|
|
return;
|
|
}
|
|
return this._onDropItem(event, item);
|
|
}
|
|
|
|
/**
|
|
* Subscribe to events from the sheet.
|
|
* @param html HTML content of the sheet.
|
|
*/
|
|
activateListeners(html) {
|
|
super.activateListeners(html);
|
|
|
|
// Toggle
|
|
html.find(".toggle-on-click").on("click", (event) => {
|
|
const elmt = $(event.currentTarget).data("toggle");
|
|
const tgt = html.find("." + elmt);
|
|
tgt.hasClass("toggle-active") ? tgt.removeClass("toggle-active") : tgt.addClass("toggle-active");
|
|
});
|
|
|
|
// *** Everything below here is only needed if the sheet is editable ***
|
|
if (!this.options.editable) {
|
|
return;
|
|
}
|
|
|
|
// *** Dice event on Skills clic ***
|
|
html.find(".dice-picker").on("click", (event) => {
|
|
const li = $(event.currentTarget);
|
|
new game.l5r5e.DicePickerDialog({
|
|
skillId: li.data("skill") || null,
|
|
skillCatId: li.data("skillcat") || null,
|
|
difficulty: li.data("diff") || 2,
|
|
actor: this.actor,
|
|
}).render(true);
|
|
});
|
|
|
|
// On focus on one numeric element, select all text for better experience
|
|
html.find(".select-on-focus").on("focus", (event) => {
|
|
event.target.select();
|
|
});
|
|
|
|
// *** Items : add, edit, delete ***
|
|
html.find(".item-add").on("click", (event) => {
|
|
this._addSubItem(event);
|
|
});
|
|
html.find(`.item-edit`).on("click", (event) => {
|
|
this._editSubItem(event);
|
|
});
|
|
html.find(`.item-delete`).on("click", (event) => {
|
|
this._deleteSubItem(event);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Add a generic item with sub type
|
|
* @private
|
|
*/
|
|
async _addSubItem(event) {
|
|
const type = $(event.currentTarget).data("item-type");
|
|
const titles = {
|
|
item: "l5r5e.items.title_new",
|
|
armor: "l5r5e.armors.title_new",
|
|
weapon: "l5r5e.weapons.title_new",
|
|
technique: "l5r5e.techniques.title_new",
|
|
peculiarity: "l5r5e.peculiarities.title_new",
|
|
advancement: "l5r5e.advancements.title_new",
|
|
};
|
|
const created = await this.actor.createEmbeddedEntity("OwnedItem", {
|
|
name: game.i18n.localize(titles[type]),
|
|
type: type,
|
|
img: "icons/svg/mystery-man.svg",
|
|
});
|
|
const item = this.actor.getOwnedItem(created._id);
|
|
|
|
// assign current school rank to the new adv/tech
|
|
if (["advancement", "technique"].includes(item.data.type)) {
|
|
item.data.data.rank = this.actor.data.data.identity.school_rank;
|
|
item.data.data.bought_at_rank = this.actor.data.data.identity.school_rank;
|
|
}
|
|
|
|
item.sheet.render(true);
|
|
}
|
|
|
|
/**
|
|
* Edit a generic item with sub type
|
|
* @private
|
|
*/
|
|
_editSubItem(event) {
|
|
const itemId = $(event.currentTarget).data("item-id");
|
|
const item = this.actor.getOwnedItem(itemId);
|
|
item.sheet.render(true);
|
|
}
|
|
|
|
/**
|
|
* Delete a generic item with sub type
|
|
* @private
|
|
*/
|
|
_deleteSubItem(event) {
|
|
const itemId = $(event.currentTarget).data("item-id");
|
|
|
|
// Remove 1 qty if possible
|
|
const tmpItem = this.actor.getOwnedItem(itemId);
|
|
if (tmpItem && tmpItem.data.data.quantity > 1 && this._modifyQuantity(tmpItem._id, -1)) {
|
|
return;
|
|
}
|
|
|
|
// Specific advancements, remove 1 to selected ring/skill
|
|
if (tmpItem.type === "advancement") {
|
|
const actor = duplicate(this.actor.data.data);
|
|
const itmData = tmpItem.data.data;
|
|
if (itmData.advancement_type === "ring") {
|
|
// Ring
|
|
actor.rings[itmData.ring] = Math.max(1, actor.rings[itmData.ring] - 1);
|
|
} else {
|
|
// Skill
|
|
const skillCatId = CONFIG.l5r5e.skills.get(itmData.skill);
|
|
if (skillCatId) {
|
|
actor.skills[skillCatId][itmData.skill] = Math.max(0, actor.skills[skillCatId][itmData.skill] - 1);
|
|
}
|
|
}
|
|
|
|
// Update Actor
|
|
this.actor.update({
|
|
data: diffObject(this.actor.data.data, actor),
|
|
});
|
|
}
|
|
|
|
return this.actor.deleteOwnedItem(itemId);
|
|
}
|
|
|
|
/**
|
|
* Switch "in_curriculum"
|
|
* @private
|
|
*/
|
|
_switchSubItemCurriculum(event) {
|
|
const itemId = $(event.currentTarget).data("item-id");
|
|
const item = this.actor.getOwnedItem(itemId);
|
|
if (item.type !== "item") {
|
|
item.update({
|
|
data: {
|
|
in_curriculum: !item.data.data.in_curriculum,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add or subtract a quantity to a owned item
|
|
* @private
|
|
*/
|
|
_modifyQuantity(itemId, add) {
|
|
const tmpItem = this.actor.getOwnedItem(itemId);
|
|
if (tmpItem) {
|
|
tmpItem.data.data.quantity = Math.max(1, tmpItem.data.data.quantity + add);
|
|
tmpItem.update({
|
|
data: {
|
|
quantity: tmpItem.data.data.quantity,
|
|
},
|
|
});
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|