commit c3a7388592ff2e851f9fd666609e54d092d777c7 Author: Erebus Date: Sat Nov 28 19:12:19 2020 +0100 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..23f021a --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.idea/ +*.code-workspace +.history/ +deploy +createRelease +TODO \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/lang/en.json b/lang/en.json new file mode 100644 index 0000000..7214beb --- /dev/null +++ b/lang/en.json @@ -0,0 +1,87 @@ +{ + "ADVERSARY.ATTACKSKILL": "Attack rating", + "ADVERSARY.DEFENCE": "Defence", + "ADVERSARY.HASHELMET": "Has a helmet", + + "ARMOR.HELMET": "Helmet", + "ARMOR.LIGHT": "Light", + "ARMOR.MEDIUM": "Medium", + "ARMOR.HEAVY": "Heavy", + + "BACKGROUND.TITLE": "Background", + "BACKGROUND.BIRTHPLACE": "Birthplace", + "BACKGROUND.SOCIALSTANDING": "Social standing", + "BACKGROUND.EVENTS": "Events", + "BACKGROUND.CONTACTS": "Contacts", + + "BIO.AGE": "Age", + "BIO.KIN": "Kin", + "BIO.NAME": "Name", + "BIO.PROFESSION": "Profession", + + "CURRENCY.QUARTER": "Quarter", + "CURRENCY.SHEKEL": "Shekel", + "CURRENCY.TITLE": "Currency", + + "DICE.ROLL": "Dice roll", + + "DIFFICULTY.SIMPLE": "Simple", + "DIFFICULTY.EASY": "Easy", + "DIFFICULTY.AVERAGE": "Average", + "DIFFICULTY.HARD": "Hard", + "DIFFICULTY.DAUNTING": "Daunting", + + "FEATURES.TITLE": "Features", + + "ITEM.ARMOR": "Rustning", + "ITEM.ATTACK": "Attack", + "ITEM.DAMAGE": "Damage", + "ITEM.DEFENCE": "Defence", + "ITEM.DESCRIPTION": "Description", + "ITEM.EQUIPABLE": "Equipable", + "ITEM.EQUIPPED": "Equipped", + "ITEM.GEAR": "Gear", + "ITEM.PRICE": "Price", + "ITEM.QUANTITY": "Quantity", + "ITEM.SKILL": "Skill", + "ITEM.SPELL": "Spell", + "ITEM.TALENT": "Talent", + "ITEM.WEAPON": "Weapon", + + "MENU.SENTTOCHAT": "Send To Chat", + + "MOD.INIT": "Initiative", + "MOD.TYPE": "Modification", + "MOD.VALUE": "Value", + + "ROLL.SUCCESS": "Success", + "ROLL.FAILURE": "Failure", + + "SKILL.TYPE": "Type", + "SKILL.BASE": "Basic", + "SKILL.ADVENTURE": "Adventure", + "SKILL.COMBAT": "Combat", + "SKILL.STARTVALUE": "Start value", + "SKILL.LANGUAGE": "Language", + + "SPELL.DIFFICULTY": "Difficulty", + "SPELL.ROLL": "Roll", + "SPELL.COST": "Cost", + + "STATS.HEALTH": "Health", + "STATS.MANA": "Mana", + "STATS.STAMINA": "Stamina", + + "TAB.BIO": "Background", + "TAB.COMBAT": "Combat", + "TAB.DESCRIPTION": "Description", + "TAB.GEAR": "Gear", + "TAB.MODIFICATION": "Modifications", + "TAB.NOTE": "Note", + "TAB.SKILLS": "Skills", + "TAB.TALENTS": "Talents and Spells", + + "WEAPON.CATEGORY": "Category", + "WEAPON.MELEE": "Melee", + "WEAPON.RANGED": "Ranged" +} \ No newline at end of file diff --git a/lang/sv.json b/lang/sv.json new file mode 100644 index 0000000..7e24d58 --- /dev/null +++ b/lang/sv.json @@ -0,0 +1,87 @@ +{ + "ADVERSARY.ATTACKSKILL": "Färdighetsvärde", + "ADVERSARY.DEFENCE": "Försvar", + "ADVERSARY.HASHELMET": "Har en hjälm", + + "ARMOR.HELMET": "Hjälm", + "ARMOR.LIGHT": "Lätt", + "ARMOR.MEDIUM": "Medel", + "ARMOR.HEAVY": "Tung", + + "BACKGROUND.TITLE": "Bakgrund", + "BACKGROUND.BIRTHPLACE": "Födelseort", + "BACKGROUND.SOCIALSTANDING": "Socialt stånd", + "BACKGROUND.EVENTS": "Händelser", + "BACKGROUND.CONTACTS": "Kontakter", + + "BIO.AGE": "Ålder", + "BIO.KIN": "Folkslag", + "BIO.NAME": "Namn", + "BIO.PROFESSION": "Värv", + + "CURRENCY.QUARTER": "Kvarting", + "CURRENCY.SHEKEL": "Shekel", + "CURRENCY.TITLE": "Mynt", + + "DICE.ROLL": "Tärningsslag", + + "DIFFICULTY.SIMPLE": "Mycket Lätt", + "DIFFICULTY.EASY": "Lätt", + "DIFFICULTY.AVERAGE": "Utmanande", + "DIFFICULTY.HARD": "Svår", + "DIFFICULTY.DAUNTING": "Mycket svårt", + + "FEATURES.TITLE": "Särdrag", + + "ITEM.ARMOR": "Rustning", + "ITEM.ATTACK": "Anfall", + "ITEM.DAMAGE": "Skada", + "ITEM.DEFENCE": "Skydd", + "ITEM.DESCRIPTION": "Beskrivning", + "ITEM.EQUIPABLE": "Bärbar", + "ITEM.EQUIPPED": "Utrustad", + "ITEM.GEAR": "Utrustning", + "ITEM.PRICE": "Pris", + "ITEM.QUANTITY": "Antal", + "ITEM.SKILL": "Färdighet", + "ITEM.SPELL": "Besvärjelse", + "ITEM.TALENT": "Förmåga", + "ITEM.WEAPON": "Vapen", + + "MENU.SENTTOCHAT": "Skicka till chat", + + "MOD.INIT": "Turordning", + "MOD.TYPE": "Modifikation", + "MOD.VALUE": "Värde", + + "ROLL.SUCCESS": "Lyckat", + "ROLL.FAILURE": "Misslyckat", + + "SKILL.TYPE": "Typ", + "SKILL.BASE": "Grundfärdigheter", + "SKILL.ADVENTURE": "Äventyrsfärdigheter", + "SKILL.COMBAT": "Stridsfärdigheter", + "SKILL.STARTVALUE": "Grundvärde", + "SKILL.LANGUAGE": "Språk", + + "SPELL.DIFFICULTY": "Svårighet", + "SPELL.ROLL": "Slag", + "SPELL.COST": "Kostnad", + + "STATS.HEALTH": "Hälsa", + "STATS.MANA": "Skuld", + "STATS.STAMINA": "Gard", + + "TAB.BIO": "Bakgrund", + "TAB.COMBAT": "Strid", + "TAB.DESCRIPTION": "Beskrivning", + "TAB.GEAR": "Utrustning", + "TAB.MODIFICATION": "Modifikationer", + "TAB.NOTE": "Anteckning", + "TAB.SKILLS": "Färdigheter", + "TAB.TALENTS": "Förmågor och Besvärjelser", + + "WEAPON.CATEGORY": "Vapentyp", + "WEAPON.MELEE": "Närstrid", + "WEAPON.RANGED": "Avstånd" +} \ No newline at end of file diff --git a/module/actors/actor-sheet.js b/module/actors/actor-sheet.js new file mode 100644 index 0000000..c830c7f --- /dev/null +++ b/module/actors/actor-sheet.js @@ -0,0 +1,501 @@ +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); + } + } + + // 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); + } +} diff --git a/module/actors/actor.js b/module/actors/actor.js new file mode 100644 index 0000000..e7433b2 --- /dev/null +++ b/module/actors/actor.js @@ -0,0 +1,18 @@ +/** + * Extend the base Actor entity. + * @extends {Actor} + */ +export class ActorKH extends Actor { + /** + * Augment the basic actor data with additional dynamic data. + */ + prepareData() { + super.prepareData(); + const actorData = this.data; + const data = actorData.data; + const flags = actorData.flags; + data.type = actorData.type; + + console.log("ACTOR DATA") + } +} \ No newline at end of file diff --git a/module/helpers/dice-helper.js b/module/helpers/dice-helper.js new file mode 100644 index 0000000..228791a --- /dev/null +++ b/module/helpers/dice-helper.js @@ -0,0 +1,124 @@ +export default class KHDiceRoller { + async rollSpellCostInChat(cost, speaker) { + let formula = cost + "d6" + + const roll = new Roll(formula); + + let res = roll.roll(); + + let rollData = { + name: "SPELL.COST", + res: res, + showFormula: true + }; + + const html = await renderTemplate("systems/kopparhavet/templates/dice/roll.html", rollData); + + await roll.toMessage({ + create: true, + content: html, + user: game.user._id, + speaker: { + actor: speaker._id, + token: speaker.token, + alias: speaker.name, + }, + }); + } + + async rollDamageInChat(facesNr, speaker) { + let formula = facesNr + "d6x6" + + const roll = new Roll(formula); + + let res = roll.roll(); + + let rollData = { + name: "ITEM.DAMAGE", + res: res, + showFormula: true + }; + + const html = await renderTemplate("systems/kopparhavet/templates/dice/roll.html", rollData); + + await roll.toMessage({ + create: true, + content: html, + user: game.user._id, + speaker: { + actor: speaker._id, + token: speaker.token, + alias: speaker.name, + }, + }); + } + + async rollArmorInChat(armor, hasHelmet, speaker) { + let formula = armor + "d6" + + if(hasHelmet) { + formula += "x6" + } + + const roll = new Roll(formula); + + let res = roll.roll(); + + let rollData = { + name: "ITEM.DEFENCE", + res: res, + showFormula: true + }; + + const html = await renderTemplate("systems/kopparhavet/templates/dice/roll.html", rollData); + + await roll.toMessage({ + create: true, + content: html, + user: game.user._id, + speaker: { + actor: speaker._id, + token: speaker.token, + alias: speaker.name, + }, + }); + } + + async rollSkillInChat(skillName, skillValue, showValue, speaker) { + const roll = new Roll(`1d100`); + + let res = roll.roll(); + + let computedName = skillName + + if(showValue) { + computedName += " (" + skillValue + ")" + } + + let rollData = { + name: computedName, + res: res + }; + + if(skillValue > 0) { + if(res.total <= skillValue) { + rollData.success = true + } else { + rollData.failure = true + } + } + + const html = await renderTemplate("systems/kopparhavet/templates/dice/roll.html", rollData); + + await roll.toMessage({ + create: true, + content: html, + user: game.user._id, + speaker: { + actor: speaker._id, + token: speaker.token, + alias: speaker.name, + }, + }); + } +} \ No newline at end of file diff --git a/module/helpers/item-helpers.js b/module/helpers/item-helpers.js new file mode 100644 index 0000000..81e26f0 --- /dev/null +++ b/module/helpers/item-helpers.js @@ -0,0 +1,35 @@ +export default class ItemHelpers { + static async itemUpdate(event, formData) { + formData = expandObject(formData); + + if (this.object.isOwned && this.object.actor?.compendium?.metadata) { + return; + } + + // Handle the free-form modifications list + const formAttrs = expandObject(formData)?.data?.modifications || {}; + + const modifications = Object.values(formAttrs).reduce((obj, v) => { + let k = v["key"].trim(); + delete v["key"]; + obj[k] = v; + return obj; + }, {}); + + // Remove modifications which are no longer used + if (this.object.data?.data?.modifications) { + for (let k of Object.keys(this.object.data.data.modifications)) { + if (!modifications.hasOwnProperty(k)) modifications[`-=${k}`] = null; + } + } + + // recombine modifications to formData + if (Object.keys(modifications).length > 0) { + setProperty(formData, `data.modifications`, modifications); + } + + // Update the Item + this.item.data.flags.loaded = false; + this.object.update(formData); + } +} \ No newline at end of file diff --git a/module/items/item-sheet.js b/module/items/item-sheet.js new file mode 100644 index 0000000..ec78067 --- /dev/null +++ b/module/items/item-sheet.js @@ -0,0 +1,157 @@ +/** + * Extend the basic ItemSheet + * @extends {ItemSheet} + */ +import ItemHelpers from "../helpers/item-helpers.js"; + +export class ItemSheetKH extends ItemSheet { + /** @override */ + static get defaultOptions() { + return mergeObject(super.defaultOptions, { + classes: ["starwarsffg", "sheet", "item"], + tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }], + scrollY: [".sheet-body", ".tab"], + }); + } + + /** @override */ + get template() { + const path = "systems/kopparhavet/templates/items"; + return `${path}/${this.item.data.type}-sheet.html`; + } + + /** @override */ + async getData() { + const data = super.getData(); + let skillList = []; + + data.dtypes = ["String", "Number", "Boolean"]; + + if (data?.data?.modifications) { + for (let attr of Object.values(data.data.modifications)) { + attr.isCheckbox = attr.dtype === "Boolean"; + } + } + + switch (this.object.data.type) { + case "weapon": + // Load Skills Compendium skills + let skillList2 = await game.packs.get("kopparhavet.skills").getContent(); + + for (let item of skillList2) { + if(item.data.type === "skill" && item.data.data.type.value === "combat") { + skillList.push(item) + } + } + + // Retrieve any created skills as well + for (let item of game.items.entities) { + if(item.data.type === "skill" && item.data.data.type.value === "combat") { + skillList.push(item) + } + } + this.position.width = 530; + this.position.height = 750; + break; + case "armor": + this.position.width = 385; + this.position.height = 628; + break; + case "gear": + case "skill": + this.position.width = 385; + this.position.height = 606; + break; + case "talent": + this.position.width = 405; + this.position.height = 570; + break; + default: + this.position.width = 450; + this.position.height = 605; + break; + } + + data.khskills = skillList; + data.KH = CONFIG.KH; + + return data; + } + + activateListeners(html) { + super.activateListeners(html); + + // Armor related + html.find(".item-bool-click").click(async (ev) => { + const clickedName = $(ev.currentTarget).data("name"); + + if(clickedName === "equipped") { + const equppiedValue = this.item.data.data.equipable.equipped; + + this.item.update({ "data.equipped.equipped": !equppiedValue }); + } else { + const clickedValue = (this.item.data.data[clickedName].value == undefined ? false : this.item.data.data[clickedName].value); + let dataName = "data." + clickedName + ".value" + let tempData = {} + + tempData[dataName] = !clickedValue + + this.item.update(tempData); + } + + this._render(); + }); + + // Add or Remove modification + html.find(".modification-control").click(this._onClickModificationControl.bind(this)); + } + + /** + * DOM event + * @param {object} event + */ + async _onClickModificationControl(event) { + event.preventDefault(); + const a = event.currentTarget; + const action = a.dataset.action; + const attrs = this.object.data.data.modifications; + const form = this.form; + + // Add new modification + if (action === "create") { + const nk = new Date().getTime(); + let newKey = document.createElement("div"); + + newKey.innerHTML = ``; + + form.appendChild(newKey); + await this._onSubmit(event); + } + + // Remove existing modification + else if (action === "delete") { + const li = a.closest(".modification"); + li.parentElement.removeChild(li); + await this._onSubmit(event); + } + } + + /** @override */ + _updateObject(event, formData) { + const itemUpdate = ItemHelpers.itemUpdate.bind(this); + itemUpdate(event, formData); + } + + _getHeaderButtons() { + let buttons = super._getHeaderButtons(); + buttons = [ + { + label: "Post Item", + class: "item-post", + icon: "fas fa-comment", + onclick: (ev) => this.item.sendToChat(), + } + ].concat(buttons); + return buttons; + } +} diff --git a/module/items/item.js b/module/items/item.js new file mode 100644 index 0000000..8526cc0 --- /dev/null +++ b/module/items/item.js @@ -0,0 +1,62 @@ +/** + * Extend the basic Item. + * @extends {Item} + */ +export class ItemKH extends Item { + prepareData() { + super.prepareData(); + } + + async sendToChat() { + const itemData = duplicate(this.data); + + if(itemData.img.includes("/mystery-man")) { + itemData.img = null; + } + + itemData.isArmor = itemData.type === "armor"; + itemData.isGear = itemData.type === "gear"; + itemData.isAdversaryAttack = itemData.type === "adversaryAttack"; + itemData.isSkill = itemData.type === "skill"; + itemData.isSpell = itemData.type === "spell"; + itemData.isTalent = itemData.type === "talent"; + itemData.isWeapon = itemData.type === "weapon"; + + const html = await renderTemplate("systems/kopparhavet/templates/chat/item-card.html", itemData); + + const chatData = { + user: game.user._id, + rollMode: game.settings.get("core", "rollMode"), + content: html, + }; + + if (["gmroll", "blindroll"].includes(chatData.rollMode)) { + chatData.whisper = ChatMessage.getWhisperRecipients("GM"); + } else if (chatData.rollMode === "selfroll") { + chatData.whisper = [game.user]; + } + + ChatMessage.create(chatData); + } + + /** + * Prepare and return details of the item for display in inventory or chat. + */ + getItemDetails() { + const itemData = duplicate(this.data); + + if(itemData.img.includes("/mystery-man")) { + itemData.img = null; + } + + itemData.isArmor = itemData.type === "armor"; + itemData.isGear = itemData.type === "gear"; + itemData.isAdversaryAttack = itemData.type === "adversaryAttack"; + itemData.isSkill = itemData.type === "skill"; + itemData.isSpell = itemData.type === "spell"; + itemData.isTalent = itemData.type === "talent"; + itemData.isWeapon = itemData.type === "weapon"; + + return itemData + } +} \ No newline at end of file diff --git a/module/kh-config.js b/module/kh-config.js new file mode 100644 index 0000000..ce7620f --- /dev/null +++ b/module/kh-config.js @@ -0,0 +1,26 @@ +export const KH = {}; + +KH.armor_mod_types = { + "init": { + "value": "init", + "label": "MOD.INIT", + }, +}; + +KH.armor_types = { + "light": { + "value": "light", + "label": "ARMOR.LIGHT", + "ac": 10, + }, + "medium": { + "value": "medium", + "label": "ARMOR.MEDIUM", + "ac": 20, + }, + "heavy": { + "value": "heavy", + "label": "ARMOR.HEAVY", + "ac": 30, + }, +}; \ No newline at end of file diff --git a/module/kh-dice.js b/module/kh-dice.js new file mode 100644 index 0000000..8ea7d78 --- /dev/null +++ b/module/kh-dice.js @@ -0,0 +1,96 @@ +class KHDice { + static async Init(controls, html) { + const diceRollbtn = $( + ` +
  • + +
      + +
    +
  • + ` + ); + + html.append(diceRollbtn); + + html.find('.kh-dice-roller-popup li').click(ev => this._rollDice(ev, html)); + + diceRollbtn[0].addEventListener('click', ev => this.PopupSheet(ev, html)); + } + + static async _rollDice(event, html) { + var diceType = event.target.dataset.diceType; + var diceRoll = event.target.dataset.diceRoll; + var formula = diceRoll + "d" + diceType; + + let r = new Roll(formula); + + let res = r.roll(); + + let rollData = { + name: "DICE.ROLL", + res: res + }; + + const html2 = await renderTemplate("systems/kopparhavet/templates/dice/roll.html", rollData); + + await r.toMessage({ + user: game.user._id, + create: true, + content: html2 + }); + + this._close(html); + + } + + static async PopupSheet(evt, html) { + evt.stopPropagation(); + + if (html.find('.sdr-scene-control').hasClass('active')) { + this._close(html); + } else { + this._open(html); + } + } + + static async _close(html) { + html.find('#SDRpopup').hide(); + html.find('.sdr-scene-control').removeClass('active'); + html.find('.scene-control').first().addClass('active'); + } + + static async _open(html) { + html.find('.scene-control').removeClass('active'); + html.find('#SDRpopup').show(); + html.find('.sdr-scene-control').addClass('active'); + } +} + +Hooks.on('renderSceneControls', (controls, html) => { + KHDice.Init(controls, html); +}); + +console.log("Kopparhavet | Dice Roller loaded"); \ No newline at end of file diff --git a/module/kh-hooks.js b/module/kh-hooks.js new file mode 100644 index 0000000..d80b500 --- /dev/null +++ b/module/kh-hooks.js @@ -0,0 +1,23 @@ +export default class KHHooks { + static async onCreateActor(actor, options, userId) { + if (actor.data.type == "character") { + const actorbaseSkills = actor.data.data.baseSkills; + + // Check if skill already exists by some chance + const existingSkills = actor.items.filter((i) => i.type === ItemType.Skill).map((i) => i.name); + const skillsToAdd = actorbaseSkills.filter((s) => !existingSkills.includes(s)); + + // Load Skills Compendium skills + const skillIndex = await game.packs.get("kopparhavet.skills").getContent(); + + // Filter skillIndex array to include only skills for Actor Type. + let _skillsList = skillIndex.filter((i) => skillsToAdd.includes(i.data.name)); + + await actor.createEmbeddedEntity("OwnedItem", _skillsList); + } else { + setTimeout(async function () { + await actor.sheet.render(true); + }, 500); + } + } +} \ No newline at end of file diff --git a/module/kh-main.js b/module/kh-main.js new file mode 100644 index 0000000..b940cd8 --- /dev/null +++ b/module/kh-main.js @@ -0,0 +1,131 @@ +import { ItemSheetKH } from "./items/item-sheet.js"; +import { ItemKH } from "./items/item.js"; +import KHHooks from "./kh-hooks.js"; +import { ActorKH } from "./actors/actor.js"; +import { ActorSheetKH } from "./actors/actor-sheet.js"; +import { KH } from "./kh-config.js"; + +Hooks.once("init", () => { + CONFIG.Combat.initiative = { formula: "(@combat.init)d6kh2", decimals: 0 }; + + CONFIG.Actor.entityClass = ActorKH; + CONFIG.Item.entityClass = ItemKH; + + // Give global access to FFG config. + CONFIG.KH = KH; + + //registerFonts(); + registerSheets(); + preloadHandlebarsTemplates(); + registerHandlebarsHelpers(); + game.settings.register("kopparhavet", "worldSchemaVersion", { + name: "World Version", + hint: "Used to automatically upgrade worlds data when the system is upgraded.", + scope: "world", + config: true, + default: 0, + type: Number, + }); +}); + +Hooks.once("ready", () => { + //migrateWorld(); +}); + +/* POPULATE CHARACTER WITH DEFAULT SKILLS */ +Hooks.on("createActor", async (actor, options, userId) => KHHooks.onCreateActor(actor, options, userId)); + +function registerSheets() { + // Register sheet application classes + console.log("Registerting sheets") + + Actors.unregisterSheet("core", ActorSheet); + Actors.registerSheet("kopparhavet", ActorSheetKH, { types: ["character"], makeDefault: true }); + Actors.registerSheet("kopparhavet", ActorSheetKH, { types: ["adversary"], makeDefault: true }); + + Items.unregisterSheet("core", ItemSheet); + Items.registerSheet("kopparhavet", ItemSheetKH, { types: ["weapon"], makeDefault: true }); + Items.registerSheet("kopparhavet", ItemSheetKH, { types: ["armor"], makeDefault: true }); + Items.registerSheet("kopparhavet", ItemSheetKH, { types: ["gear"], makeDefault: true }); + Items.registerSheet("kopparhavet", ItemSheetKH, { types: ["skill"], makeDefault: true }); + Items.registerSheet("kopparhavet", ItemSheetKH, { types: ["spell"], makeDefault: true }); + Items.registerSheet("kopparhavet", ItemSheetKH, { types: ["talent"], makeDefault: true }); + Items.registerSheet("kopparhavet", ItemSheetKH, { types: ["adversaryAttack"], makeDefault: true }); +} + +function preloadHandlebarsTemplates() { + const templatePaths = [ + "systems/kopparhavet/templates/chat/item-card.html", + "systems/kopparhavet/templates/actors/adversary-sheet.html", + "systems/kopparhavet/templates/actors/character-sheet.html", + "systems/kopparhavet/templates/dice/roll.html", + "systems/kopparhavet/templates/items/adversaryAttack-sheet.html", + "systems/kopparhavet/templates/items/armor-sheet.html", + "systems/kopparhavet/templates/items/gear-sheet.html", + "systems/kopparhavet/templates/items/skill-sheet.html", + "systems/kopparhavet/templates/items/spell-sheet.html", + "systems/kopparhavet/templates/items/talent-sheet.html", + "systems/kopparhavet/templates/items/weapon-sheet.html", + "systems/kopparhavet/templates/parts/actor/bio.html", + "systems/kopparhavet/templates/parts/actor/combat.html", + "systems/kopparhavet/templates/parts/actor/gear.html", + "systems/kopparhavet/templates/parts/actor/skills.html", + "systems/kopparhavet/templates/parts/actor/talent.html", + "systems/kopparhavet/templates/parts/shared/modifications.html", + ]; + return loadTemplates(templatePaths); +} + +function normalize(data, defaultValue) { + if (data) { + return data.toLowerCase(); + } else { + return defaultValue; + } +} + +function registerHandlebarsHelpers() { + Handlebars.registerHelper("weaponCategory", function (category) { + category = normalize(category, "melee"); + switch (category) { + case "melee": + return game.i18n.localize("WEAPON.MELEE"); + case "ranged": + return game.i18n.localize("WEAPON.RANGED"); + } + }); + + Handlebars.registerHelper("rollDifficulty", function (difficulty) { + difficulty = normalize(difficulty, "average"); + switch (difficulty) { + case "simple": + return game.i18n.localize("DIFFICULTY.SIMPLE"); + case "easy": + return game.i18n.localize("DIFFICULTY.EASY"); + case "average": + return game.i18n.localize("DIFFICULTY.AVERAGE"); + case "hard": + return game.i18n.localize("DIFFICULTY.HARD"); + case "daunting": + return game.i18n.localize("DIFFICULTY.DAUNTING"); + } + }); + + Handlebars.registerHelper("skillType", function (skillType) { + skillType = normalize(skillType, "base"); + + switch (skillType) { + case "base": + return game.i18n.localize("SKILL.BASE"); + case "adventure": + return game.i18n.localize("SKILL.ADVENTURE"); + case "combat": + return game.i18n.localize("SKILL.COMBAT"); + } + }); + + Handlebars.registerHelper('plaintextToHTML', function(value) { + // strip tags, add
    tags + return new Handlebars.SafeString(value.replace(/(<([^>]+)>)/gi, "").replace(/(?:\r\n|\r|\n)/g, '
    ')); + }); +} \ No newline at end of file diff --git a/packs/skills.db b/packs/skills.db new file mode 100644 index 0000000..dcafdc1 --- /dev/null +++ b/packs/skills.db @@ -0,0 +1,35 @@ +{"name":"Smidighet","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"base","label":"SKILL.TYPE"},"value":"25","used":false},"flags":{"core":{"sourceId":"Item.tmNI5YIYYjGuZK5i"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"INlHwwv9wG4VA13D"} +{"name":"Trolldom","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.Ewo6mu7WdoOcF6DD"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"kIu2AL79urDjZV8z"} +{"name":"Fingerfärdighet","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.syxeJlio8vIrx0Lr"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"9gkb0ONyCuBcWwoB"} +{"name":"Finna dolda ting","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.Yvy3Ej3tDNhp0VVA"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"PaieUawcyfKhfSCv"} +{"name":"Förleda","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.orNrL49vWUePhLaU"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"JN1b5A5QHoinB5Gd"} +{"name":"Gömma sig","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.b9U8BXjNvI4LwlRX"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"A8DBqRLBJ0yU3Ebp"} +{"name":"Handel","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.DDZkgceLisZOYHEi"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"GS4NknpWIKYWceky"} +{"name":"Hantverk","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.0m5bA14oR16bB2Gt"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"imcYcYl21T4xewNa"} +{"name":"Insikt","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.bVEJBtXhXOkqIBek"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"NXJMfsrhYeCCEFP2"} +{"name":"Jakt","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.5wz37ypKMn6tgtTr"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"3EuThKpVWrtV1Myy"} +{"name":"Kastvapen","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"combat","label":"SKILL.TYPE"},"value":"30","used":false},"flags":{"core":{"sourceId":"Item.XF2eNRo6uMhDs5Uv"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"33aM81M73ZyO1NNT"} +{"name":"Knivar","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"combat","label":"SKILL.TYPE"},"value":"30","used":false},"flags":{"core":{"sourceId":"Item.boR4AGF8LD12ykoc"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"dGpYfI8CLwxKSzas"} +{"name":"Lagkunskap","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.8org0OaWuKO6rCqm"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"SYa97JM5u0aCNJMA"} +{"name":"Legender","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.dYcVgFgx6WmBWCeZ"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"JcmDWE2GxJ9HKIuT"} +{"name":"Läkekonst","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.DSWruhUT6DT9kY7Z"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"DqnFfN91ovo86QOn"} +{"name":"Lärdom","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.5oQCvQNiDSeIe8vI"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"Y6kDWokNA81POqvG"} +{"name":"Musik","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.bcRVOx8M3Ue6tgGz"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"5nMScpCnMbb77QfD"} +{"name":"Rida","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.eFeMJtv30eImpUMn"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"lqRRrd1Qdmbv5Nay"} +{"name":"Sjömanskap","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.BkCe8HPX1wVYlsmV"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"JIEl6a3sIkH2V5wX"} +{"name":"Skytte","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"combat","label":"SKILL.TYPE"},"value":"30","used":false},"flags":{"core":{"sourceId":"Item.5NCkdBZ4lWwLzGwk"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"jEoMTkqoxQ9iSaoB"} +{"name":"Sköldar","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"combat","label":"SKILL.TYPE"},"value":"30","used":false},"flags":{"core":{"sourceId":"Item.NBRV1tzrIV9AmGka"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"OkDh0xCD26hUrthN"} +{"name":"Slagsmål","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"combat","label":"SKILL.TYPE"},"value":"30","used":false},"flags":{"core":{"sourceId":"Item.IC2RGKp9cEYzyIln"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"afCvHSTrmDWNkj31"} +{"name":"Spana","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.EVCN4lxeferZYP0P"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"Z0pWniEH3MBU69g6"} +{"name":"Spel","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.Uc9XLdzkI7mGMTLL"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"HigGBE4oUC9yYMYw"} +{"name":"Spjut","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"combat","label":"SKILL.TYPE"},"value":"30","used":false},"flags":{"core":{"sourceId":"Item.qbi9SN1V4bPT3T3Y"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"Ab22ZgWUiHl83kLQ"} +{"name":"Språk","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.elGMSOQROaYXCEPA"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"VZx4zxd72g4tKvAd"} +{"name":"Status","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.F5lkuwhO3WGl5yWk"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"eaooVeqja7pzzC1X"} +{"name":"Stigvana","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.TjVsmOs0PqbAzNUf"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"dR4UbIXYyCovTGzA"} +{"name":"Styrka","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"base","label":"SKILL.TYPE"},"value":"25","used":false},"flags":{"core":{"sourceId":"Item.YpheXmaKsX9omtBA"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"anObCIyX3DdSjNPY"} +{"name":"Svärd","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"combat","label":"SKILL.TYPE"},"value":"30","used":false},"flags":{"core":{"sourceId":"Item.uctwxo07FCKCk2K4"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"IketG7YFZuGHnihF"} +{"name":"Taktik","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.KJLF5fDrZaA1DKg1"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"5xTZzjUd7x2EgmG2"} +{"name":"Uthållighet","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"base","label":"SKILL.TYPE"},"value":"25","used":false},"flags":{"core":{"sourceId":"Item.VWaRRqiUlMw1nPUn"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"4yFL38tdvBenZKBQ"} +{"name":"Viljestyrka","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"base","label":"SKILL.TYPE"},"value":"25","used":false},"flags":{"core":{"sourceId":"Item.UkwUABDT8NB8y1mQ"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"vyW9DUMDPYqBSCtz"} +{"name":"Väderkunskap","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"adventure","label":"SKILL.TYPE"},"value":"15","used":false},"flags":{"core":{"sourceId":"Item.GxkoQ2KdPh9ogGuJ"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"TEOcwEm0Z9GzJS6h"} +{"name":"Yxor","permission":{"default":0,"tSnD115pP1EPBAXK":3},"type":"skill","data":{"description":{"value":"","type":"String","label":"ITEM.DESCRIPTION"},"type":{"value":"combat","label":"SKILL.TYPE"},"value":"30","used":false},"flags":{"core":{"sourceId":"Item.b9no6ZeE5KyG9AfT"}},"img":"icons/svg/mystery-man.svg","effects":[],"_id":"BKu4BwEspAMQ5Yn3"} diff --git a/styles/actors.css b/styles/actors.css new file mode 100644 index 0000000..35e86f2 --- /dev/null +++ b/styles/actors.css @@ -0,0 +1,191 @@ +.kopparhavet .character { + display: flex; + flex-direction: column; + height: 100%; + width: 100%; +} + +.kopparhavet .adversary { + display: flex; + flex-direction: column; + height: 100%; + width: 100%; +} + +.actor-avatar-img { + max-width: 200px; + display: block; + margin-left: auto; + margin-right: auto; +} + +.charname { + border: none; +} + +.skill-value { + max-width: 50px; + text-align: center !important; +} + +/* +.kopparhavet .note .editor { + min-height: 302px; +} + */ + +.kopparhavet .scroll-y { + height: 100%; + overflow-y: auto; +} + +.item-list-header { +/* border: 0 none; */ + font-family: var(--font-special); + font-size: 14px; + font-weight: bold; + line-height: initial; + margin: 0; + padding: 4px 4px; + text-align: center; + text-transform: uppercase; +} + +.kopparhavet .item-list { + display: flex; + flex-direction: column; + font-family: var(--font-table); + font-size: 12px; + line-height: 16px; + min-height: 0; + height: calc(100% - 20px); +} + +.kopparhavet .item-list .header { + align-items: flex-end; + border-bottom: 2px solid var(--color-border); + font-size: 10px; + font-weight: bold; + margin: 0 8px; + padding: 4px 8px; + text-transform: uppercase; + width: calc(100% - 16px); +} + +.kopparhavet .item-list .items { + overflow-y: scroll; + padding: 0 2px 0 8px; + width: 100%; +} + +.kopparhavet .flex { + display: flex; +} + +.kopparhavet .skills-tab .skills h1 { + margin-bottom: 0; + grid-column: 1/4; + font-family: var(--font-special); + font-size: 14px; + font-weight: bold; + line-height: initial; + margin: 0; + padding: 4px 4px; + text-align: center; + text-transform: uppercase; +} + +.kopparhavet .talent-tab { + height: 100%; +} + +.kopparhavet .talent-tab .talents { + height: 100%; + overflow: hidden; +} + +.kopparhavet .talent-tab .spells { + height: 100%; + overflow: hidden; +} + +.kopparhavet .item-list .items { + overflow-y: scroll; + padding: 0 2px 0 8px; + width: 100%; + height: calc(100% - 20px); +} + +.kopparhavet .item-list .items .item:nth-child(odd) { + background: var(--color-secondary); +} + +.kopparhavet .item-list .items .item { + border-bottom: 1px solid var(--color-border); + padding: 1px 8px; +} + +.kopparhavet .item-list .items .item .image-container { + background: radial-gradient(closest-side, var(--color-highlight) 0%, transparent 100%); +} + +.kopparhavet .item-list .items .item .image { + background-size: 32px; + background-repeat: no-repeat; + height: 32px; + width: 32px; +} + +.kopparhavet .character .talents .talent .name { + align-items: center; + flex-basis: 100%; + display: flex; +} + +.kopparhavet .character .spells .spell { + align-items: center; +} + +.kopparhavet .character .spells .spell .name { + align-items: center; + flex-basis: 60%; + display: flex; +} + +.kopparhavet .character .spells .spell .cost { + flex-basis: 15%; + text-align: center; +} + +.kopparhavet .character .spells .spell .difficulty { + flex-basis: 25%; + text-align: center; +} + +.kopparhavet .character .bio-background h1 { + margin-bottom: 0; + grid-column: 1/5; + font-family: var(--font-special); + font-size: 14px; + font-weight: bold; + line-height: initial; + margin: 0; + padding: 4px 4px; + text-align: center; + text-transform: uppercase; +} + +.kopparhavet .character .bio-background .name { + align-items: center; + flex-basis: 100%; + display: flex; +} + +.kopparhavet .character .bio-background .name-top { + align-items: self-start; + flex-basis: 100%; + display: flex; +} + +.sheet-tabs { +} \ No newline at end of file diff --git a/styles/common.css b/styles/common.css new file mode 100644 index 0000000..def99c6 --- /dev/null +++ b/styles/common.css @@ -0,0 +1,140 @@ +:root { + --color-foreground: #221f20; + --color-background: #fff; + --color-highlight: #d1d2d4; + --color-secondary: #e1e2e4; + --color-border: #939598; + --font-editor: "IM Fell Great Primer", serif; + --font-input: "Poppins", sans-serif; + --font-special: "IM Fell DW Pica", serif; + --font-subheader: "IM Fell DW Pica SC", serif; + --font-table: "Poppins", sans-serif; +} + +.kopparhavet.sheet .window-content form { + min-height: 0; +} + +/********* Grid / Flex *********/ +.grid-container { + display: grid; + grid-gap: 5px; +} + +.flexrow { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: flex-start; +} + +/********* Editor *********/ +.editor, +editor-content { + font-family: var(--font-editor); + height: 100%; + padding: 5px; + width: 100%; +} + +.info-small, +.info-small .description, +.info-small .description .editor { + height: 166px; +} + +.info, +.info .description, +.info .description .editor { + height: 180px; +} + +.info-bio, +.info-bio .description, +.info-bio .description .editor { + height: 190px; +} + +.info-mid, +.info-mid .description, +.info-mid .description .editor { + height: 238px; +} + +.info-high, +.info-high .description, +.info-high .description .editor { + height: 380px; +} + +/********* Headers *********/ +h5 { + border-bottom: 1px solid #782e22; + font-size: 1.15em; + margin-bottom: 5px; + margin-top: 10px; + text-align: center; +} + +/********* Text *********/ +.center-text { + text-align: center !important; +} + +/********* Button *********/ +.kopparhavet button { + background: transparent; + border: 0 none; + border-radius: 3px; + box-shadow: none; + color: var(--color-foreground); + cursor: pointer; + font-family: inherit; + font-size: inherit; + font-weight: bold; + height: auto; + line-height: inherit; + padding: 2px 4px; + width: auto; +} +.kopparhavet button:focus, +.kopparhavet button:hover { + background: var(--color-highlight); +} + +/********* Border *********/ +.kopparhavet .border { + border: 1px solid #00000f; + padding: 5px; +} + +/********* Tabs *********/ +.kopparhavet .sheet-body { + overflow: hidden; + flex-grow: 1; +} + +.kopparhavet .tab { + height: 100%; + width: 100%; +} + +.tabs .item.active { + border-bottom: none; + text-shadow: 0 0 10px red; +} +.tabs .item { + border: solid #333333 1px; + border-top-right-radius: 5px; + border-top-left-radius: 5px; + text-align: center; + /* margin: auto; */ +} + +/********* Dice rolls *********/ +.rollable:hover, +.rollable:focus { + color: #000; + text-shadow: 0 0 10px red; + cursor: pointer; +} \ No newline at end of file diff --git a/styles/dice-roller.css b/styles/dice-roller.css new file mode 100644 index 0000000..6068feb --- /dev/null +++ b/styles/dice-roller.css @@ -0,0 +1,65 @@ +.roll-item h2, +.roll-item h3 { + text-align: center; +} + +.dice-formula-kh { + text-align: center; +} + + +.kh-dice-roller-popup { + /* margin: -46px 0 0 46px; */ + box-sizing: content-box; + font-size: 18px; + line-height: 36px; + background: rgba(0, 0, 0, 0.5); + box-shadow: 0 0 10px #000; + color: #BBB; + border: 1px solid #000; + border-radius: 5px; + z-index: 30; +} + +.kh-dice-roller-popup ul { + list-style-type: none; + margin: 0; + padding: 0; + border-bottom: 1px solid #333; + display: flex; +} + +.kh-dice-roller-popup .sdr-lastrow { + border-bottom: none; +} + +.kh-dice-roller-popup .sdr-lastcol { + border-right: none; +} + +.kh-dice-roller-popup .sdr-col1 { + width: 110px; + text-align: left; +} + +.kh-dice-roller-popup li { + display: inline-block; + width: 48px; + text-align: center; + padding: 5px; + border-right: 1px solid #333; + height: 48px; + cursor: pointer; +} + +.kh-dice-roller-popup li:hover { + color: #FFF; + border: 1px solid red; + border-bottom: 1px solid #ff6400; + box-shadow: 0 0 10px #ff6400; +} + +.kh-dice-roller-popup li i { + font-size: 24px; + vertical-align: middle; +} \ No newline at end of file diff --git a/styles/items.css b/styles/items.css new file mode 100644 index 0000000..e8ca394 --- /dev/null +++ b/styles/items.css @@ -0,0 +1,113 @@ +.item-avatar-img { + max-width: 200px; + display: block; + margin-left: auto; + margin-right: auto; +} + +.item-weapon-cat-select { + width: calc(100% - 2px); + min-width: 20px; +} + +.chat-item h2, +.chat-item h4 { + text-align: center; +} + +.modification-value { + text-align: center; +} + +.kopparhavet .gear-tab h1 { + margin-bottom: 0; + grid-column: 1/4; + font-family: var(--font-special); + font-size: 14px; + font-weight: bold; + line-height: initial; + margin: 0; + padding: 4px 4px; + text-align: center; + text-transform: uppercase; +} + +.items { + margin: 0 0 10px 0; + padding: 0; + border: 0; +} + +.items .items-header { + text-align: center; + padding: 3px; + /* border-bottom: 1px solid rgba(0, 0, 0, 0.25); */ + /* background-color: #490c0b; */ + /* color: white; */ + /* font-size: 12px; */ + border-bottom: 2px solid #782e22; + /* font-weight: normal; */ +} + +.items .items-header .header-name { + flex: 1 0 100px; + text-align: left; +} + +.items .items-list { + padding: 0; +} + +.items .items-list .item-name { + margin: 0; +/* cursor: pointer; */ + flex: 1 0 100px; + text-align: left; +} + +.item-quantity { + position: relative; +} + +.quantity.increase { + right: 25%; + cursor: pointer; +} + +.quantity.decrease { + left: 25%; + cursor: pointer; +} + +.quantity { + position: absolute; + top: 0; +} + +.item-weapon-skill-select { + width: calc(100% - 2px); + min-width: 20px; + text-align: center !important; +} + +.items .items-list .item-controls { + z-index: 500; + width: 20px; + text-align: end; + margin: auto; +} + +.items .items-list .item { + line-height: 24px; + padding: 3px 0; + border-bottom: 1px solid #bbb; + text-align: center; +} + +.items .items-list .item .toggle-equipped { + color: #888; +} + +.items .items-list .item .toggle-equipped.active { + color: #191813; +} \ No newline at end of file diff --git a/system.json b/system.json new file mode 100644 index 0000000..28d2438 --- /dev/null +++ b/system.json @@ -0,0 +1,51 @@ +{ + "name": "kopparhavet", + "title": "Kopparhavets Hjältar", + "description": "The Molten Sea is a dangerous but exciting place, where pirates, sorcerers and secretive orders of knighthood struggle for power, wealth and ancient lore.", + "version": "0.0.1", + "minimumCoreVersion": "0.7.5", + "compatibleCoreVersion": "0.7.7", + "templateVersion": 3, + "author": "Erebus", + "scripts": [], + "esmodules": [ + "module/kh-main.js", + "module/kh-dice.js" + ], + "styles": [ + "styles/common.css", + "styles/actors.css", + "styles/items.css", + "styles/dice-roller.css" + ], + "packs": [ + { + "name": "skills", + "label": "Skills", + "system": "kopparhavet", + "path": "./packs/skills.db", + "entity": "Item" + } + ], + "languages": [ + { + "lang": "en", + "name": "English", + "path": "lang/en.json" + }, + { + "lang": "sv", + "name": "Swedish", + "path": "lang/sv.json" + } + ], + "gridDistance": 2, + "gridUnits": "m", + "primaryTokenAttribute": "health", + "secondaryTokenAttribute": "stamina", + "url": "https://pi.rikspolisen.se/foundryvtt/kopparhavet", + "socket": true, + "manifest": "https://pi.rikspolisen.se/foundryvtt/kopparhavet/raw/branch/master/system.json", + "download": "https://pi.rikspolisen.se/foundryvtt/kopparhavet/archive/v0.0.1.zip", + "license": "" +} diff --git a/template.json b/template.json new file mode 100644 index 0000000..822b6a9 --- /dev/null +++ b/template.json @@ -0,0 +1,307 @@ +{ + "Actor": { + "types": [ + "character", + "adversary" + ], + "templates": { + "core": { + "description": { + "value": "", + "type": "String", + "label": "ITEM.DESCRIPTION" + } + }, + "stats": { + "health": { + "label": "STATS.HEALTH", + "value": 10, + "max": 10 + }, + "stamina": { + "label": "STATS.STAMINA", + "value": 10, + "max": 10 + }, + "mana": { + "label": "STATS.MANA", + "value": 0, + "max": 15 + } + }, + "combat": { + "combat": { + "init": 4 + } + } + }, + "character": { + "templates": [ + "core", + "stats", + "combat" + ], + "baseSkills": [ + "Smidighet", + "Trolldom", + "Fingerfärdighet", + "Finna dolda ting", + "Förleda", + "Gömma sig", + "Handel", + "Hantverk", + "Insikt", + "Jakt", + "Kastvapen", + "Knivar", + "Lagkunskap", + "Legender", + "Läkekonst", + "Lärdom", + "Musik", + "Rida", + "Sjömanskap", + "Skytte", + "Sköldar", + "Slagsmål", + "Spana", + "Spel", + "Spjut", + "Språk", + "Status", + "Stigvana", + "Styrka", + "Svärd", + "Taktik", + "Uthållighet", + "Viljestyrka", + "Väderkunskap", + "Yxor" + ], + "bio": { + "kin": { + "label": "BIO.KIN", + "value": "" + }, + "profession": { + "label": "BIO.PROFESSION", + "value": "" + }, + "age": { + "label": "BIO.AGE", + "value": "" + } + }, + "background": { + "birthplace": { + "label": "BACKGROUND.BIRTHPLACE", + "value": "" + }, + "social": { + "label": "BACKGROUND.SOCIALSTANDING", + "value": "" + }, + "events": { + "label": "BACKGROUND.EVENTS", + "value": "" + }, + "contacts": { + "label": "BACKGROUND.CONTACTS", + "value": "" + } + }, + "feature": { + "one": { + "label": "", + "value": false + }, + "two": { + "label": "", + "value": false + }, + "three": { + "label": "", + "value": false + }, + "four": { + "label": "", + "value": false + } + }, + "languages": { + "label": "SKILL.LANGUAGE", + "value": "" + }, + "currency": { + "shekel": { + "label": "CURRENCY.SHEKEL", + "value": 0 + }, + "quarter": { + "label": "CURRENCY.QUARTER", + "value": 0 + } + } + }, + "adversary": { + "templates": [ + "core", + "stats", + "combat" + ] + } + }, + "Item": { + "types": [ + "adversaryAttack", + "armor", + "gear", + "skill", + "spell", + "talent", + "weapon" + ], + "templates": { + "core": { + "description": { + "value": "", + "type": "String", + "label": "ITEM.DESCRIPTION" + } + }, + "basic": { + "quantity": { + "value": 1, + "type": "Number", + "label": "ITEM.QUANTITY" + }, + "price": { + "value": 0, + "type": "Number", + "label": "ITEM.PRICE" + } + }, + "equipable": { + "equipable": { + "value": true, + "type": "Boolean", + "equipped": false, + "label": "ITEM.EQUIPABLE" + } + }, + "modifications": { + "modifications": {} + } + }, + "adversaryAttack": { + "templates": [ + "core" + ], + "skill": { + "value": "", + "type": "String", + "label": "ADVERSARY.ATTACKSKILL" + }, + "damage": { + "value": "", + "type": "String", + "label": "ITEM.DAMAGE" + }, + "category": { + "value": "melee", + "type": "String", + "label": "WEAPON.CATEGORY" + } + }, + "armor": { + "templates": [ + "basic", + "core", + "equipable", + "modifications" + ], + "defence": { + "value": "", + "type": "String", + "label": "ITEM.DEFENCE" + }, + "helmet": { + "value": false, + "type": "Boolean", + "label": "ARMOR.HELMET" + }, + "type": { + "value": "light", + "label": "SKILL.TYPE" + } + }, + "gear": { + "templates": [ + "basic", + "core", + "modifications" + ] + }, + "skill": { + "templates": [ + "core" + ], + "type": { + "value": "adventure", + "label": "SKILL.TYPE" + }, + "value": "25", + "used": false + }, + "spell": { + "templates": [ + "core" + ], + "roll": { + "value": "", + "type": "String", + "label": "SPELL.ROLL" + }, + "cost": { + "value": "", + "type": "String", + "label": "SPELL.COST" + }, + "difficulty": { + "value": "average", + "type": "String", + "label": "SPELL.DIFFICULTY" + } + }, + "talent": { + "templates": [ + "core", + "modifications" + ] + }, + "weapon": { + "templates": [ + "basic", + "core", + "equipable", + "modifications" + ], + "skill": { + "value": "Svärd", + "type": "String", + "label": "ITEM.SKILL" + }, + "damage": { + "value": "", + "type": "String", + "label": "ITEM.DAMAGE" + }, + "category": { + "value": "melee", + "type": "String", + "label": "WEAPON.CATEGORY" + } + } + } +} + diff --git a/templates/actors/adversary-sheet.html b/templates/actors/adversary-sheet.html new file mode 100644 index 0000000..d7ddb34 --- /dev/null +++ b/templates/actors/adversary-sheet.html @@ -0,0 +1,51 @@ +
    +
    +
    +
    + +
    + + +

    + + +
    + + / + +
    + + +
    + + / + +
    + + +
    + + / + +
    +
    + +
    + {{localize "TAB.COMBAT"}} + {{localize "TAB.TALENTS"}} + {{localize "TAB.NOTE"}} +
    + +
    +
    + {{> systems/kopparhavet/templates/parts/actor/combat.html}} +
    +
    + {{> systems/kopparhavet/templates/parts/actor/talent.html}} +
    +
    + {{editor content=data.bio.note.value target="data.bio.note.value" button=true owner=owner editable=editable}} +
    +
    +
    +
    diff --git a/templates/actors/character-sheet.html b/templates/actors/character-sheet.html new file mode 100644 index 0000000..406e4bd --- /dev/null +++ b/templates/actors/character-sheet.html @@ -0,0 +1,95 @@ +
    +
    +
    +
    + +
    + + +

    + + + + + + + + +
    + + / + +
    + + +
    + + / + +
    + + +
    + + / + +
    + +
    +

    {{localize "FEATURES.TITLE"}}

    + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    + {{localize "TAB.SKILLS"}} + + {{localize "TAB.TALENTS"}} + {{localize "TAB.GEAR"}} + {{localize "TAB.BIO"}} + {{localize "TAB.NOTE"}} +
    + +
    +
    + {{> systems/kopparhavet/templates/parts/actor/skills.html}} +
    + +
    + {{> systems/kopparhavet/templates/parts/actor/talent.html}} +
    +
    + {{> systems/kopparhavet/templates/parts/actor/gear.html}} +
    +
    + {{> systems/kopparhavet/templates/parts/actor/bio.html}} +
    +
    + {{editor content=data.bio.note.value target="data.bio.note.value" button=true owner=owner editable=editable}} +
    +
    +
    +
    diff --git a/templates/chat/item-card.html b/templates/chat/item-card.html new file mode 100644 index 0000000..553e89b --- /dev/null +++ b/templates/chat/item-card.html @@ -0,0 +1,105 @@ +
    +
    + {{#if img}} + + {{/if}} + +

    {{name}}

    + {{#if isGear}} +

    {{localize "ITEM.GEAR"}}

    + {{/if}} + {{#if isAdversaryAttack}} +

    {{localize "ITEM.ATTACK"}}

    + {{/if}} + {{#if isSkill}} +

    {{skillType data.type.value}}

    + {{/if}} + {{#if isSpell}} +

    {{localize "ITEM.SPELL"}}

    + {{/if}} + {{#if isTalent}} +

    {{localize "ITEM.TALENT"}}

    + {{/if}} + {{#if isWeapon}} +

    {{weaponCategory data.category.value}}

    + {{/if}} + + {{#if isArmor}} +
    + {{localize data.price.label}}: {{data.price.value}} + {{#if data.helmet.value}} + {{localize data.helmet.label}} + {{else}} + {{localize data.defence.label}}: {{data.defence.value}} + {{/if}} +
    + {{#if data.description}} +
    {{localize data.description.label}}
    + {{{data.description.value}}} + {{/if}} + {{/if}} + + {{#if isGear}} +
    + {{localize data.price.label}}: {{data.price.value}} + {{localize data.quantity.label}}: {{data.quantity.value}} +
    + + {{#if data.description}} +
    {{localize data.description.label}}
    + {{{data.description.value}}} + {{/if}} + {{/if}} + + {{#if isSkill}} + {{#if data.description}} +
    {{localize data.description.label}}
    + {{{data.description.value}}} + {{/if}} + {{/if}} + + {{#if isAdversaryAttack}} +
    + {{localize data.skill.label}}: {{data.skill.value}} + {{localize data.damage.label}}: {{data.damage.value}} +
    + {{#if data.description}} +
    {{localize data.description.label}}
    + {{{data.description.value}}} + {{/if}} + {{/if}} + + {{#if isSpell}} +
    + {{localize data.cost.label}}: {{data.cost.value}} + {{localize data.difficulty.label}}: {{rollDifficulty data.difficulty.value}} +
    + {{localize data.roll.label}} + {{data.roll.value}} +
    +
    + {{#if data.description}} +
    {{localize data.description.label}}
    + {{{data.description.value}}} + {{/if}} + {{/if}} + + {{#if isTalent}} + {{#if data.description}} +
    {{localize data.description.label}}
    + {{{data.description.value}}} + {{/if}} + {{/if}} + + {{#if isWeapon}} +
    + {{localize data.price.label}}: {{data.price.value}} + {{localize data.damage.label}}: {{data.damage.value}} +
    + {{#if data.description}} +
    {{localize data.description.label}}
    + {{{data.description.value}}} + {{/if}} + {{/if}} +
    +
    \ No newline at end of file diff --git a/templates/dice/roll.html b/templates/dice/roll.html new file mode 100644 index 0000000..1b79561 --- /dev/null +++ b/templates/dice/roll.html @@ -0,0 +1,26 @@ +
    +
    +

    {{localize name}}

    + +
    +
    +
    + {{#if showFormula}} +
    {{res.formula}}
    + {{/if}} +

    {{res.total}}

    +
    +
    +
    + {{#if success}} +
    + {{localize "ROLL.SUCCESS"}} +
    + {{/if}} + {{#if failure}} +
    + {{localize "ROLL.FAILURE"}} +
    + {{/if}} +
    +
    diff --git a/templates/items/adversaryAttack-sheet.html b/templates/items/adversaryAttack-sheet.html new file mode 100644 index 0000000..0945387 --- /dev/null +++ b/templates/items/adversaryAttack-sheet.html @@ -0,0 +1,39 @@ +
    +
    +
    + +
    +
    +

    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    +

    {{localize data.description.label}}

    +
    +
    + {{editor content=data.description.value target="data.description.value" button=true owner=owner editable=editable}} +
    +
    +
    +
    +
    diff --git a/templates/items/armor-sheet.html b/templates/items/armor-sheet.html new file mode 100644 index 0000000..c8eff11 --- /dev/null +++ b/templates/items/armor-sheet.html @@ -0,0 +1,72 @@ +
    +
    +
    + +
    +
    +

    +
    +
    +
    + +
    + + +
    +
    + + +
    +
    + + +
    +
    + + + + + +
    +
    + +
    + {{localize "TAB.DESCRIPTION"}} + {{localize "TAB.MODIFICATION"}} +
    + +
    +
    +
    +
    +

    {{localize data.description.label}}

    +
    +
    + {{editor content=data.description.value target="data.description.value" button=true owner=owner editable=editable}} +
    +
    +
    +
    +
    + +
    + {{> systems/kopparhavet/templates/parts/shared/modifications.html}} +
    +
    +
    \ No newline at end of file diff --git a/templates/items/gear-sheet.html b/templates/items/gear-sheet.html new file mode 100644 index 0000000..e4d98f1 --- /dev/null +++ b/templates/items/gear-sheet.html @@ -0,0 +1,30 @@ +
    +
    +
    + +
    +
    +

    +
    +
    +
    + + +
    +
    + + +
    +
    +
    +
    +
    +

    {{localize data.description.label}}

    +
    +
    + {{editor content=data.description.value target="data.description.value" button=true owner=owner editable=editable}} +
    +
    +
    +
    +
    diff --git a/templates/items/skill-sheet.html b/templates/items/skill-sheet.html new file mode 100644 index 0000000..0086f24 --- /dev/null +++ b/templates/items/skill-sheet.html @@ -0,0 +1,36 @@ +
    +
    +
    + +
    +
    +

    +
    +
    +
    +
    + + +
    +
    + + +
    +
    +
    +
    +

    {{localize data.description.label}}

    +
    +
    + {{editor content=data.description.value target="data.description.value" button=true owner=owner editable=editable}} +
    +
    +
    +
    +
    diff --git a/templates/items/spell-sheet.html b/templates/items/spell-sheet.html new file mode 100644 index 0000000..1dc9ee6 --- /dev/null +++ b/templates/items/spell-sheet.html @@ -0,0 +1,42 @@ +
    +
    +
    + +
    +
    +

    +
    +
    +
    +
    + + +
    +
    + + +
    +
    + + +
    +
    +
    +
    +

    {{localize data.description.label}}

    +
    +
    + {{editor content=data.description.value target="data.description.value" button=true owner=owner editable=editable}} +
    +
    +
    +
    +
    diff --git a/templates/items/talent-sheet.html b/templates/items/talent-sheet.html new file mode 100644 index 0000000..1fb1ca8 --- /dev/null +++ b/templates/items/talent-sheet.html @@ -0,0 +1,20 @@ +
    +
    +
    + +
    +
    +

    +
    +
    +
    +
    +

    {{localize data.description.label}}

    +
    +
    + {{editor content=data.description.value target="data.description.value" button=true owner=owner editable=editable}} +
    +
    +
    +
    +
    diff --git a/templates/items/weapon-sheet.html b/templates/items/weapon-sheet.html new file mode 100644 index 0000000..d94a34b --- /dev/null +++ b/templates/items/weapon-sheet.html @@ -0,0 +1,63 @@ +
    +
    +
    + +
    +
    +

    +
    +
    +
    + +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    + +
    +
    +
    +

    {{localize data.description.label}}

    +
    +
    + {{editor content=data.description.value target="data.description.value" button=true owner=owner editable=editable}} +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/templates/parts/actor/bio.html b/templates/parts/actor/bio.html new file mode 100644 index 0000000..e80d76e --- /dev/null +++ b/templates/parts/actor/bio.html @@ -0,0 +1,32 @@ +
    +
    +

    {{localize "BACKGROUND.TITLE"}}

    + +
    {{localize data.bio.age.label}}:
    + + +
    {{localize data.background.birthplace.label}}
    + + +
    {{localize data.background.social.label}}
    + + +
    +
    + +
    {{localize data.background.events.label}}
    +
    +
    + {{editor content=data.background.events.value target="data.background.events.value" button=true owner=owner editable=editable}} +
    +
    + + +
    {{localize data.background.contacts.label}}
    +
    +
    + {{editor content=data.background.contacts.value target="data.background.contacts.value" button=true owner=owner editable=editable}} +
    +
    +
    +
    \ No newline at end of file diff --git a/templates/parts/actor/combat.html b/templates/parts/actor/combat.html new file mode 100644 index 0000000..64e682d --- /dev/null +++ b/templates/parts/actor/combat.html @@ -0,0 +1,76 @@ +
    +
    +
      +
        +
      • +
        {{localize "MOD.INIT"}}
        +
        +
      • + +
      • +
        {{localize "ADVERSARY.DEFENCE"}}
        +
        +
      • +
      • +
        {{localize "ADVERSARY.HASHELMET"}}
        +
        + + + +
        +
      • + +
      • +
        {{localize "ITEM.DEFENCE"}}
        +
        +
      • +
      +
    +
    + +
    +

    {{localize "TAB.SKILLS"}}

    +
      +
        + {{#each actor.skills as |skill key|}} +
      • +
        {{skill.name}}
        +
        +
      • + {{/each}} +
      +
    +
    +
    +
      +
    • +
      {{localize "ITEM.ATTACK"}}
      +
      {{localize "ITEM.SKILL"}}
      +
      {{localize "ITEM.DAMAGE"}}
      +
      +
    • + +
        + {{#each actor.attacks as |item key|}} +
      • +
        {{item.name}}
        +
        +
        +
        {{item.data.skill.value}}
        +
        +
        +
        +
        +
        {{item.data.damage.value}}
        +
        +
        +
        + + +
        +
      • + {{/each}} +
      +
    +
    +
    diff --git a/templates/parts/actor/gear.html b/templates/parts/actor/gear.html new file mode 100644 index 0000000..f297b34 --- /dev/null +++ b/templates/parts/actor/gear.html @@ -0,0 +1,117 @@ +
    +
    +
    +

    {{localize "CURRENCY.TITLE"}}

    + + + + +
    +
    +
    + {{!-- Weapons List --}} +
      +
    • +
      {{localize "ITEM.WEAPON"}}
      +
      {{localize "ITEM.EQUIPPED"}}
      +
      {{localize "ITEM.SKILL"}}
      +
      {{localize "ITEM.DAMAGE"}}
      +
      +
    • +
        + {{#each actor.weapons as |item id|}} +
      • +
        {{item.name}}
        +
        + {{#if item.data.equipable.equipped}} + + {{else}} + + {{/if}} +
        +
        +
        +
        {{item.data.skill.value}}
        +
        +
        +
        +
        +
        {{item.data.damage.value}}
        +
        +
        +
        + + +
        +
      • + {{/each}} +
      +
    + + {{!-- Armour List --}} +
      +
    • +
      {{localize "ITEM.ARMOR"}}
      +
      {{localize "ITEM.EQUIPPED"}}
      +
      {{localize "ITEM.DEFENCE"}}
      +
      +
    • +
        + {{#each actor.armor as |item id|}} +
      • +
        {{item.name}}
        +
        + {{#if item.data.equipable.equipped}} + + {{else}} + + {{/if}} +
        + {{#if item.data.helmet.value}} +
        + {{localize item.data.helmet.label}} +
        + {{else}} +
        +
        +
        {{item.data.defence.value}}
        +
        +
        + {{/if}} +
        + + +
        +
      • + {{/each}} +
      +
    + + {{!-- Gear List --}} +
      +
    • +
      {{localize "ITEM.GEAR"}}
      +
      {{localize "ITEM.QUANTITY"}}
      +
      +
    • + +
        + {{#each actor.gear as |item id|}} +
      • +
        {{item.name}}
        +
        +
        + {{item.data.quantity.value}} +
        +
        +
        + + +
        +
      • + {{/each}} +
      +
    +
    +
    + diff --git a/templates/parts/actor/skills.html b/templates/parts/actor/skills.html new file mode 100644 index 0000000..cb08d38 --- /dev/null +++ b/templates/parts/actor/skills.html @@ -0,0 +1,66 @@ +
    +
    +

    {{localize "SKILL.BASE"}}

    +
      +
        + {{#each actor.skills as |skill key|}} + {{#if skill.hasBase}} +
      • +
        {{skill.name}}
        +
        +
        + + + +
        +
      • + {{/if}} + {{/each}} +
      +
    +
    +
    +

    {{localize "SKILL.ADVENTURE"}}

    +
      +
        + {{#each actor.skills as |skill key|}} + {{#if skill.hasAdventure}} +
      • +
        {{skill.name}}
        +
        +
        + + + +
        +
      • + {{/if}} + {{/each}} +
      +
    +
    +
    +

    {{localize "SKILL.COMBAT"}}

    +
      +
        + {{#each actor.skills as |skill key|}} + {{#if skill.hasCombat}} +
      • +
        {{skill.name}}
        +
        +
        + + + +
        +
      • + {{/if}} + {{/each}} +
      +
    +
    +
    +

    {{localize data.languages.label}}

    + +
    +
    \ No newline at end of file diff --git a/templates/parts/actor/talent.html b/templates/parts/actor/talent.html new file mode 100644 index 0000000..779c9b5 --- /dev/null +++ b/templates/parts/actor/talent.html @@ -0,0 +1,42 @@ +
    +
    +

    {{localize "ITEM.TALENT"}}

    + +
    +
      +
        + {{#each actor.talents as |talent key|}} +
      • +
        {{talent.name}}
        +
        + + +
        +
      • + {{/each}} +
      +
    +
    +
    +
    +

    {{localize "ITEM.SPELL"}}

    + +
    +
      +
        + {{#each actor.spells as |spell key|}} +
      • +
        {{spell.name}}
        +
        {{spell.data.cost.value}}
        +
        {{rollDifficulty spell.data.difficulty.value}}
        +
        + + +
        +
      • + {{/each}} +
      +
    +
    +
    +
    \ No newline at end of file diff --git a/templates/parts/shared/modifications.html b/templates/parts/shared/modifications.html new file mode 100644 index 0000000..d4cd290 --- /dev/null +++ b/templates/parts/shared/modifications.html @@ -0,0 +1,22 @@ +
    + {{localize "MOD.TYPE"}} + {{localize "MOD.VALUE"}} + +
    +
      + {{#each data.modifications as |attr key|}} +
    1. + + + + +
    2. + {{/each}} +