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); } }