From 2e389704eaeb1471d88cc8e17076a190831a8e30 Mon Sep 17 00:00:00 2001 From: erebus Date: Tue, 1 Dec 2020 16:30:39 +0000 Subject: [PATCH] v0.0.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Finishing v0.0.3 Layout fixes Added support for rolling skill on spells Fixed migration Cleaned up the code Fixed Hjältarnas Tid skill error, updated layout, fixed buttons not working Fixed right click menu for weapons Added roll dialog and exceptional rolls Updated skills styling Co-Authored-By: erebus Co-Committed-By: erebus --- lang/en.json | 12 + lang/sv.json | 16 +- module/actors/actor-sheet.js | 315 ++++++++++++++++++++++++++- module/actors/actor.js | 2 - module/helpers/dice-helper.js | 130 ++++++++++- module/helpers/migration-helper.js | 21 ++ module/items/item-sheet.js | 30 ++- module/kh-hooks.js | 3 + module/kh-main.js | 42 +++- styles/actors.css | 1 + styles/items.css | 15 +- system.json | 6 +- template.json | 7 +- templates/chat/item-card.html | 3 +- templates/dice/roll.html | 22 +- templates/items/spell-sheet.html | 21 +- templates/parts/actor/combat.html | 18 +- templates/parts/actor/gear.html | 14 +- templates/parts/actor/ht-combat.html | 16 +- templates/parts/actor/ht-skills.html | 12 +- templates/parts/actor/skills.html | 18 +- templates/parts/actor/talent.html | 12 +- templates/roll-dialog.html | 55 +++++ 23 files changed, 702 insertions(+), 89 deletions(-) create mode 100644 module/helpers/migration-helper.js create mode 100644 templates/roll-dialog.html diff --git a/lang/en.json b/lang/en.json index 443a221..70d1616 100644 --- a/lang/en.json +++ b/lang/en.json @@ -23,6 +23,7 @@ "CURRENCY.QUARTER": "Quarter", "CURRENCY.SHEKEL": "Shekel", "CURRENCY.TITLE": "Currency", + "CURRENCY.SILVER": "Silver", "DICE.ROLL": "Dice roll", @@ -62,6 +63,14 @@ "ROLL.SUCCESS": "Success", "ROLL.FAILURE": "Failure", + "ROLL.TITLE": "Roll dice", + "BUTTON.ROLL": "Roll", + "BUTTON.CANCEL": "Cancel", + "ROLL.CLOSED": "Closed", + "ROLL.OPENED": "Opened", + "MENU.SHOWROLLDIALOG": "Show diceroller dialog", + "ROLL.OPENCLOSE": "Open / Close", + "ROLL.EXCEPTIONAL": "Exceptional", "SKILL.TYPE": "Type", "SKILL.BASE": "Basic", @@ -73,6 +82,9 @@ "SPELL.DIFFICULTY": "Difficulty", "SPELL.ROLL": "Roll", "SPELL.COST": "Cost", + "SPELL.ATTACKROLL": "Attack roll", + "SPELL.OPPOSITE": "Opposite", + "SPELL.RITUAL": "Ritual", "STATS.HEALTH": "Health", "STATS.MANA": "Mana", diff --git a/lang/sv.json b/lang/sv.json index fd20903..04ec44e 100644 --- a/lang/sv.json +++ b/lang/sv.json @@ -24,6 +24,7 @@ "CURRENCY.QUARTER": "Kvarting", "CURRENCY.SHEKEL": "Shekel", "CURRENCY.TITLE": "Mynt", + "CURRENCY.SILVER": "Silver", "DICE.ROLL": "Tärningsslag", @@ -64,6 +65,15 @@ "ROLL.SUCCESS": "Lyckat", "ROLL.FAILURE": "Misslyckat", + "ROLL.TITLE": "Slå tärningar", + "BUTTON.ROLL": "Slå", + "BUTTON.CANCEL": "Avbryt", + "ROLL.CLOSED": "Stängd", + "ROLL.OPENED": "Öppnad", + "MENU.SHOWROLLDIALOG": "Visa tärningsdialog", + "ROLL.OPENCLOSE": "Öppna / Stäng", + "ROLL.EXCEPTIONAL": "Exceptionellt", + "SKILL.TYPE": "Typ", "SKILL.BASE": "Grundfärdigheter", "SKILL.ADVENTURE": "Äventyrsfärdigheter", @@ -72,8 +82,12 @@ "SKILL.LANGUAGE": "Språk", "SPELL.DIFFICULTY": "Svårighet", - "SPELL.ROLL": "Slag", + "SPELL.ROLLTITLE": "Slag", "SPELL.COST": "Kostnad", + "SPELL.ROLL": "Färdighetsslag", + "SPELL.ATTACKROLL": "Anfallsslag", + "SPELL.OPPOSITE": "Motsatt", + "SPELL.RITUAL": "Ritual", "STATS.HEALTH": "Hälsa", "STATS.MANA": "Skuld", diff --git a/module/actors/actor-sheet.js b/module/actors/actor-sheet.js index 2d94178..74b2937 100644 --- a/module/actors/actor-sheet.js +++ b/module/actors/actor-sheet.js @@ -139,6 +139,186 @@ export class ActorSheetKH extends ActorSheet { }, ]); + new ContextMenu(html, "li.item-weapon", [ + { + name: game.i18n.localize("MENU.SHOWROLLDIALOG"), + icon: '', + callback: (li) => { + let itemId = li.data("itemId"); + let _item = this.actor.items.find((element) => element._id == itemId); + + // Retrieve skill based on name + let skill = this.actor.items.find((element) => element.name === _item.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.rollSkillDialogInChat(skillName, skillValue, showValue, this.actor) + }, + }, + { + name: game.i18n.localize("MENU.SENTTOCHAT"), + icon: '', + callback: (li) => { + let itemId = li.data("itemId"); + this._itemDetailsToChat(itemId); + }, + }, + ]); + + new ContextMenu(html, "li.item-skill", [ + { + name: game.i18n.localize("MENU.SHOWROLLDIALOG"), + icon: '', + callback: (li) => { + 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.rollSkillDialogInChat(skillName, skillValue, showValue, this.actor) + }, + }, + { + name: game.i18n.localize("MENU.SENTTOCHAT"), + icon: '', + callback: (li) => { + let itemId = li.data("itemId"); + this._itemDetailsToChat(itemId); + }, + }, + ]); + + new ContextMenu(html, "li.item-spell", [ + { + name: game.i18n.localize("MENU.SHOWROLLDIALOG"), + icon: '', + callback: (li) => { + let itemId = li.data("itemId"); + let _item = this.actor.items.find((element) => element._id == itemId); + + if (!_item) { + _item = game.items.get(itemId); + + if (!_item) { + console.log("IMPORT ERROR") + return + } + } + + let showValue = false + let difficulty = 0 + + if(this.actor.data.type === "character") { + showValue = true + } + + switch (_item.data.data.difficulty.value) { + case "simple": + difficulty = 5 + break; + case "easy": + difficulty = 2 + break; + case "hard": + difficulty = -2 + break; + case "daunting": + difficulty = -5 + break; + } + + if(_item.data.data.roll.value === "roll" || _item.data.data.roll.value === "attackroll") { + // Retrieve skill based on name + let skill = this.actor.items.find((element) => element.name === _item.data.data.roll.skill); + + let skillName = _item.name + let skillValue = skill.data.data.value + + if(this.actor.data.type === "character") { + skillName = _item.name + " (" + skill.name + ")" + } + + this.khRoller.rollSkillDialogInChat(skillName, skillValue, showValue, this.actor, difficulty) + } else if(_item.data.data.roll.value === "opposite") { + // Retrieve skill based on name + let skill = this.actor.items.find((element) => element.name === _item.data.data.roll.skill); + + let skillName = _item.name + let skillValue = skill.data.data.value + + if(this.actor.data.type === "character") { + skillName = _item.name + " (" + skill.name + ")" + } + + this.khRoller.rollSkillDialogInChat(skillName, skillValue, showValue, this.actor, difficulty) + } else if(_item.data.data.roll.value === "ritual") { + console.log("Not supported yet") + this.khRoller.rollSkillDialogInChat("Ritual", -1, showValue, this.actor, difficulty) + } + }, + }, + { + name: game.i18n.localize("MENU.SENTTOCHAT"), + icon: '', + callback: (li) => { + let itemId = li.data("itemId"); + this._itemDetailsToChat(itemId); + }, + }, + ]); + + new ContextMenu(html, "li.item-attack", [ + { + name: game.i18n.localize("MENU.SHOWROLLDIALOG"), + icon: '', + callback: (li) => { + let skillValue = li.data("ability"); + let skillName = "ITEM.ATTACK"; + let showValue = false + + if(this.actor.data.type === "character") { + showValue = true + } + + this.khRoller.rollSkillDialogInChat(skillName, skillValue, showValue, this.actor) + }, + }, + { + name: game.i18n.localize("MENU.SENTTOCHAT"), + icon: '', + callback: (li) => { + let itemId = li.data("itemId"); + this._itemDetailsToChat(itemId); + }, + }, + ]); + + new ContextMenu(html, "li.item-defence", [ + { + name: game.i18n.localize("MENU.SHOWROLLDIALOG"), + icon: '', + callback: (li) => { + const skillValue = li.data("defence"); + + let skillName = "ADVERSARY.DEFENCE"; + + this.khRoller.rollSkillDialogInChat(skillName, skillValue, false, this.actor) + }, + } + ]); + html.find(".feature").click(async (ev) => { const featureName = $(ev.currentTarget).data("feature"); const featureValue = this.actor.data.data.feature[featureName].value; @@ -156,7 +336,35 @@ export class ActorSheetKH extends ActorSheet { // Delete Inventory Item html.find(".item-delete").click((ev) => { - const li = $(ev.currentTarget).parents(".item"); + let parent = $(ev.currentTarget).data("parent") + const li = $(ev.currentTarget).parents(parent); + const item = this.actor.getOwnedItem(li.data("itemId")); + + if(item) { + if (item.type === "armor") { + let initValue = -1 + + if (item.data.data.equipable.equipped) { + initValue = 4 + + this.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; + } + } + } + } + }); + } + + if (initValue > 0) { + this.actor.update({["data.combat.init"]: initValue}); + } + } + } this.actor.deleteOwnedItem(li.data("itemId")); @@ -165,7 +373,8 @@ export class ActorSheetKH extends ActorSheet { // Edit Inventory Item html.find(".item-edit").click(async (ev) => { - const li = $(ev.currentTarget).parents(".item"); + let parent = $(ev.currentTarget).data("parent") + let li = $(ev.currentTarget).parents(parent); let itemId = li.data("itemId"); let item = this.actor.getOwnedItem(itemId); @@ -184,7 +393,7 @@ export class ActorSheetKH extends ActorSheet { /* Roll spell cost */ html.find(".roll-spell-cost").click((ev) => { - const li = $(ev.currentTarget).parents(".item"); + const li = $(ev.currentTarget).parents(".item-spell"); let itemId = li.data("itemId"); let spell = this.actor.getOwnedItem(itemId); @@ -207,9 +416,74 @@ export class ActorSheetKH extends ActorSheet { } }); + html.find(".roll-spell-skill").click((ev) => { + const li = $(ev.currentTarget).parents(".item-spell"); + 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 showValue = false + let difficulty = 0 + + if(this.actor.data.type === "character") { + showValue = true + } + + switch (spell.data.data.difficulty.value) { + case "simple": + difficulty = 5 + break; + case "easy": + difficulty = 2 + break; + case "hard": + difficulty = -2 + break; + case "daunting": + difficulty = -5 + break; + } + + if(spell.data.data.roll.value === "roll" || spell.data.data.roll.value === "attackroll") { + // Retrieve skill based on name + let skill = this.actor.items.find((element) => element.name === spell.data.data.roll.skill); + + let skillName = spell.name + let skillValue = skill.data.data.value + + if(this.actor.data.type === "character") { + skillName = spell.name + " (" + skill.name + ")" + } + + this.khRoller.rollSkillInChat(skillName, skillValue, showValue, this.actor, difficulty) + } else if(spell.data.data.roll.value === "opposite") { + // Retrieve skill based on name + let skill = this.actor.items.find((element) => element.name === spell.data.data.roll.skill); + + let skillName = spell.name + let skillValue = skill.data.data.value + + if(this.actor.data.type === "character") { + skillName = spell.name + " (" + skill.name + ")" + } + + this.khRoller.rollSkillInChat(skillName, skillValue, showValue, this.actor, difficulty) + } else if(spell.data.data.roll.value === "ritual") { + console.log("Not supported yet") + } + }); + /* Roll skill */ html.find(".roll-skill").click((ev) => { - const li = $(ev.currentTarget).parents(".item"); + const li = $(ev.currentTarget).parents(".item-skill"); let itemId = li.data("itemId"); let _item = this.actor.items.find((element) => element._id == itemId); @@ -226,7 +500,7 @@ export class ActorSheetKH extends ActorSheet { /* Roll weapon skill */ html.find(".roll-weapon-skill").click((ev) => { - const li = $(ev.currentTarget).parents(".item"); + const li = $(ev.currentTarget).parents(".item-weapon"); let itemId = li.data("itemId"); let weapon = this.actor.getOwnedItem(itemId); @@ -255,7 +529,7 @@ export class ActorSheetKH extends ActorSheet { /* Roll weapon damage */ html.find(".roll-damage").click((ev) => { - const li = $(ev.currentTarget).parents(".item"); + const li = $(ev.currentTarget).parents(".item-weapon"); let itemId = li.data("itemId"); let weapon = this.actor.getOwnedItem(itemId); @@ -318,6 +592,7 @@ export class ActorSheetKH extends ActorSheet { /* Toggle item equipped */ html.find(".items .item a.toggle-equipped").click(this._toggleEquippedItem.bind(this)); + html.find(".items .item-weapon 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))); @@ -331,7 +606,7 @@ export class ActorSheetKH extends ActorSheet { /* Adversary specific */ html.find(".roll-adversary-attack").click((ev) => { - const li = $(ev.currentTarget).parents(".item"); + const li = $(ev.currentTarget).parents(".item-attack"); let skillValue = li.data("ability"); let skillName = "ITEM.ATTACK"; @@ -339,6 +614,30 @@ export class ActorSheetKH extends ActorSheet { this.khRoller.rollSkillInChat(skillName, skillValue, false, this.actor) }); + html.find(".roll-attack-damage").click((ev) => { + const li = $(ev.currentTarget).parents(".item-attack"); + 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) + } + }); + html.find(".roll-defence").click((ev) => { const skillValue = $(ev.currentTarget).data("defence"); @@ -480,7 +779,7 @@ export class ActorSheetKH extends ActorSheet { const item = this.actor.getOwnedItem(li.data("itemId")); const actor = this.actor; - if (item) { + if(item) { if(item.type === "armor") { let initValue = -1 diff --git a/module/actors/actor.js b/module/actors/actor.js index e7433b2..d082d58 100644 --- a/module/actors/actor.js +++ b/module/actors/actor.js @@ -12,7 +12,5 @@ export class ActorKH extends Actor { 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 index 228791a..3784511 100644 --- a/module/helpers/dice-helper.js +++ b/module/helpers/dice-helper.js @@ -84,7 +84,7 @@ export default class KHDiceRoller { }); } - async rollSkillInChat(skillName, skillValue, showValue, speaker) { + async rollSkillInChat(skillName, skillValue, showValue, speaker, openclosed) { const roll = new Roll(`1d100`); let res = roll.roll(); @@ -95,16 +95,43 @@ export default class KHDiceRoller { computedName += " (" + skillValue + ")" } + if(openclosed === undefined) { + openclosed = 0 + } + let rollData = { name: computedName, res: res }; if(skillValue > 0) { - if(res.total <= skillValue) { + let oneRes = Math.floor((res.total / 1) % 10); + let tenRes = Math.floor((res.total / 10) % 10); + + if(openclosed < 0) { + rollData.closed = Math.abs(openclosed) + } + + if(openclosed > 0) { + rollData.opened = Math.abs(openclosed) + } + + if(openclosed < 0 && oneRes !== 0 && Math.abs(openclosed) >= oneRes) { + // roll is closed + rollData.failure = true + } else if(openclosed > 0 && oneRes !== 0 && Math.abs(openclosed) >= oneRes){ + // roll is opened rollData.success = true } else { - rollData.failure = true + if (res.total <= skillValue) { + rollData.success = true + } else { + rollData.failure = true + } + } + + if(oneRes === tenRes) { + rollData.excetional = true } } @@ -121,4 +148,101 @@ export default class KHDiceRoller { }, }); } + + async rollSkillDialogInChat(skillName, skillValue, showValue, speaker, startopen) { + const id = randomID(); + + if(startopen === undefined) { + startopen = 0 + } + + const content = await renderTemplate("systems/kopparhavet/templates/roll-dialog.html", { + id, + startopen, + skillName, + skillValue, + }); + + await new Dialog({ + title: game.i18n.localize("ROLL.TITLE"), + content, + buttons: { + one: { + icon: '', + label: game.i18n.localize("BUTTON.ROLL"), + callback: async () => { + const container = document.getElementById(id); + let openclosed = container.querySelector('[name="openclosed"]').value + + const roll = new Roll(`1d100`); + + let res = roll.roll(); + + let computedName = skillName + + if(showValue) { + computedName += " (" + skillValue + ")" + } + + let rollData = { + name: computedName, + res: res + }; + + // Evaluate result only if we have a positive skillvalue + if(skillValue > 0) { + let oneRes = Math.floor((res.total / 1) % 10); + let tenRes = Math.floor((res.total / 10) % 10); + + if(openclosed < 0) { + rollData.closed = Math.abs(openclosed) + } + + if(openclosed > 0) { + rollData.opened = Math.abs(openclosed) + } + + if(openclosed < 0 && oneRes !== 0 && Math.abs(openclosed) >= oneRes) { + // roll is closed + rollData.failure = true + } else if(openclosed > 0 && oneRes !== 0 && Math.abs(openclosed) >= oneRes){ + // roll is opened + rollData.success = true + } else { + if (res.total <= skillValue) { + rollData.success = true + } else { + rollData.failure = true + } + } + + if(oneRes === tenRes) { + rollData.excetional = 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, + }, + }); + }, + }, + two: { + icon: '', + label: game.i18n.localize("BUTTON.CANCEL"), + }, + }, + }, + { + classes: ["dialog", "kopparhavet"], + }).render(true); + } } \ No newline at end of file diff --git a/module/helpers/migration-helper.js b/module/helpers/migration-helper.js new file mode 100644 index 0000000..28ebcf7 --- /dev/null +++ b/module/helpers/migration-helper.js @@ -0,0 +1,21 @@ +/** + * Perform a system migration for the entire World, applying migrations for Actors, Items, and Compendium packs + * @return {Promise} A Promise which resolves once the migration is completed + */ +export const migrateWorld = async function () { + ui.notifications.info( + `Applying System Migration for version ${game.system.data.version}. Please be patient and do not close your game or shut down your server.`, + {permanent: true} + ); + + // Migrate to v0.0.3 from v0.0.2 and v0.0.1 + game.items.forEach((item) => { + if(item.data.type === "spell") { + item.update({"data.roll.label": "SPELL.ROLLTITLE"}); + } + }); + + // Set the migration as complete + game.settings.set("kopparhavet", "worldSchemaVersion", game.system.data.version); + ui.notifications.info(`System Migration to version ${game.system.data.version} completed!`, { permanent: true }); +}; \ No newline at end of file diff --git a/module/items/item-sheet.js b/module/items/item-sheet.js index a740701..76fa16d 100644 --- a/module/items/item-sheet.js +++ b/module/items/item-sheet.js @@ -36,7 +36,13 @@ export class ItemSheetKH extends ItemSheet { switch (this.object.data.type) { case "weapon": // Load Skills Compendium skills - let skillList2 = await game.packs.get("kopparhavet.skills").getContent(); + let skillList2 + + if(game.settings.get("kopparhavet", "gameSystem") === "hjaltarnas-tid") { + skillList2 = await game.packs.get("kopparhavet.skills-ht").getContent(); + } else { + skillList2 = await game.packs.get("kopparhavet.skills").getContent(); + } for (let item of skillList2) { if(item.data.type === "skill" && item.data.data.type.value === "combat") { @@ -66,6 +72,28 @@ export class ItemSheetKH extends ItemSheet { this.position.width = 405; this.position.height = 570; break; + case "spell": + // Load Skills Compendium skills + let skillList3 + + if(game.settings.get("kopparhavet", "gameSystem") === "hjaltarnas-tid") { + skillList3 = await game.packs.get("kopparhavet.skills-ht").getContent(); + } else { + skillList3 = await game.packs.get("kopparhavet.skills").getContent(); + } + + for (let item of skillList3) { + if(item.data.type === "skill") { + skillList.push(item) + } + } + + // Retrieve any created skills as well + for (let item of game.items.entities) { + if(item.data.type === "skill") { + skillList.push(item) + } + } default: this.position.width = 450; this.position.height = 605; diff --git a/module/kh-hooks.js b/module/kh-hooks.js index 49a7fb0..650d0ce 100644 --- a/module/kh-hooks.js +++ b/module/kh-hooks.js @@ -6,6 +6,9 @@ export default class KHHooks { let actorbaseSkills; if(game.settings.get("kopparhavet", "gameSystem") === "hjaltarnas-tid") { + // Set currency name + actor.update({ "data.currency.shekel.label": "CURRENCY.SILVER" }); + actorbaseSkills = CONFIG.KH.baseSkillsHT skillIndex = await game.packs.get("kopparhavet.skills-ht").getContent(); } else { diff --git a/module/kh-main.js b/module/kh-main.js index 763e905..86d5a46 100644 --- a/module/kh-main.js +++ b/module/kh-main.js @@ -4,6 +4,7 @@ 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"; +import * as migrations from "./helpers/migration-helper.js"; Hooks.once("init", () => { CONFIG.Combat.initiative = { formula: "(@combat.init)d6kh2", decimals: 0 }; @@ -20,7 +21,7 @@ Hooks.once("init", () => { scope: "world", config: true, default: 0, - type: Number, + type: String, }); game.settings.register("kopparhavet", "gameSystem", { name: "Game System", @@ -59,8 +60,6 @@ Hooks.on("createActor", async (actor, options, userId) => KHHooks.onCreateActor( 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 }); @@ -170,6 +169,20 @@ function registerHandlebarsHelpers() { } }); + Handlebars.registerHelper("spellRoll", function (roll) { + roll = normalize(roll, "roll"); + switch (roll) { + case "roll": + return game.i18n.localize("SPELL.ROLL"); + case "attackroll": + return game.i18n.localize("SPELL.ATTACKROLL"); + case "opposite": + return game.i18n.localize("SPELL.OPPOSITE"); + case "ritual": + return game.i18n.localize("SPELL.RITUAL"); + } + }); + Handlebars.registerHelper('plaintextToHTML', function(value) { // strip tags, add
tags return new Handlebars.SafeString(value.replace(/(<([^>]+)>)/gi, "").replace(/(?:\r\n|\r|\n)/g, '
')); @@ -177,12 +190,21 @@ function registerHandlebarsHelpers() { } function migrateWorld() { - game.actors.forEach((actor) => { - // Migrate to v0.0.2 from v0.0.1 - if(actor.data.type === "character") { - if(!actor.data?.data?.bio?.appearance) { - actor.update({"data.bio.appearance.label": "BIO.APPEARANCE", "data.bio.appearance.value": ""}); - } + // Determine whether a system migration is required and feasible + const currentVersion = game.settings.get("kopparhavet", "worldSchemaVersion"); + const NEEDS_MIGRATION_VERSION = "0.0.3"; + const COMPATIBLE_MIGRATION_VERSION = '0' || isNaN('NaN'); + let needMigration = currentVersion < NEEDS_MIGRATION_VERSION || currentVersion === null; + + // Perform the migration + if (needMigration && game.user.isGM) { + if (currentVersion && currentVersion < COMPATIBLE_MIGRATION_VERSION) { + ui.notifications.error( + `Your system data is from a version that cannot be reliably migrated to the latest version. The process will be attempted, but errors may occur.`, + { permanent: true } + ); } - }); + + migrations.migrateWorld(); + } } \ No newline at end of file diff --git a/styles/actors.css b/styles/actors.css index 46296c6..1a86394 100644 --- a/styles/actors.css +++ b/styles/actors.css @@ -188,6 +188,7 @@ } .sheet-tabs { + min-height: 36px; } .relation-list li:not(:last-child) { diff --git a/styles/items.css b/styles/items.css index e8ca394..43472d5 100644 --- a/styles/items.css +++ b/styles/items.css @@ -97,17 +97,24 @@ margin: auto; } -.items .items-list .item { +.items .items-list .item, +.items .items-list .item-spell, +.items .items-list .item-weapon, +.items .items-list .item-skill, +.items .items-list .item-nor, +.items .items-list .item-defence { line-height: 24px; padding: 3px 0; border-bottom: 1px solid #bbb; text-align: center; } -.items .items-list .item .toggle-equipped { +.items .items-list .item .toggle-equipped, +.items .items-list .item-weapon .toggle-equipped { color: #888; } -.items .items-list .item .toggle-equipped.active { +.items .items-list .item .toggle-equipped.active, +.items .items-list .item-weapon .toggle-equipped.active{ color: #191813; -} \ No newline at end of file +} diff --git a/system.json b/system.json index ebc97d9..90b6749 100644 --- a/system.json +++ b/system.json @@ -2,10 +2,10 @@ "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.2", + "version": "0.0.3", "minimumCoreVersion": "0.7.5", "compatibleCoreVersion": "0.7.7", - "templateVersion": 3, + "templateVersion": 4, "author": "Erebus", "scripts": [], "esmodules": [ @@ -53,6 +53,6 @@ "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.2.zip", + "download": "https://pi.rikspolisen.se/foundryvtt/kopparhavet/archive/v0.0.3.zip", "license": "" } diff --git a/template.json b/template.json index 5806e21..773113b 100644 --- a/template.json +++ b/template.json @@ -230,9 +230,12 @@ "core" ], "roll": { - "value": "", + "value": "roll", "type": "String", - "label": "SPELL.ROLL" + "label": "SPELL.ROLLTITLE", + "skill": "Trolldom", + "oppositeskill": "", + "ritual": {} }, "cost": { "value": "", diff --git a/templates/chat/item-card.html b/templates/chat/item-card.html index 553e89b..ec4f581 100644 --- a/templates/chat/item-card.html +++ b/templates/chat/item-card.html @@ -74,8 +74,7 @@ {{localize data.cost.label}}: {{data.cost.value}} {{localize data.difficulty.label}}: {{rollDifficulty data.difficulty.value}}
- {{localize data.roll.label}} - {{data.roll.value}} + {{localize data.roll.label}}: {{spellRoll data.roll.value}}
{{#if data.description}} diff --git a/templates/dice/roll.html b/templates/dice/roll.html index 1b79561..160d58b 100644 --- a/templates/dice/roll.html +++ b/templates/dice/roll.html @@ -4,6 +4,16 @@
+ {{#if closed}} +
+ {{localize "ROLL.CLOSED"}}: {{closed}} +
+ {{/if}} + {{#if opened}} +
+ {{localize "ROLL.OPENED"}}: {{opened}} +
+ {{/if}}
{{#if showFormula}}
{{res.formula}}
@@ -13,14 +23,14 @@
{{#if success}} -
- {{localize "ROLL.SUCCESS"}} -
+

+ {{#if excetional}}{{localize "ROLL.EXCEPTIONAL"}}{{/if}} {{localize "ROLL.SUCCESS"}} +

{{/if}} {{#if failure}} -
- {{localize "ROLL.FAILURE"}} -
+

+ {{#if excetional}}{{localize "ROLL.EXCEPTIONAL"}}{{/if}} {{localize "ROLL.FAILURE"}} +

{{/if}}
diff --git a/templates/items/spell-sheet.html b/templates/items/spell-sheet.html index 1dc9ee6..23696b6 100644 --- a/templates/items/spell-sheet.html +++ b/templates/items/spell-sheet.html @@ -26,7 +26,24 @@
- + +
+
+ +
@@ -39,4 +56,4 @@
- + \ No newline at end of file diff --git a/templates/parts/actor/combat.html b/templates/parts/actor/combat.html index 64e682d..0202642 100644 --- a/templates/parts/actor/combat.html +++ b/templates/parts/actor/combat.html @@ -2,16 +2,16 @@
- - + +
{{/each}} @@ -79,8 +79,8 @@ {{/if}}
- - + +
{{/each}} @@ -105,8 +105,8 @@
- - + +
{{/each}} diff --git a/templates/parts/actor/ht-combat.html b/templates/parts/actor/ht-combat.html index e287d61..e96173e 100644 --- a/templates/parts/actor/ht-combat.html +++ b/templates/parts/actor/ht-combat.html @@ -2,17 +2,17 @@
      -
    • +
    • {{localize "MOD.INIT"}}
    • -
    • +
    • {{localize "ADVERSARY.DEFENCE"}}
    • -
    • +
    • {{localize "ITEM.DEFENCE"}}
    • @@ -25,7 +25,7 @@
          {{#each actor.skills as |skill key|}} -
        • +
        • {{skill.name}}
        • @@ -44,7 +44,7 @@
            {{#each actor.attacks as |item key|}} -
          • +
          • {{item.name}}
            @@ -53,12 +53,12 @@
            -
            {{item.data.damage.value}}
            +
            {{item.data.damage.value}}
            - - + +
          • {{/each}} diff --git a/templates/parts/actor/ht-skills.html b/templates/parts/actor/ht-skills.html index 792334b..901a6c7 100644 --- a/templates/parts/actor/ht-skills.html +++ b/templates/parts/actor/ht-skills.html @@ -5,10 +5,10 @@
              {{#each actor.skills as |skill key|}} {{#if skill.hasAdventure}} -
            • -
              {{skill.name}}
              +
            • +
              {{skill.name}}
              -
              +
              @@ -25,10 +25,10 @@
                {{#each actor.skills as |skill key|}} {{#if skill.hasCombat}} -
              • -
                {{skill.name}}
                +
              • +
                {{skill.name}}
                -
                +
                diff --git a/templates/parts/actor/skills.html b/templates/parts/actor/skills.html index cb08d38..e413f5f 100644 --- a/templates/parts/actor/skills.html +++ b/templates/parts/actor/skills.html @@ -5,10 +5,10 @@
                  {{#each actor.skills as |skill key|}} {{#if skill.hasBase}} -
                • -
                  {{skill.name}}
                  +
                • +
                  {{skill.name}}
                  -
                  +
                  @@ -25,10 +25,10 @@
                    {{#each actor.skills as |skill key|}} {{#if skill.hasAdventure}} -
                  • -
                    {{skill.name}}
                    +
                  • +
                    {{skill.name}}
                    -
                    +
                    @@ -45,10 +45,10 @@
                      {{#each actor.skills as |skill key|}} {{#if skill.hasCombat}} -
                    • -
                      {{skill.name}}
                      +
                    • +
                      {{skill.name}}
                      -
                      +
                      diff --git a/templates/parts/actor/talent.html b/templates/parts/actor/talent.html index 779c9b5..696929c 100644 --- a/templates/parts/actor/talent.html +++ b/templates/parts/actor/talent.html @@ -9,8 +9,8 @@
                    • {{talent.name}}
                      - - + +
                    • {{/each}} @@ -25,13 +25,13 @@
                          {{#each actor.spells as |spell key|}} -
                        • -
                          {{spell.name}}
                          +
                        • +
                          {{spell.name}}
                          {{spell.data.cost.value}}
                          {{rollDifficulty spell.data.difficulty.value}}
                          - - + +
                        • {{/each}} diff --git a/templates/roll-dialog.html b/templates/roll-dialog.html new file mode 100644 index 0000000..1db1153 --- /dev/null +++ b/templates/roll-dialog.html @@ -0,0 +1,55 @@ +
                          +

                          {{localize skillName}} ({{skillValue}})

                          +
                          + + + + + +
                          +
                          + + \ No newline at end of file