first commit

pull/1/head
Erebus 2020-11-28 19:12:19 +01:00
commit c3a7388592
38 changed files with 3189 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.idea/
*.code-workspace
.history/
deploy
createRelease
TODO

0
README.md Normal file
View File

87
lang/en.json Normal file
View File

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

87
lang/sv.json Normal file
View File

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

View File

@ -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: '<i class="far fa-comment"></i>',
callback: (li) => {
let itemId = li.data("itemId");
this._itemDetailsToChat(itemId);
},
},
]);
new ContextMenu(html, "div.item", [
{
name: game.i18n.localize("MENU.SENTTOCHAT"),
icon: '<i class="far fa-comment"></i>',
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);
}
}

18
module/actors/actor.js Normal file
View File

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

View File

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

View File

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

157
module/items/item-sheet.js Normal file
View File

@ -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 = `<input type="text" name="data.modifications.attr${nk}.key" value="attr${nk}" style="display:none;"/><select class="modification-modtype" name="data.modifications.attr${nk}.modtype"><option value="init">Turordning</option></select><input class="modification-value" type="text" name="data.modifications.attr${nk}.value" value="0" data-dtype="Number" placeholder="0"/>`;
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;
}
}

62
module/items/item.js Normal file
View File

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

26
module/kh-config.js Normal file
View File

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

96
module/kh-dice.js Normal file
View File

@ -0,0 +1,96 @@
class KHDice {
static async Init(controls, html) {
const diceRollbtn = $(
`
<li class="scene-control sdr-scene-control">
<i class="fas fa-dice-d20"></i>
<ol class="control-tools">
<div id="SDRpopup" class="kh-dice-roller-popup" style="display: none;">
<ul>
<li data-dice-type="6" data-dice-roll="1" class="sdr-col1"><i class="df-d6-6" data-dice-type="6" data-dice-roll="1"></i> d6</li>
<li data-dice-type="6" data-dice-roll="2">2</li>
<li data-dice-type="6" data-dice-roll="3">3</li>
<li data-dice-type="6" data-dice-roll="4">4</li>
<li data-dice-type="6" data-dice-roll="5" class="sdr-lastcol">5</li>
</ul>
<ul>
<li data-dice-type="10" data-dice-roll="1" class="sdr-col1"><i class="df-d10-10" data-dice-type="10" data-dice-roll="1"></i> d10</li>
<li data-dice-type="10" data-dice-roll="2">2</li>
<li data-dice-type="10" data-dice-roll="3">3</li>
<li data-dice-type="10" data-dice-roll="4">4</li>
<li data-dice-type="10" data-dice-roll="5" class="sdr-lastcol">5</li>
</ul>
<ul class="sdr-lastrow">
<li data-dice-type="100" data-dice-roll="1" class="sdr-col1"><i class="df-d10-10" data-dice-type="100" data-dice-roll="1"></i><i class="df-d10-10" data-dice-type="100" data-dice-roll="1"></i> d100</li>
<li data-dice-type="100" data-dice-roll="2">2</li>
<li data-dice-type="100" data-dice-roll="3">3</li>
<li data-dice-type="100" data-dice-roll="4">4</li>
<li data-dice-type="100" data-dice-roll="5" class="sdr-lastcol">5</li>
</ul>
</div>
</ol>
</li>
`
);
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");

23
module/kh-hooks.js Normal file
View File

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

131
module/kh-main.js Normal file
View File

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

35
packs/skills.db Normal file
View File

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

191
styles/actors.css Normal file
View File

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

140
styles/common.css Normal file
View File

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

65
styles/dice-roller.css Normal file
View File

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

113
styles/items.css Normal file
View File

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

51
system.json Normal file
View File

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

307
template.json Normal file
View File

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

View File

@ -0,0 +1,51 @@
<form class="{{cssClass}}" autocomplete="off">
<div class="adversary">
<div class="header grid-container" style="grid-template-columns: auto 1fr auto 1fr auto;margin-bottom: 5px">
<div class="avatar flex-group-center" style="grid-row: 1/7;">
<img class="actor-avatar-img" src="{{actor.img}}" data-edit="img" title="{{actor.name}}" />
</div>
<label style="margin: auto;">{{localize "BIO.NAME"}}:</label>
<h1 class="charname" style="margin-bottom: 0;grid-column: 3/6;"><input name="name" type="text" value="{{actor.name}}" placeholder="{{localize "BIO.NAME"}}" /></h1>
<label style="margin: auto;">{{localize data.health.label}}:</label>
<div class="grid-container" style="grid-template-columns: auto 1fr auto;">
<input class="center-text" type="text" name="data.health.value" value="{{data.health.value}}" data-dtype="Number" />
<span> / </span>
<input class="center-text" type="text" name="data.health.max" value="{{data.health.max}}" data-dtype="Number" />
</div>
<label style="margin: auto;">{{localize data.stamina.label}}:</label>
<div class="grid-container" style="grid-template-columns: auto 1fr auto;">
<input class="center-text" type="text" name="data.stamina.value" value="{{data.stamina.value}}" data-dtype="Number" />
<span> / </span>
<input class="center-text" type="text" name="data.stamina.max" value="{{data.stamina.max}}" data-dtype="Number" />
</div>
<label style="margin: auto;">{{localize data.mana.label}}:</label>
<div class="grid-container" style="grid-template-columns: auto 1fr auto;">
<input class="center-text" type="text" name="data.mana.value" value="{{data.mana.value}}" data-dtype="Number" />
<span> / </span>
<input class="center-text" type="text" name="data.mana.max" value="{{data.mana.max}}" data-dtype="Number" />
</div>
</div>
<div class="sheet-tabs tabs flex row" data-group="primary">
<b class="item" data-tab="combat">{{localize "TAB.COMBAT"}}</b>
<b class="item" data-tab="talent">{{localize "TAB.TALENTS"}}</b>
<b class="item" data-tab="note">{{localize "TAB.NOTE"}}</b>
</div>
<div class="sheet-body">
<div class="tab" data-group="primary" data-tab="combat">
{{> systems/kopparhavet/templates/parts/actor/combat.html}}
</div>
<div class="tab" data-group="primary" data-tab="talent">
{{> systems/kopparhavet/templates/parts/actor/talent.html}}
</div>
<div class="tab border note" data-group="primary" data-tab="note" style="height: 100%; border-top: none;">
{{editor content=data.bio.note.value target="data.bio.note.value" button=true owner=owner editable=editable}}
</div>
</div>
</div>
</form>

View File

@ -0,0 +1,95 @@
<form class="{{cssClass}}" autocomplete="off">
<div class="character">
<div class="header grid-container" style="grid-template-columns: auto auto 1.7fr 2fr;">
<div class="avatar flex-group-center" style="grid-row: 1/7;">
<img class="actor-avatar-img" src="{{actor.img}}" data-edit="img" title="{{actor.name}}" />
</div>
<label style="margin: auto;">{{localize "BIO.NAME"}}:</label>
<h1 class="charname" style="margin-bottom: 0;grid-column: 3/5;"><input name="name" type="text" value="{{actor.name}}" placeholder="{{localize "BIO.NAME"}}" /></h1>
<label style="margin: auto;">{{localize data.bio.profession.label}}:</label>
<input name="data.bio.profession.value" type="text" value="{{data.bio.profession.value}}" placeholder="{{localize data.bio.profession.label}}" />
<label style="margin: auto;">{{localize data.bio.kin.label}}:</label>
<input name="data.bio.kin.value" type="text" value="{{data.bio.kin.value}}" placeholder="{{localize data.bio.kin.label}}" />
<label style="margin: auto;">{{localize data.health.label}}:</label>
<div class="grid-container" style="grid-template-columns: auto 1fr auto;">
<input class="center-text" type="text" name="data.health.value" value="{{data.health.value}}" data-dtype="Number" />
<span> / </span>
<input class="center-text" type="text" name="data.health.max" value="{{data.health.max}}" data-dtype="Number" />
</div>
<label style="margin: auto;">{{localize data.stamina.label}}:</label>
<div class="grid-container" style="grid-template-columns: auto 1fr auto;">
<input class="center-text" type="text" name="data.stamina.value" value="{{data.stamina.value}}" data-dtype="Number" />
<span> / </span>
<input class="center-text" type="text" name="data.stamina.max" value="{{data.stamina.max}}" data-dtype="Number" />
</div>
<label style="margin: auto;">{{localize data.mana.label}}:</label>
<div class="grid-container" style="grid-template-columns: auto 1fr auto;">
<input class="center-text" type="text" name="data.mana.value" value="{{data.mana.value}}" data-dtype="Number" />
<span> / </span>
<input class="center-text" type="text" name="data.mana.max" value="{{data.mana.max}}" data-dtype="Number" />
</div>
<div class="border grid-container" style="grid-column-gap: 5px; grid-column: 4; grid-row: 2/7; grid-template-columns: 1fr auto;">
<h2 style="margin-bottom: 0; grid-column: 1/3;">{{localize "FEATURES.TITLE"}}</h2>
<input name="data.feature.one.label" class="roll-feature" type="text" value="{{data.feature.one.label}}" />
<a style="margin: auto;" class="feature {{#if data.feature.one.value}}active{{/if}}" data-feature="one">
<i class="far {{#if data.feature.one.value}}fa-times-circle{{else}}fa-circle{{/if}}"></i>
</a>
<input name="data.feature.two.label" class="roll-feature" type="text" value="{{data.feature.two.label}}" />
<a style="margin: auto;" class="feature {{#if data.feature.two.value}}active{{/if}}" data-feature="two">
<i class="far {{#if data.feature.two.value}}fa-times-circle{{else}}fa-circle{{/if}}"></i>
</a>
<input name="data.feature.three.label" class="roll-feature" type="text" value="{{data.feature.three.label}}" />
<a style="margin: auto;" class="feature {{#if data.feature.three.value}}active{{/if}}" data-feature="three">
<i class="far {{#if data.feature.three.value}}fa-times-circle{{else}}fa-circle{{/if}}"></i>
</a>
<input name="data.feature.four.label" class="roll-feature" type="text" value="{{data.feature.four.label}}" />
<a style="margin: auto;" class="feature {{#if data.feature.four.value}}active{{/if}}" data-feature="four">
<i class="far {{#if data.feature.four.value}}fa-times-circle{{else}}fa-circle{{/if}}"></i>
</a>
</div>
</div>
<div class="sheet-tabs tabs flex row" data-group="primary">
<b class="item" data-tab="skills">{{localize "TAB.SKILLS"}}</b>
<!-- <b class="item" data-tab="combat">{{localize "TAB.COMBAT"}}</b> -->
<b class="item" data-tab="talent">{{localize "TAB.TALENTS"}}</b>
<b class="item" data-tab="gear">{{localize "TAB.GEAR"}}</b>
<b class="item" data-tab="bio">{{localize "TAB.BIO"}}</b>
<b class="item" data-tab="note">{{localize "TAB.NOTE"}}</b>
</div>
<div class="sheet-body">
<div class="tab" data-group="primary" data-tab="skills">
{{> systems/kopparhavet/templates/parts/actor/skills.html}}
</div>
<!--
<div class="tab" data-group="primary" data-tab="combat">
{{> systems/kopparhavet/templates/parts/actor/combat.html}}
</div>
-->
<div class="tab" data-group="primary" data-tab="talent">
{{> systems/kopparhavet/templates/parts/actor/talent.html}}
</div>
<div class="tab" data-group="primary" data-tab="gear">
{{> systems/kopparhavet/templates/parts/actor/gear.html}}
</div>
<div class="tab" data-group="primary" data-tab="bio">
{{> systems/kopparhavet/templates/parts/actor/bio.html}}
</div>
<div class="tab note border" data-group="primary" data-tab="note">
{{editor content=data.bio.note.value target="data.bio.note.value" button=true owner=owner editable=editable}}
</div>
</div>
</div>
</form>

View File

@ -0,0 +1,105 @@
<div class="kopparhavet chat-item">
<div class="border">
{{#if img}}
<img src="{{img}}" title="{{name}}" />
{{/if}}
<h2>{{name}}</h2>
{{#if isGear}}
<h4>{{localize "ITEM.GEAR"}}</h4>
{{/if}}
{{#if isAdversaryAttack}}
<h4>{{localize "ITEM.ATTACK"}}</h4>
{{/if}}
{{#if isSkill}}
<h4>{{skillType data.type.value}}</h4>
{{/if}}
{{#if isSpell}}
<h4>{{localize "ITEM.SPELL"}}</h4>
{{/if}}
{{#if isTalent}}
<h4>{{localize "ITEM.TALENT"}}</h4>
{{/if}}
{{#if isWeapon}}
<h4>{{weaponCategory data.category.value}}</h4>
{{/if}}
{{#if isArmor}}
<div class="grid-container" style="grid-template-columns: 1fr auto 1fr auto">
<strong>{{localize data.price.label}}:</strong> {{data.price.value}}
{{#if data.helmet.value}}
<strong>{{localize data.helmet.label}}</strong>
{{else}}
<strong>{{localize data.defence.label}}:</strong> {{data.defence.value}}
{{/if}}
</div>
{{#if data.description}}
<h5>{{localize data.description.label}}</h5>
{{{data.description.value}}}
{{/if}}
{{/if}}
{{#if isGear}}
<div class="grid-container" style="grid-template-columns: 1fr auto 1fr auto;">
<strong>{{localize data.price.label}}:</strong> {{data.price.value}}
<strong>{{localize data.quantity.label}}:</strong> {{data.quantity.value}}
</div>
{{#if data.description}}
<h5>{{localize data.description.label}}</h5>
{{{data.description.value}}}
{{/if}}
{{/if}}
{{#if isSkill}}
{{#if data.description}}
<h5>{{localize data.description.label}}</h5>
{{{data.description.value}}}
{{/if}}
{{/if}}
{{#if isAdversaryAttack}}
<div class="grid-container" style="grid-template-columns: 1fr auto 1fr auto">
<strong>{{localize data.skill.label}}:</strong> {{data.skill.value}}
<strong>{{localize data.damage.label}}:</strong> {{data.damage.value}}
</div>
{{#if data.description}}
<h5>{{localize data.description.label}}</h5>
{{{data.description.value}}}
{{/if}}
{{/if}}
{{#if isSpell}}
<div class="grid-container" style="grid-template-columns: 1fr auto 1fr auto">
<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}}
</div>
</div>
{{#if data.description}}
<h5>{{localize data.description.label}}</h5>
{{{data.description.value}}}
{{/if}}
{{/if}}
{{#if isTalent}}
{{#if data.description}}
<h5>{{localize data.description.label}}</h5>
{{{data.description.value}}}
{{/if}}
{{/if}}
{{#if isWeapon}}
<div class="grid-container" style="grid-template-columns: 1fr auto 1fr auto">
<strong>{{localize data.price.label}}:</strong> {{data.price.value}}
<strong>{{localize data.damage.label}}:</strong> {{data.damage.value}}
</div>
{{#if data.description}}
<h5>{{localize data.description.label}}</h5>
{{{data.description.value}}}
{{/if}}
{{/if}}
</div>
</div>

26
templates/dice/roll.html Normal file
View File

@ -0,0 +1,26 @@
<div class="kopparhavet roll-item">
<div class="border">
<h3>{{localize name}}</h3>
<div class="roll">
<div class="dice-roll">
<div class="dice-result">
{{#if showFormula}}
<div class="dice-formula-kh">{{res.formula}}</div>
{{/if}}
<h2>{{res.total}}</h2>
</div>
</div>
</div>
{{#if success}}
<div style="text-align: center; width: 100%; font-weight: bold; color: limegreen;">
{{localize "ROLL.SUCCESS"}}
</div>
{{/if}}
{{#if failure}}
<div style="text-align: center; width: 100%; font-weight: bold; color: darkred;">
{{localize "ROLL.FAILURE"}}
</div>
{{/if}}
</div>
</div>

View File

@ -0,0 +1,39 @@
<form class="{{cssClass}} item-sheet-talent" autocomplete="off">
<div class="header">
<div class="avatar flex-group-center" style="grid-row: 1/3;">
<img class="item-avatar-img" src="{{item.img}}" data-edit="img" title="{{item.name}}" />
</div>
<div class="container flex-group-center item-name">
<h1><input name="name" type="text" value="{{item.name}}" placeholder="{{localize "ITEM.ATTACK"}}" /></h1>
</div>
</div>
<div class="grid-container" style="grid-template-columns: 1fr 1fr;">
<div class="rating">
<label>{{localize data.damage.label}}</label>
<input name="data.damage.value" type="text" value="{{data.damage.value}}" />
</div>
<div class="rating">
<label>{{localize data.skill.label}}</label>
<input name="data.skill.value" type="text" value="{{data.skill.value}}" />
</div>
<div>
<label>{{localize data.category.label}}</label>
<select class="item-weapon-cat-select" name="data.category.value">
{{#select data.category.value}}
<option value="melee">{{localize "WEAPON.MELEE"}}</option>
<option value="ranged">{{localize "WEAPON.RANGED"}}</option>
{{/select}}
</select>
</div>
</div>
<div class="item">
<div class="description">
<h2>{{localize data.description.label}}</h2>
<div class="info">
<div class="description">
{{editor content=data.description.value target="data.description.value" button=true owner=owner editable=editable}}
</div>
</div>
</div>
</div>
</form>

View File

@ -0,0 +1,72 @@
<form class="{{cssClass}}" autocomplete="off">
<div class="header">
<div class="avatar flex-group-center" style="grid-row: 1/3;">
<img class="item-avatar-img" src="{{item.img}}" data-edit="img" title="{{item.name}}" />
</div>
<div class="container flex-group-center item-name">
<h1><input name="name" type="text" value="{{item.name}}" placeholder="{{localize "ITEM.ARMOR"}}" /></h1>
</div>
</div>
<div class="grid-container" style="grid-template-columns: 1fr 1fr;">
<!--
<div class="supply">
<label>{{localize data.quantity.label}}</label>
<input name="data.quantity.value" type="number" value="{{data.quantity.value}}" />
</div>
-->
<div class="cost">
<label>{{localize data.price.label}}</label>
<input name="data.price.value" type="text" value="{{data.price.value}}" />
</div>
<div class="rating">
<label>{{localize data.defence.label}}</label>
<input name="data.defence.value" type="text" value="{{data.defence.value}}" />
</div>
<div>
<label>{{localize data.type.label}}</label>
<select class="item-weapon-cat-select" name="data.type.value">
{{#select data.type.value}}
{{#each this.KH.armor_types as |t|}}
<option value="{{t.value}}">{{localize t.label}}</option>
{{/each}}
{{/select}}
</select>
</div>
<div class="grid-container" style="grid-template-columns: 1fr auto;">
<!--
<label>{{localize data.equipable.label}}</label>
<a class="item-bool-click {{#if data.equipable.value}}active{{/if}}" data-name="equipable" title="equipable">
<i class="far {{#if data.equipable.value}}fa-times-circle{{else}}fa-circle{{/if}}"></i>
</a>
-->
<label>{{localize data.helmet.label}}</label>
<a class="item-bool-click {{#if data.helmet.value}}active{{/if}}" data-name="helmet" title="helmet">
<i class="far {{#if data.helmet.value}}fa-times-circle{{else}}fa-circle{{/if}}"></i>
</a>
</div>
</div>
<div class="sheet-tabs tabs flex row" data-group="primary">
<b class="item" data-tab="description">{{localize "TAB.DESCRIPTION"}}</b>
<b class="item" data-tab="modifiers">{{localize "TAB.MODIFICATION"}}</b>
</div>
<div class="sheet-body">
<div class="tab scroll-y" data-group="primary" data-tab="description">
<div class="item">
<div class="description">
<h2>{{localize data.description.label}}</h2>
<div class="info">
<div class="description">
{{editor content=data.description.value target="data.description.value" button=true owner=owner editable=editable}}
</div>
</div>
</div>
</div>
</div>
<div class="tab scroll-y" data-group="primary" data-tab="modifiers">
{{> systems/kopparhavet/templates/parts/shared/modifications.html}}
</div>
</div>
</form>

View File

@ -0,0 +1,30 @@
<form class="{{cssClass}}" autocomplete="off">
<div class="header">
<div class="avatar flex-group-center" style="grid-row: 1/3;">
<img class="item-avatar-img" src="{{item.img}}" data-edit="img" title="{{item.name}}" />
</div>
<div class="container flex-group-center item-name">
<h1><input name="name" type="text" value="{{item.name}}" placeholder="{{localize "ITEM.GEAR"}}" /></h1>
</div>
<div class="grid-container" style="grid-template-columns: 1fr 1fr;">
<div class="supply">
<label>{{localize data.quantity.label}}</label>
<input name="data.quantity.value" type="number" value="{{data.quantity.value}}" />
</div>
<div class="cost">
<label>{{localize data.price.label}}</label>
<input name="data.price.value" type="text" value="{{data.price.value}}" />
</div>
</div>
</div>
<div class="item">
<div class="description">
<h2>{{localize data.description.label}}</h2>
<div class="info-mid">
<div class="description">
{{editor content=data.description.value target="data.description.value" button=true owner=owner editable=editable}}
</div>
</div>
</div>
</div>
</form>

View File

@ -0,0 +1,36 @@
<form class="{{cssClass}}" autocomplete="off">
<div class="header">
<div class="avatar flex-group-center" style="grid-row: 1/3;">
<img class="item-avatar-img" src="{{item.img}}" data-edit="img" title="{{item.name}}" />
</div>
<div class="container flex-group-center item-name">
<h1><input name="name" type="text" value="{{item.name}}" placeholder="{{localize "ITEM.SKILL"}}" /></h1>
</div>
</div>
<div class="grid-container" style="grid-template-columns: 1fr 1fr;">
<div>
<label>{{localize data.type.label}}</label>
<select class="item-weapon-cat-select" name="data.type.value">
{{#select data.type.value}}
<option value="base">{{localize "SKILL.BASE"}}</option>
<option value="adventure">{{localize "SKILL.ADVENTURE"}}</option>
<option value="combat">{{localize "SKILL.COMBAT"}}</option>
{{/select}}
</select>
</div>
<div>
<label>{{localize "SKILL.STARTVALUE"}}</label>
<input name="data.value" type="text" value="{{data.value}}" />
</div>
</div>
<div class="item">
<div class="description">
<h2>{{localize data.description.label}}</h2>
<div class="info-mid">
<div class="description">
{{editor content=data.description.value target="data.description.value" button=true owner=owner editable=editable}}
</div>
</div>
</div>
</div>
</form>

View File

@ -0,0 +1,42 @@
<form class="{{cssClass}} item-sheet-talent" autocomplete="off">
<div class="header">
<div class="avatar flex-group-center" style="grid-row: 1/3;">
<img class="item-avatar-img" src="{{item.img}}" data-edit="img" title="{{item.name}}" />
</div>
<div class="container flex-group-center item-name">
<h1><input name="name" type="text" value="{{item.name}}" placeholder="{{localize "ITEM.ARMOR"}}" /></h1>
</div>
</div>
<div class="grid-container" style="grid-template-columns: 1fr 1fr;">
<div class="cost">
<label>{{localize data.cost.label}}</label>
<input name="data.cost.value" type="text" value="{{data.cost.value}}" />
</div>
<div>
<label>{{localize data.difficulty.label}}</label>
<select class="item-weapon-cat-select" name="data.difficulty.value">
{{#select data.difficulty.value}}
<option value="simple">{{localize "DIFFICULTY.SIMPLE"}}</option>
<option value="easy">{{localize "DIFFICULTY.EASY"}}</option>
<option value="average">{{localize "DIFFICULTY.AVERAGE"}}</option>
<option value="hard">{{localize "DIFFICULTY.HARD"}}</option>
<option value="daunting">{{localize "DIFFICULTY.DAUNTING"}}</option>
{{/select}}
</select>
</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}}" />
</div>
</div>
<div class="item">
<div class="description">
<h2>{{localize data.description.label}}</h2>
<div class="info">
<div class="description">
{{editor content=data.description.value target="data.description.value" button=true owner=owner editable=editable}}
</div>
</div>
</div>
</div>
</form>

View File

@ -0,0 +1,20 @@
<form class="{{cssClass}} item-sheet-talent" autocomplete="off">
<div class="header">
<div class="avatar flex-group-center" style="grid-row: 1/3;">
<img class="item-avatar-img" src="{{item.img}}" data-edit="img" title="{{item.name}}" />
</div>
<div class="container flex-group-center item-name">
<h1><input name="name" type="text" value="{{item.name}}" placeholder="{{localize "ITEM.TALENT"}}" /></h1>
</div>
</div>
<div class="item">
<div class="talent-description">
<h2>{{localize data.description.label}}</h2>
<div class="info-mid">
<div class="description">
{{editor content=data.description.value target="data.description.value" button=true owner=owner editable=editable}}
</div>
</div>
</div>
</div>
</form>

View File

@ -0,0 +1,63 @@
<form class="{{cssClass}}" autocomplete="off">
<div class="header">
<div class="avatar flex-group-center" style="grid-row: 1/3;">
<img class="item-avatar-img" src="{{item.img}}" data-edit="img" title="{{item.name}}" />
</div>
<div class="container flex-group-center item-name">
<h1><input name="name" type="text" value="{{item.name}}" placeholder="{{localize "ITEM.WEAPON"}}" /></h1>
</div>
</div>
<div class="grid-container" style="grid-template-columns: 1fr 1fr;">
<!--
<div class="supply">
<label>{{localize data.quantity.label}}</label>
<input name="data.quantity.value" type="number" value="{{data.quantity.value}}" />
</div>
-->
<div class="cost">
<label>{{localize data.price.label}}</label>
<input name="data.price.value" type="text" value="{{data.price.value}}" />
</div>
<div class="rating">
<label>{{localize data.damage.label}}</label>
<input name="data.damage.value" type="text" value="{{data.damage.value}}" />
</div>
<div>
<label>{{localize data.category.label}}</label>
<select class="item-weapon-cat-select" name="data.category.value">
{{#select data.category.value}}
<option value="melee">{{localize "WEAPON.MELEE"}}</option>
<option value="ranged">{{localize "WEAPON.RANGED"}}</option>
{{/select}}
</select>
</div>
<div>
<label>{{localize "ITEM.SKILL"}}</label>
<select class="item-weapon-skill-select" name="data.skill.value">
{{#select data.skill.value}}
{{#each this.khskills as |t|}}
<option value="{{t.name}}">{{t.name}}</option>
{{/each}}
{{/select}}
</select>
</div>
<!--
<div class="grid-container" style="grid-template-columns: 1fr auto;">
<label>{{localize data.equipable.label}}</label>
<a class="item-bool-click {{#if data.equipable.value}}active{{/if}}" data-name="equipable" title="equipable">
<i class="far {{#if data.equipable.value}}fa-times-circle{{else}}fa-circle{{/if}}"></i>
</a>
</div>
-->
</div>
<div class="item">
<div class="description">
<h2>{{localize data.description.label}}</h2>
<div class="info">
<div class="description">
{{editor content=data.description.value target="data.description.value" button=true owner=owner editable=editable}}
</div>
</div>
</div>
</div>
</form>

View File

@ -0,0 +1,32 @@
<div class="bio-tab border" style="height: 100%; border-top: none;">
<div class="bio-background grid-container" style="height: 100%; grid-column-gap: 5px; grid-template-rows: auto auto auto 1fr; grid-template-columns: auto 1fr auto 1fr;">
<h1>{{localize "BACKGROUND.TITLE"}}</h1>
<div class="name">{{localize data.bio.age.label}}:</div>
<input name="data.bio.age.value" type="text" value="{{data.bio.age.value}}" />
<div class="name">{{localize data.background.birthplace.label}}</div>
<input name="data.background.birthplace.value" type="text" value="{{data.background.birthplace.value}}" />
<div class="name">{{localize data.background.social.label}}</div>
<input name="data.background.social.value" type="text" value="{{data.background.social.value}}" />
<div></div>
<div></div>
<div class="name-top">{{localize data.background.events.label}}</div>
<div class="info-bio border-thin" style="/*grid-row: 4/6; grid-column: 2;*/">
<div class="description">
{{editor content=data.background.events.value target="data.background.events.value" button=true owner=owner editable=editable}}
</div>
</div>
<div class="name-top">{{localize data.background.contacts.label}}</div>
<div class="info-bio border-thin" style="/*grid-row: 4/6; grid-column: 4;*/">
<div class="description">
{{editor content=data.background.contacts.value target="data.background.contacts.value" button=true owner=owner editable=editable}}
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,76 @@
<div class="border grid-container" style="height: 100%; border-top: none; grid-template-columns: 1fr 1fr; grid-template-rows: auto 1fr;">
<div class="combat border">
<ul class="items">
<ul class="items-list">
<li class="item 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">
<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">
<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}}">
<i class="far {{#if data.combat.helmet}}fa-times-circle{{else}}fa-circle{{/if}}"></i>
</a>
</div>
</li>
<li class="item 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>
</ul>
</ul>
</div>
<div class="talents border" style="grid-row: 1/3; grid-column-start: 2;">
<h1 style="margin-bottom: 0; grid-column: 1/4;">{{localize "TAB.SKILLS"}}</h1>
<ul class="items">
<ul class="items-list">
{{#each actor.skills as |skill key|}}
<li class="item 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>
{{/each}}
</ul>
</ul>
</div>
<div class="attacks border">
<ul class="items">
<li class="items-header flexrow">
<div class="header-name">{{localize "ITEM.ATTACK"}}</div>
<div class="">{{localize "ITEM.SKILL"}}</div>
<div>{{localize "ITEM.DAMAGE"}}</div>
<div></div>
</li>
<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}}">
<div class="item-name">{{item.name}}</div>
<div class="skill-roll">
<div class="roll-button">
<div class="roll-adversary-attack rollable">{{item.data.skill.value}}</div>
</div>
</div>
<div class="skill-roll">
<div class="roll-button">
<div class="roll-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>
</div>
</li>
{{/each}}
</ul>
</ul>
</div>
</div>

View File

@ -0,0 +1,117 @@
<div class="gear-tab border grid-container" style="grid-template-rows: auto 1fr; height: 100%; border-top: none;">
<div class="currencies border">
<div class="header grid-container" style="grid-template-columns: auto 1fr auto 1fr;">
<h1 style="margin-bottom: 0; grid-column: 1/5;">{{localize "CURRENCY.TITLE"}}</h1>
<label style="margin: auto;">{{localize data.currency.shekel.label}}:</label>
<input class="center-text" name="data.currency.shekel.value" type="number" value="{{data.currency.shekel.value}}" />
<label style="margin: auto;">{{localize data.currency.quarter.label}}:</label>
<input class="center-text" name="data.currency.quarter.value" type="number" value="{{data.currency.quarter.value}}" />
</div>
</div>
<div class="gear border scroll-y">
{{!-- Weapons List --}}
<ul class="items">
<li class="items-header flexrow">
<div class="header-name">{{localize "ITEM.WEAPON"}}</div>
<div>{{localize "ITEM.EQUIPPED"}}</div>
<div class="">{{localize "ITEM.SKILL"}}</div>
<div>{{localize "ITEM.DAMAGE"}}</div>
<div></div>
</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}}">
<div class="item-name">{{item.name}}</div>
<div>
{{#if item.data.equipable.equipped}}
<a class="toggle-equipped active" data-item-id="{{item._id}}" title="Equipped"><i class="fas fa-fist-raised"></i></a>
{{else}}
<a class="toggle-equipped" data-item-id="{{item._id}}" title="Equipped"><i class="fas fa-fist-raised"></i></a>
{{/if}}
</div>
<div class="skill-roll">
<div class="roll-button">
<div class="roll-weapon-skill rollable">{{item.data.skill.value}}</div>
</div>
</div>
<div class="skill-roll">
<div class="roll-button">
<div class="roll-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>
</div>
</li>
{{/each}}
</ul>
</ul>
{{!-- Armour List --}}
<ul class="items">
<li class="items-header flexrow">
<div class="header-name">{{localize "ITEM.ARMOR"}}</div>
<div>{{localize "ITEM.EQUIPPED"}}</div>
<div>{{localize "ITEM.DEFENCE"}}</div>
<div></div>
</li>
<ul class="items-list">
{{#each actor.armor as |item id|}}
<li class="item flexrow" data-item-id="{{item._id}}">
<div class="item-name">{{item.name}}</div>
<div>
{{#if item.data.equipable.equipped}}
<a class="toggle-equipped active" data-item-id="{{item._id}}" title="Equipped"><i class="fas fa-shield-alt"></i></a>
{{else}}
<a class="toggle-equipped" data-item-id="{{item._id}}" title="Equipped"><i class="fas fa-shield-alt"></i></a>
{{/if}}
</div>
{{#if item.data.helmet.value}}
<div>
{{localize item.data.helmet.label}}
</div>
{{else}}
<div class="skill-roll">
<div class="roll-button">
<div class="roll-armor rollable">{{item.data.defence.value}}</div>
</div>
</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>
</div>
</li>
{{/each}}
</ul>
</ul>
{{!-- Gear List --}}
<ul class="items">
<li class="items-header flexrow">
<div class="header-name">{{localize "ITEM.GEAR"}}</div>
<div>{{localize "ITEM.QUANTITY"}}</div>
<div></div>
</li>
<ul class="items-list">
{{#each actor.gear as |item id|}}
<li class="item flexrow" data-item-id="{{item._id}}">
<div class="item-name">{{item.name}}</div>
<div class="item-quantity">
<div class="quantity increase"><i class="far fa-plus-square"></i></div>
{{item.data.quantity.value}}
<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>
</div>
</li>
{{/each}}
</ul>
</ul>
</div>
</div>

View File

@ -0,0 +1,66 @@
<div class="skills-tab border scroll-y grid-container" style="grid-template-columns: 1fr 1fr; border-top: none;">
<div class="skills border">
<h1 style="margin-bottom: 0; grid-column: 1/4;">{{localize "SKILL.BASE"}}</h1>
<ul class="items">
<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>
<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">
<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>
</div>
</li>
{{/if}}
{{/each}}
</ul>
</ul>
</div>
<div class="skills border" style="grid-column: 2; grid-row: 1/5;">
<h1 style="margin-bottom: 0; grid-column: 1/4;">{{localize "SKILL.ADVENTURE"}}</h1>
<ul class="items">
<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>
<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">
<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>
</div>
</li>
{{/if}}
{{/each}}
</ul>
</ul>
</div>
<div class="skills border">
<h1 style="margin-bottom: 0; grid-column: 1/4;">{{localize "SKILL.COMBAT"}}</h1>
<ul class="items">
<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>
<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">
<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>
</div>
</li>
{{/if}}
{{/each}}
</ul>
</ul>
</div>
<div class="skills border grid-container" style="grid-column-gap: 5px; grid-template-columns: 1fr;">
<h1 style="grid-column: unset;">{{localize data.languages.label}}</h1>
<input name="data.languages.value" type="text" value="{{data.languages.value}}" />
</div>
</div>

View File

@ -0,0 +1,42 @@
<div class="talent-tab border grid-container" style="grid-template-columns: 1fr 1fr; border-top: none;">
<div class="talents border">
<h1 class="item-list-header">{{localize "ITEM.TALENT"}}</h1>
<div class="scroll-y" style="height: calc(100% - 20px);">
<ul class="items">
<ul class="items-list">
{{#each actor.talents as |talent key|}}
<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>
</div>
</li>
{{/each}}
</ul>
</ul>
</div>
</div>
<div class="spells border">
<h1 class="item-list-header">{{localize "ITEM.SPELL"}}</h1>
<div class="scroll-y" style="height: calc(100% - 20px);">
<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>
<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>
</div>
</li>
{{/each}}
</ul>
</ul>
</div>
</div>
</div>

View File

@ -0,0 +1,22 @@
<header class="modifications-header grid-container" style="grid-template-columns: 2fr 1fr auto;">
<span class="modification-modtype">{{localize "MOD.TYPE"}}</span>
<span class="modification-mod">{{localize "MOD.VALUE"}}</span>
<a class="modification-control" data-action="create"><i class="fas fa-plus"></i></a>
</header>
<ol class="modifications-list" style="padding: 0;">
{{#each data.modifications as |attr key|}}
<li class="modification grid-container" style="grid-template-columns: 2fr 1fr auto;" data-attribute="{{key}}">
<input class="modification-key" type="text" name="data.modifications.{{key}}.key" value="{{key}}" style="display: none;" />
<select class="modification-modtype" name="data.modifications.{{key}}.modtype">
{{../this.KH.armor_mod_types}}
{{#select attr.modtype}}
{{#if (eq ../item.type "armor")}} {{#each ../this.KH.armor_mod_types as |t|}}
<option value="{{t.value}}">{{localize t.label}}</option>
{{/each}} {{/if}}
{{/select}}
</select>
<input class="modification-value" type="text" name="data.modifications.{{key}}.value" value="{{attr.value}}" data-dtype="Number" placeholder="0" />
<a class="modification-control" data-action="delete"><i class="fas fa-trash"></i></a>
</li>
{{/each}}
</ol>