v0.0.3 #2

Merged
erebus merged 12 commits from dev into master 2020-12-01 17:30:40 +01:00
23 changed files with 702 additions and 89 deletions

View File

@ -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",

View File

@ -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",

View File

@ -139,6 +139,186 @@ export class ActorSheetKH extends ActorSheet {
},
]);
new ContextMenu(html, "li.item-weapon", [
{
name: game.i18n.localize("MENU.SHOWROLLDIALOG"),
icon: '<i class="far dice"></i>',
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: '<i class="far fa-comment"></i>',
callback: (li) => {
let itemId = li.data("itemId");
this._itemDetailsToChat(itemId);
},
},
]);
new ContextMenu(html, "li.item-skill", [
{
name: game.i18n.localize("MENU.SHOWROLLDIALOG"),
icon: '<i class="far dice"></i>',
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: '<i class="far fa-comment"></i>',
callback: (li) => {
let itemId = li.data("itemId");
this._itemDetailsToChat(itemId);
},
},
]);
new ContextMenu(html, "li.item-spell", [
{
name: game.i18n.localize("MENU.SHOWROLLDIALOG"),
icon: '<i class="far dice"></i>',
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: '<i class="far fa-comment"></i>',
callback: (li) => {
let itemId = li.data("itemId");
this._itemDetailsToChat(itemId);
},
},
]);
new ContextMenu(html, "li.item-attack", [
{
name: game.i18n.localize("MENU.SHOWROLLDIALOG"),
icon: '<i class="far dice"></i>',
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: '<i class="far fa-comment"></i>',
callback: (li) => {
let itemId = li.data("itemId");
this._itemDetailsToChat(itemId);
},
},
]);
new ContextMenu(html, "li.item-defence", [
{
name: game.i18n.localize("MENU.SHOWROLLDIALOG"),
icon: '<i class="far dice"></i>',
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

View File

@ -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")
}
}

View File

@ -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: '<i class="fas fa-check"></i>',
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: '<i class="fas fa-times"></i>',
label: game.i18n.localize("BUTTON.CANCEL"),
},
},
},
{
classes: ["dialog", "kopparhavet"],
}).render(true);
}
}

View File

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

View File

@ -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;

View File

@ -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 {

View File

@ -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 <br/> tags
return new Handlebars.SafeString(value.replace(/(<([^>]+)>)/gi, "").replace(/(?:\r\n|\r|\n)/g, '<br/>'));
@ -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();
}
}

View File

@ -188,6 +188,7 @@
}
.sheet-tabs {
min-height: 36px;
}
.relation-list li:not(:last-child) {

View File

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

View File

@ -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": ""
}

View File

@ -230,9 +230,12 @@
"core"
],
"roll": {
"value": "",
"value": "roll",
"type": "String",
"label": "SPELL.ROLL"
"label": "SPELL.ROLLTITLE",
"skill": "Trolldom",
"oppositeskill": "",
"ritual": {}
},
"cost": {
"value": "",

View File

@ -74,8 +74,7 @@
<strong>{{localize data.cost.label}}:</strong> {{data.cost.value}}
<strong>{{localize data.difficulty.label}}:</strong> {{rollDifficulty data.difficulty.value}}
<div style="grid-column-start: 1; grid-column-end: 5;">
<strong>{{localize data.roll.label}}</strong>
{{data.roll.value}}
<strong>{{localize data.roll.label}}:</strong> {{spellRoll data.roll.value}}
</div>
</div>
{{#if data.description}}

View File

@ -4,6 +4,16 @@
<div class="roll">
<div class="dice-roll">
{{#if closed}}
<div style="text-align: center; margin-bottom: 0.5rem;">
{{localize "ROLL.CLOSED"}}: {{closed}}
</div>
{{/if}}
{{#if opened}}
<div style="text-align: center; margin-bottom: 0.5rem;">
{{localize "ROLL.OPENED"}}: {{opened}}
</div>
{{/if}}
<div class="dice-result">
{{#if showFormula}}
<div class="dice-formula-kh">{{res.formula}}</div>
@ -13,14 +23,14 @@
</div>
</div>
{{#if success}}
<div style="text-align: center; width: 100%; font-weight: bold; color: limegreen;">
{{localize "ROLL.SUCCESS"}}
</div>
<h2 style="border: none; text-align: center; width: 100%; font-weight: bold; color: limegreen;">
{{#if excetional}}{{localize "ROLL.EXCEPTIONAL"}}{{/if}} {{localize "ROLL.SUCCESS"}}
</h2>
{{/if}}
{{#if failure}}
<div style="text-align: center; width: 100%; font-weight: bold; color: darkred;">
{{localize "ROLL.FAILURE"}}
</div>
<h2 style="border: none; text-align: center; width: 100%; font-weight: bold; color: darkred;">
{{#if excetional}}{{localize "ROLL.EXCEPTIONAL"}}{{/if}} {{localize "ROLL.FAILURE"}}
</h2>
{{/if}}
</div>
</div>

View File

@ -26,7 +26,24 @@
</div>
<div style="grid-column-start: 1; grid-column-end: 3;">
<label>{{localize data.roll.label}}</label>
<input name="data.roll.value" type="text" value="{{data.roll.value}}" />
<select class="item-weapon-cat-select" name="data.roll.value">
{{#select data.roll.value}}
<option value="roll">{{localize "SPELL.ROLL"}}</option>
<option value="attackroll">{{localize "SPELL.ATTACKROLL"}}</option>
<option value="opposite">{{localize "SPELL.OPPOSITE"}}</option>
<option value="ritual">{{localize "SPELL.RITUAL"}}</option>
{{/select}}
</select>
</div>
<div style="grid-column-start: 1; grid-column-end: 3;">
<label>{{localize "ITEM.SKILL"}}</label>
<select class="item-weapon-skill-select" name="data.roll.skill">
{{#select data.roll.skill}}
{{#each this.khskills as |t|}}
<option value="{{t.name}}">{{t.name}}</option>
{{/each}}
{{/select}}
</select>
</div>
</div>
<div class="item">
@ -39,4 +56,4 @@
</div>
</div>
</div>
</form>
</form>

View File

@ -2,16 +2,16 @@
<div class="combat border">
<ul class="items">
<ul class="items-list">
<li class="item flexrow">
<li class="item-nor flexrow">
<div class="item-name" style="flex-grow: 8;">{{localize "MOD.INIT"}}</div>
<div class="item-value" style="flex: none;"><input name="data.combat.init" class="skill-value" type="number" value="{{data.combat.init}}" data-dtype="Number" /></div>
</li>
<li class="item flexrow">
<li class="item-defence flexrow" data-defence="{{data.combat.defence}}">
<div class="item-name roll-defence rollable" data-defence="{{data.combat.defence}}" style="flex-grow: 8;">{{localize "ADVERSARY.DEFENCE"}}</div>
<div class="item-value" style="flex: none;"><input name="data.combat.defence" class="skill-value" type="text" value="{{data.combat.defence}}" /></div>
</li>
<li class="item flexrow">
<li class="item-nor flexrow">
<div class="item-name" style="flex-grow: 8;">{{localize "ADVERSARY.HASHELMET"}}</div>
<div style="align-self: flex-end">
<a style="margin: auto;" class="adversary-helmet-click {{#if data.combat.helmet}}active{{/if}}">
@ -20,7 +20,7 @@
</div>
</li>
<li class="item flexrow">
<li class="item-nor flexrow">
<div class="item-name roll-adversary-armor rollable" data-armor="{{data.combat.armor}}" style="flex-grow: 8;">{{localize "ITEM.DEFENCE"}}</div>
<div class="item-value" style="flex: none;"><input name="data.combat.armor" class="skill-value" type="text" value="{{data.combat.armor}}" /></div>
</li>
@ -33,7 +33,7 @@
<ul class="items">
<ul class="items-list">
{{#each actor.skills as |skill key|}}
<li class="item flexrow" data-item-id="{{skill._id}}">
<li class="item-skill flexrow" data-item-id="{{skill._id}}">
<div class="item-name roll-skill rollable" style="flex-grow: 8;">{{skill.name}}</div>
<div class="item-value" style="flex: none;"><input class="skill-value" type="number" value="{{skill.data.value}}" min="0" data-item-id="{{skill._id}}" data-dtype="Number" /></div>
</li>
@ -52,7 +52,7 @@
<ul class="items-list">
{{#each actor.attacks as |item key|}}
<li class="item flexrow" data-item-id="{{item._id}}" data-ability="{{item.data.skill.value}}">
<li class="item-attack flexrow" data-item-id="{{item._id}}" data-ability="{{item.data.skill.value}}">
<div class="item-name">{{item.name}}</div>
<div class="skill-roll">
<div class="roll-button">
@ -61,12 +61,12 @@
</div>
<div class="skill-roll">
<div class="roll-button">
<div class="roll-damage rollable">{{item.data.damage.value}}</div>
<div class="roll-attack-damage rollable">{{item.data.damage.value}}</div>
</div>
</div>
<div class="item-controls">
<a class="item-control item-edit" title="Edit Item"><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a>
<a class="item-control item-edit" data-parent=".item-attack" title="Edit Item"><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" data-parent=".item-attack" title="Delete Item"><i class="fas fa-trash"></i></a>
</div>
</li>
{{/each}}

View File

@ -20,7 +20,7 @@
</li>
<ul class="items-list">
{{#each actor.weapons as |item id|}}
<li class="item flexrow" data-item-id="{{item._id}}" data-ability="{{item.data.skill.value}}">
<li class="item-weapon flexrow" data-item-id="{{item._id}}" data-ability="{{item.data.skill.value}}">
<div class="item-name">{{item.name}}</div>
<div>
{{#if item.data.equipable.equipped}}
@ -40,8 +40,8 @@
</div>
</div>
<div class="item-controls">
<a class="item-control item-edit" title="Edit Item"><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a>
<a class="item-control item-edit" data-parent=".item-weapon" title="Edit Item"><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" data-parent=".item-weapon" title="Delete Item"><i class="fas fa-trash"></i></a>
</div>
</li>
{{/each}}
@ -79,8 +79,8 @@
</div>
{{/if}}
<div class="item-controls">
<a class="item-control item-edit" title="Edit Item"><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a>
<a class="item-control item-edit" data-parent=".item" title="Edit Item"><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" data-parent=".item" title="Delete Item"><i class="fas fa-trash"></i></a>
</div>
</li>
{{/each}}
@ -105,8 +105,8 @@
<div class="quantity decrease"><i class="far fa-minus-square"></i></div>
</div>
<div class="item-controls">
<a class="item-control item-edit" title="Edit Item"><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a>
<a class="item-control item-edit" data-parent=".item" title="Edit Item"><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" data-parent=".item" title="Delete Item"><i class="fas fa-trash"></i></a>
</div>
</li>
{{/each}}

View File

@ -2,17 +2,17 @@
<div class="combat border">
<ul class="items">
<ul class="items-list">
<li class="item flexrow">
<li class="item-nor flexrow">
<div class="item-name" style="flex-grow: 8;">{{localize "MOD.INIT"}}</div>
<div class="item-value" style="flex: none;"><input name="data.combat.init" class="skill-value" type="number" value="{{data.combat.init}}" data-dtype="Number" /></div>
</li>
<li class="item flexrow">
<li class="item-defence flexrow" data-defence="{{data.combat.defence}}">
<div class="item-name roll-defence rollable" data-defence="{{data.combat.defence}}" style="flex-grow: 8;">{{localize "ADVERSARY.DEFENCE"}}</div>
<div class="item-value" style="flex: none;"><input name="data.combat.defence" class="skill-value" type="text" value="{{data.combat.defence}}" /></div>
</li>
<li class="item flexrow">
<li class="item-nor flexrow">
<div class="item-name roll-adversary-armor rollable" data-armor="{{data.combat.armor}}" style="flex-grow: 8;">{{localize "ITEM.DEFENCE"}}</div>
<div class="item-value" style="flex: none;"><input name="data.combat.armor" class="skill-value" type="text" value="{{data.combat.armor}}" /></div>
</li>
@ -25,7 +25,7 @@
<ul class="items">
<ul class="items-list">
{{#each actor.skills as |skill key|}}
<li class="item flexrow" data-item-id="{{skill._id}}">
<li class="item-skill flexrow" data-item-id="{{skill._id}}">
<div class="item-name roll-skill rollable" style="flex-grow: 8;">{{skill.name}}</div>
<div class="item-value" style="flex: none;"><input class="skill-value" type="number" value="{{skill.data.value}}" min="0" data-item-id="{{skill._id}}" data-dtype="Number" /></div>
</li>
@ -44,7 +44,7 @@
<ul class="items-list">
{{#each actor.attacks as |item key|}}
<li class="item flexrow" data-item-id="{{item._id}}" data-ability="{{item.data.skill.value}}">
<li class="item-attack flexrow" data-item-id="{{item._id}}" data-ability="{{item.data.skill.value}}">
<div class="item-name">{{item.name}}</div>
<div class="skill-roll">
<div class="roll-button">
@ -53,12 +53,12 @@
</div>
<div class="skill-roll">
<div class="roll-button">
<div class="roll-damage rollable">{{item.data.damage.value}}</div>
<div class="roll-attack-damage rollable">{{item.data.damage.value}}</div>
</div>
</div>
<div class="item-controls">
<a class="item-control item-edit" title="Edit Item"><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a>
<a class="item-control item-edit" data-parent=".item-attack" title="Edit Item"><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" data-parent=".item-attack" title="Delete Item"><i class="fas fa-trash"></i></a>
</div>
</li>
{{/each}}

View File

@ -5,10 +5,10 @@
<ul class="items-list">
{{#each actor.skills as |skill key|}}
{{#if skill.hasAdventure}}
<li class="item flexrow" data-item-id="{{skill._id}}">
<div class="item-name roll-skill" style="flex-grow: 8;">{{skill.name}}</div>
<li class="item-skill flexrow" data-item-id="{{skill._id}}">
<div class="item-name roll-skill rollable" style="flex-grow: 8;">{{skill.name}}</div>
<div class="item-value" style="flex: none;"><input class="skill-value" type="number" value="{{skill.data.value}}" min="0" data-item-id="{{skill._id}}" data-dtype="Number" /></div>
<div style="align-self: flex-end">
<div style="align-self: flex-end; margin-top: auto; margin-bottom: auto;">
<a style="margin: auto;" class="click-skill {{#if skill.data.used}}active{{/if}}" data-item-id="{{skill._id}}">
<i class="far {{#if skill.data.used}}fa-times-circle{{else}}fa-circle{{/if}}"></i>
</a>
@ -25,10 +25,10 @@
<ul class="items-list">
{{#each actor.skills as |skill key|}}
{{#if skill.hasCombat}}
<li class="item flexrow" data-item-id="{{skill._id}}">
<div class="item-name roll-skill" style="flex-grow: 8;">{{skill.name}}</div>
<li class="item-skill flexrow" data-item-id="{{skill._id}}">
<div class="item-name roll-skill rollable" style="flex-grow: 8;">{{skill.name}}</div>
<div class="item-value" style="flex: none;"><input class="skill-value" type="number" value="{{skill.data.value}}" min="0" data-item-id="{{skill._id}}" data-dtype="Number" /></div>
<div style="align-self: flex-end">
<div style="align-self: flex-end; margin-top: auto; margin-bottom: auto;">
<a style="margin: auto;" class="click-skill {{#if skill.data.used}}active{{/if}}" data-item-id="{{skill._id}}">
<i class="far {{#if skill.data.used}}fa-times-circle{{else}}fa-circle{{/if}}"></i>
</a>

View File

@ -5,10 +5,10 @@
<ul class="items-list">
{{#each actor.skills as |skill key|}}
{{#if skill.hasBase}}
<li class="item flexrow" data-item-id="{{skill._id}}">
<div class="item-name roll-skill" style="flex-grow: 8;">{{skill.name}}</div>
<li class="item-skill flexrow" data-item-id="{{skill._id}}">
<div class="item-name roll-skill rollable" style="flex-grow: 8;">{{skill.name}}</div>
<div class="item-value" style="flex: none;"><input class="skill-value" type="number" value="{{skill.data.value}}" min="0" data-item-id="{{skill._id}}" data-dtype="Number" /></div>
<div style="align-self: flex-end">
<div style="align-self: flex-end; margin-top: auto; margin-bottom: auto;">
<a style="margin: auto;" class="click-skill {{#if skill.data.used}}active{{/if}}" data-item-id="{{skill._id}}">
<i class="far {{#if skill.data.used}}fa-times-circle{{else}}fa-circle{{/if}}"></i>
</a>
@ -25,10 +25,10 @@
<ul class="items-list">
{{#each actor.skills as |skill key|}}
{{#if skill.hasAdventure}}
<li class="item flexrow" data-item-id="{{skill._id}}">
<div class="item-name roll-skill" style="flex-grow: 8;">{{skill.name}}</div>
<li class="item-skill flexrow" data-item-id="{{skill._id}}">
<div class="item-name roll-skill rollable" style="flex-grow: 8;">{{skill.name}}</div>
<div class="item-value" style="flex: none;"><input class="skill-value" type="number" value="{{skill.data.value}}" min="0" data-item-id="{{skill._id}}" data-dtype="Number" /></div>
<div style="align-self: flex-end">
<div style="align-self: flex-end; margin-top: auto; margin-bottom: auto;">
<a style="margin: auto;" class="click-skill {{#if skill.data.used}}active{{/if}}" data-item-id="{{skill._id}}">
<i class="far {{#if skill.data.used}}fa-times-circle{{else}}fa-circle{{/if}}"></i>
</a>
@ -45,10 +45,10 @@
<ul class="items-list">
{{#each actor.skills as |skill key|}}
{{#if skill.hasCombat}}
<li class="item flexrow" data-item-id="{{skill._id}}">
<div class="item-name roll-skill" style="flex-grow: 8;">{{skill.name}}</div>
<li class="item-skill flexrow" data-item-id="{{skill._id}}">
<div class="item-name roll-skill rollable" style="flex-grow: 8;">{{skill.name}}</div>
<div class="item-value" style="flex: none;"><input class="skill-value" type="number" value="{{skill.data.value}}" min="0" data-item-id="{{skill._id}}" data-dtype="Number" /></div>
<div style="align-self: flex-end">
<div style="align-self: flex-end; margin-top: auto; margin-bottom: auto;">
<a style="margin: auto;" class="click-skill {{#if skill.data.used}}active{{/if}}" data-item-id="{{skill._id}}">
<i class="far {{#if skill.data.used}}fa-times-circle{{else}}fa-circle{{/if}}"></i>
</a>

View File

@ -9,8 +9,8 @@
<li class="item flexrow" data-item-id="{{talent._id}}">
<div class="item-name">{{talent.name}}</div>
<div class="item-controls">
<a class="item-control item-edit" title="Edit Item"><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a>
<a class="item-control item-edit" data-parent=".item" title="Edit Item"><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" data-parent=".item" title="Delete Item"><i class="fas fa-trash"></i></a>
</div>
</li>
{{/each}}
@ -25,13 +25,13 @@
<ul class="items">
<ul class="items-list">
{{#each actor.spells as |spell key|}}
<li class="item flexrow" data-item-id="{{spell._id}}">
<div class="item-name">{{spell.name}}</div>
<li class="item-spell flexrow" data-item-id="{{spell._id}}">
<div class="item-name roll-spell-skill rollable" style="margin-top: auto; margin-bottom: auto;">{{spell.name}}</div>
<div class="cost roll-spell-cost rollable">{{spell.data.cost.value}}</div>
<div class="difficulty">{{rollDifficulty spell.data.difficulty.value}}</div>
<div class="item-controls">
<a class="item-control item-edit" title="Edit Item"><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a>
<a class="item-control item-edit" data-parent=".item-spell" title="Edit Item"><i class="fas fa-edit"></i></a>
<a class="item-control item-delete" data-parent=".item-spell" title="Delete Item"><i class="fas fa-trash"></i></a>
</div>
</li>
{{/each}}

View File

@ -0,0 +1,55 @@
<div id="{{id}}" class="roll-dialog">
<h1 style="text-align: center;">{{localize skillName}} ({{skillValue}})</h1>
<div class="flexrow" style="margin-bottom: 10px;">
<label style="margin: auto;">{{localize "ROLL.OPENCLOSE"}}</label>
<input type="button" name="openclosed" min="0" value="{{startopen}}" style="min-height: 30px; min-width: 45px;" />
<label name="notopenedorclosed" style="margin: auto; display: inline-block;">&nbsp;</label>
<label name="closed" style="margin: auto; display: none; text-align: center;">{{localize "ROLL.CLOSED"}}</label>
<label name="opened" style="margin: auto; display: none; text-align: center;">{{localize "ROLL.OPENED"}}</label>
</div>
</div>
<script>
(function() {
const scope = document.getElementById("{{{id}}}");
const input = scope.querySelector(`[name="openclosed"`);
const closed = scope.querySelector(`[name="closed"`);
const opened = scope.querySelector(`[name="opened"`);
const none = scope.querySelector(`[name="notopenedorclosed"`);
input.addEventListener("click", (event) => {
event.target.value++;
if(event.target.value > 0) {
closed.style.display = "none"
none.style.display = "none"
opened.style.display = "inline-block"
} else if(event.target.value < 0) {
opened.style.display = "none"
none.style.display = "none"
closed.style.display = "inline-block"
} else {
opened.style.display = "none"
closed.style.display = "none"
none.style.display = "inline-block"
}
});
input.addEventListener("contextmenu", (event) => {
event.target.value--;
if(event.target.value > 0) {
closed.style.display = "none"
none.style.display = "none"
opened.style.display = "inline-block"
} else if(event.target.value < 0) {
opened.style.display = "none"
none.style.display = "none"
closed.style.display = "inline-block"
} else {
opened.style.display = "none"
closed.style.display = "none"
none.style.display = "inline-block"
}
});
})();
</script>