import KHDiceRoller from "../helpers/dice-helper.js"
/**
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
export class ActorSheetKH extends ActorSheet {
khRoller = new KHDiceRoller();
/** @override */
static get defaultOptions() {
return mergeObject(super.defaultOptions, {
classes: ["kopparhavet", "sheet", "actor"],
template: "systems/kopparhavet/templates/actors/character-sheet.html",
width: 710,
height: 650,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "skills" }],
scrollY: [".skills-tab .skills", ".talent-tab .items"],
});
}
/** @override */
get template() {
const path = "systems/kopparhavet/templates/actors";
return `${path}/${this.actor.data.type}-sheet.html`;
}
/* -------------------------------------------- */
/** @override */
getData() {
const data = super.getData();
data.dtypes = ["String", "Number", "Boolean"];
// Prepare items.
this._prepareCharacterItems(data);
return data;
}
/**
* Organize and classify Items for Character sheets.
* @param {Object} actorData The actor to prepare.
* @return {undefined}
*/
_prepareCharacterItems(sheetData) {
const actorData = sheetData.actor;
// Initialize containers.
const skills = [];
const talents = [];
const weapons = [];
const armor = [];
const gear = [];
const spells = [];
const attacks = [];
// Iterate through items, allocating to containers
for (let i of sheetData.items) {
i.img = i.img || DEFAULT_TOKEN;
// Append to gear.
if (i.type === "skill") {
i.hasBase = i.data.type.value === "base";
i.hasAdventure = i.data.type.value === "adventure";
i.hasCombat = i.data.type.value === "combat";
skills.push(i);
} else if (i.type === "talent") {
talents.push(i);
} else if (i.type === "weapon") {
weapons.push(i);
} else if (i.type === "armor") {
armor.push(i);
} else if (i.type === "gear") {
gear.push(i);
} else if (i.type === "spell") {
spells.push(i);
} else if (i.type === "adversaryAttack") {
attacks.push(i);
}
}
skills.sort((a,b) => (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0));
// Assign and return
actorData.skills = skills;
actorData.talents = talents;
actorData.weapons = weapons;
actorData.armor = armor;
actorData.gear = gear;
actorData.spells = spells;
actorData.attacks = attacks;
}
/* -------------------------------------------- */
/** @override */
activateListeners(html) {
super.activateListeners(html);
/* Send item Details to chat */
new ContextMenu(html, "li.item", [
{
name: game.i18n.localize("MENU.SENTTOCHAT"),
icon: '',
callback: (li) => {
let itemId = li.data("itemId");
this._itemDetailsToChat(itemId);
},
},
]);
new ContextMenu(html, "div.item", [
{
name: game.i18n.localize("MENU.SENTTOCHAT"),
icon: '',
callback: (li) => {
let itemId = li.data("itemId");
this._itemDetailsToChat(itemId);
},
},
]);
html.find(".feature").click(async (ev) => {
const featureName = $(ev.currentTarget).data("feature");
const featureValue = this.actor.data.data.feature[featureName].value;
if (featureName === "one") {
this.actor.update({ "data.feature.one.value": !featureValue });
} else if (featureName === "two") {
this.actor.update({ "data.feature.two.value": !featureValue });
} else if (featureName === "three") {
this.actor.update({ "data.feature.three.value": !featureValue });
} else if (featureName === "four") {
this.actor.update({ "data.feature.four.value": !featureValue });
}
this._render();
});
// 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));
});
// Edit Inventory Item
html.find(".item-edit").click(async (ev) => {
const li = $(ev.currentTarget).parents(".item");
let itemId = li.data("itemId");
let item = this.actor.getOwnedItem(itemId);
if (!item) {
item = game.items.get(itemId);
if (!item) {
console.log("IMPORT ERROR")
}
}
if (item?.sheet) {
item.sheet.render(true);
}
});
/* Roll spell cost */
html.find(".roll-spell-cost").click((ev) => {
const li = $(ev.currentTarget).parents(".item");
let itemId = li.data("itemId");
let spell = this.actor.getOwnedItem(itemId);
if (!spell) {
spell = game.items.get(itemId);
if (!spell) {
console.log("IMPORT ERROR")
return
}
}
let cost = spell.data.data.cost.value;
let regex = /([0-9]*)t([0-9]*)/g;
let regexMatch;
while (regexMatch = regex.exec(cost.toLowerCase())) {
this.khRoller.rollSpellCostInChat(regexMatch[1], this.actor)
}
});
/* Roll skill */
html.find(".roll-skill").click((ev) => {
const li = $(ev.currentTarget).parents(".item");
let itemId = li.data("itemId");
let _item = this.actor.items.find((element) => element._id == itemId);
let skillName = _item.name
let skillValue = _item.data.data.value
let showValue = false
if(this.actor.data.type === "character") {
showValue = true
}
this.khRoller.rollSkillInChat(skillName, skillValue, showValue, this.actor)
});
/* Roll weapon skill */
html.find(".roll-weapon-skill").click((ev) => {
const li = $(ev.currentTarget).parents(".item");
let itemId = li.data("itemId");
let weapon = this.actor.getOwnedItem(itemId);
if (!weapon) {
weapon = game.items.get(itemId);
if (!weapon) {
console.log("IMPORT ERROR")
return
}
}
// Retrieve skill based on name
let skill = this.actor.items.find((element) => element.name === weapon.data.data.skill.value);
let skillName = skill.name
let skillValue = skill.data.data.value
let showValue = false
if(this.actor.data.type === "character") {
showValue = true
}
this.khRoller.rollSkillInChat(skillName, skillValue, showValue, this.actor)
});
/* Roll weapon damage */
html.find(".roll-damage").click((ev) => {
const li = $(ev.currentTarget).parents(".item");
let itemId = li.data("itemId");
let weapon = this.actor.getOwnedItem(itemId);
if (!weapon) {
weapon = game.items.get(itemId);
if (!weapon) {
console.log("IMPORT ERROR")
return
}
}
let damage = weapon.data.data.damage.value;
let regex = /([0-9]*)t([0-9]*)/g;
let regexMatch;
while (regexMatch = regex.exec(damage.toLowerCase())) {
this.khRoller.rollDamageInChat(regexMatch[1], this.actor)
}
});
/* Roll armor */
html.find(".roll-armor").click((ev) => {
const li = $(ev.currentTarget).parents(".item");
let itemId = li.data("itemId");
let armor = this.actor.getOwnedItem(itemId);
if (!armor) {
armor = game.items.get(itemId);
if (!armor) {
console.log("IMPORT ERROR")
return
}
}
let defence = armor.data.data.defence.value;
let hasHelmet = false;
this.actor.items.map((i) => {
if(i.type === "armor") {
if(i.data.data.equipable.equipped && i.data.data.helmet.value) {
if(CONFIG.KH.armor_types[i.data.data.type.value]?.ac >= CONFIG.KH.armor_types[armor.data.data.type.value]?.ac) {
hasHelmet = true
}
}
}
});
let regex = /([0-9]*)t([0-9]*)/g;
let regexMatch;
while (regexMatch = regex.exec(defence.toLowerCase())) {
this.khRoller.rollArmorInChat(regexMatch[1], hasHelmet, this.actor)
}
});
/* Toggle item equipped */
html.find(".items .item a.toggle-equipped").click(this._toggleEquippedItem.bind(this));
/* Handle increase of items in inventory */
html.find(".item-quantity .quantity.increase").click(this._increaseQuantity.bind((this)));
/* Handle decrease of items in inventory */
html.find(".item-quantity .quantity.decrease").click(this._decreaseQuantity.bind(this));
/* CHANGE SKILL VALUE */
html.find(".skill-value").change(this._onChangeSkillValue.bind(this));
html.find(".click-skill").click(this._onClickSkill.bind(this));
/* Adversary specific */
html.find(".roll-adversary-attack").click((ev) => {
const li = $(ev.currentTarget).parents(".item");
let skillValue = li.data("ability");
console.log(skillValue)
let skillName = "ITEM.ATTACK";
this.khRoller.rollSkillInChat(skillName, skillValue, false, this.actor)
});
html.find(".roll-defence").click((ev) => {
const skillValue = $(ev.currentTarget).data("defence");
let skillName = "ADVERSARY.DEFENCE";
this.khRoller.rollSkillInChat(skillName, skillValue, false, this.actor)
});
html.find(".adversary-helmet-click").click((ev) => {
ev.preventDefault();
let helmetValue = this.actor.data.data.combat.helmet;
if(helmetValue === undefined || helmetValue === null) {
helmetValue = false
}
this.actor.update({ "data.combat.helmet": !helmetValue });
});
html.find(".roll-adversary-armor").click((ev) => {
const armor = $(ev.currentTarget).data("armor");
let hasHelmet = this.actor.data.data.combat.helmet;
if(hasHelmet === undefined || hasHelmet === null) {
hasHelmet = false;
}
let regex = /([0-9]*)t([0-9]*)/g;
let regexMatch;
while (regexMatch = regex.exec(armor.toLowerCase())) {
this.khRoller.rollArmorInChat(regexMatch[1], hasHelmet, this.actor)
}
});
}
async _toggleEquippedItem(event) {
const li = $(event.currentTarget);
const item = this.actor.getOwnedItem(li.data("itemId"));
const actor = this.actor;
if (item) {
if(item.type === "armor") {
let initValue = -1
if(item.data.data.equipable.equipped) {
initValue = 4
actor.items.map((i) => {
if(i.type === "armor") {
if(i._id !== item._id && i.data.data.equipable.equipped && i.data?.data?.modifications) {
for(let k of Object.keys(i.data.data.modifications)) {
if(i.data.data.modifications[k].modtype === "init") {
initValue = i.data.data.modifications[k].value;
}
}
}
}
});
} else {
if (item.data?.data?.modifications) {
for(let k of Object.keys(item.data.data.modifications)) {
if(item.data.data.modifications[k].modtype === "init") {
initValue = item.data.data.modifications[k].value;
}
}
}
}
if(initValue > 0) {
actor.update({ ["data.combat.init"]: initValue });
}
}
item.update({ ["data.equipable.equipped"]: !item.data.data.equipable.equipped });
} else {
console.log("Could not find item")
}
}
async _increaseQuantity(event) {
event.stopPropagation();
const li = $(event.currentTarget).parents(".item");
let itemId = li.data("itemId");
let item = this.actor.getOwnedItem(itemId);
if (!item) {
console.log("IMPORT ERROR")
}
item.update({ ["data.quantity.value"]: item.data.data.quantity.value + 1 });
}
async _decreaseQuantity(event) {
event.stopPropagation();
const li = $(event.currentTarget).parents(".item");
let itemId = li.data("itemId");
let item = this.actor.getOwnedItem(itemId);
if (!item) {
console.log("IMPORT ERROR")
}
let count = item.data.data.quantity.value - 1 > 0 ? item.data.data.quantity.value - 1 : 0;
item.update({ ["data.quantity.value"]: count });
}
async _onChangeSkillValue(event) {
event.preventDefault();
const itemId = $(event.currentTarget).data("item-id");
let _item = this.actor.items.find((element) => element._id == itemId);
if (_item) {
let update = {
_id: _item._id,
data: { value: $(event.currentTarget).val() },
};
await this.actor.updateEmbeddedEntity("OwnedItem", update);
}
}
async _onClickSkill(event) {
event.preventDefault();
const itemId = $(event.currentTarget).data("item-id");
let _item = this.actor.items.find((element) => element._id == itemId);
if (_item) {
let newVal = true;
if(_item.data.data.used !== undefined) {
newVal = !_item.data.data.used;
}
let update = {
_id: _item._id,
data: { used: newVal },
};
await this.actor.updateEmbeddedEntity("OwnedItem", update);
}
}
/**
* Send details of an item to chat.
* @private
*/
async _itemDetailsToChat(itemId) {
let item = this.actor.getOwnedItem(itemId);
if (!item) {
item = game.items.get(itemId);
}
if (!item) {
console.log("IMPORT ERROR")
return
}
const itemDetails = item?.getItemDetails();
const html = await renderTemplate("systems/kopparhavet/templates/chat/item-card.html", itemDetails);
const messageData = {
user: game.user._id,
type: CONST.CHAT_MESSAGE_TYPES.OTHER,
content: html,
speaker: {
actor: this.actor._id,
token: this.actor.token,
alias: this.actor.name,
},
};
ChatMessage.create(messageData);
}
}