197 lines
5.7 KiB
JavaScript
197 lines
5.7 KiB
JavaScript
import {
|
|
DGPercentileRoll,
|
|
DGLethalityRoll,
|
|
DGDamageRoll,
|
|
DGSanityDamageRoll,
|
|
} from "../roll/roll.js";
|
|
|
|
/**
|
|
* Extend the basic ItemSheet with some very simple modifications
|
|
* @extends {ItemSheet}
|
|
*/
|
|
export default class DeltaGreenItemSheet extends foundry.appv1.sheets
|
|
.ItemSheet {
|
|
/** @override */
|
|
static get defaultOptions() {
|
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
|
classes: ["deltagreen", "sheet", "item"],
|
|
width: 520,
|
|
height: 600,
|
|
tabs: [
|
|
{
|
|
navSelector: ".sheet-tabs",
|
|
contentSelector: ".sheet-body",
|
|
initial: "attributes",
|
|
},
|
|
],
|
|
});
|
|
}
|
|
|
|
/** @override */
|
|
get template() {
|
|
const path = "systems/deltagreen/templates/item";
|
|
|
|
// unique item sheet by type, like `weapon-sheet.html`.
|
|
return `${path}/item-${this.item.type}-sheet.html`;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
async getData() {
|
|
const data = super.getData();
|
|
|
|
data.enrichedDescription =
|
|
await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
|
this.object.system.description,
|
|
{ async: true },
|
|
);
|
|
|
|
if (data.item.type === "tome" || data.item.type === "ritual") {
|
|
data.enrichedHandlerNotes =
|
|
await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
|
this.object.system.handlerNotes,
|
|
{ async: true },
|
|
);
|
|
}
|
|
|
|
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
|
|
// eslint-disable-next-line no-useless-return
|
|
if (!this.options.editable) return;
|
|
|
|
// Rollable abilities - bind to everything with the 'Rollable' class
|
|
html.find(".rollable").click(this._onRoll.bind(this));
|
|
html.find(".rollable").contextmenu(this._onRoll.bind(this)); // this is for right-click, which triggers the roll modifier dialogue for most rolls
|
|
|
|
// toggle whether information is shown to a player for a tome or not
|
|
html.find(".tome-show-hide-info").click((event) => {
|
|
event.preventDefault();
|
|
|
|
this.item.system.revealed = !this.item.system.revealed;
|
|
|
|
this.item.update({ "system.revealed": this.item.system.revealed });
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Handle clickable rolls.
|
|
*
|
|
* @param {Event} event The originating click event
|
|
* @async
|
|
* @private
|
|
*/
|
|
async _onRoll(event) {
|
|
event.preventDefault();
|
|
const element = event.currentTarget;
|
|
const { dataset } = element;
|
|
|
|
if (event && event.which === 2) {
|
|
// probably don't want rolls to trigger from a middle mouse click so just kill it here
|
|
return;
|
|
}
|
|
|
|
// const item = this.actor.items.get(dataset.iid);
|
|
const rollOptions = {
|
|
rollType: dataset.rolltype,
|
|
key: dataset.key,
|
|
// actor: this,
|
|
specialTrainingName: dataset?.name || null, // Only applies to Special Training Rolls
|
|
item: this.item,
|
|
};
|
|
|
|
// Create a default 1d100 roll just in case.
|
|
let roll = new Roll("1d100", {});
|
|
switch (dataset.rolltype) {
|
|
case "stat":
|
|
case "skill":
|
|
case "sanity":
|
|
case "special-training":
|
|
case "weapon":
|
|
roll = new DGPercentileRoll("1D100", {}, rollOptions);
|
|
break;
|
|
case "lethality":
|
|
roll = new DGLethalityRoll("1D100", {}, rollOptions);
|
|
break;
|
|
case "damage": {
|
|
let diceFormula = this.item.system.damage;
|
|
const { skill } = this.item.system;
|
|
if (
|
|
(this.actor.type === "agent" || this.actor.type === "npc") &&
|
|
(skill === "unarmed_combat" || skill === "melee_weapons")
|
|
) {
|
|
diceFormula +=
|
|
this.actor.system.statistics.str.meleeDamageBonusFormula;
|
|
}
|
|
roll = new DGDamageRoll(diceFormula, {}, rollOptions);
|
|
break;
|
|
}
|
|
case "sanity-damage": {
|
|
// Applies only to rituals, as far as Jalen can tell. Tomes have "study damage", but it just uses `sanity`.
|
|
const isLearntDamage = element.hasAttribute("data-san-on-learn");
|
|
|
|
const { successLoss, failedLoss } = isLearntDamage
|
|
? this.item.system.learnedSanity
|
|
: this.item.system.sanity;
|
|
const combinedFormula = `{${successLoss}, ${failedLoss}}`;
|
|
|
|
roll = new DGSanityDamageRoll(combinedFormula, {}, rollOptions);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
this.processRoll(event, roll, rollOptions);
|
|
}
|
|
|
|
/**
|
|
* Show a dialog for the roll and then send to chat.
|
|
* Broke this logic out from `_onRoll()` so that other files can call it,
|
|
* namely the macro logic.
|
|
*
|
|
* TODO: Move this logic to the roll.js.
|
|
*
|
|
* @param {Event} event The originating click event
|
|
* @param {Event} roll The roll to show a dialog for and then send to chat.
|
|
* @async
|
|
*/
|
|
async processRoll(event, roll) {
|
|
// Open dialog if user requests it (no dialog for Sanity Damage rolls)
|
|
if (
|
|
(event.shiftKey || event.which === 3) &&
|
|
!(roll instanceof DGSanityDamageRoll)
|
|
) {
|
|
const dialogData = await roll.showDialog();
|
|
if (!dialogData) return;
|
|
if (dialogData.newFormula) {
|
|
roll = new DGDamageRoll(dialogData.newFormula, {}, roll.options);
|
|
}
|
|
roll.modifier += dialogData.targetModifier;
|
|
roll.options.rollMode = dialogData.rollMode;
|
|
}
|
|
// Evaluate the roll.
|
|
await roll.evaluate();
|
|
// Send the roll to chat.
|
|
roll.toChat();
|
|
}
|
|
}
|