Browse Source

first commit

pull/1/head
Erebus 1 year ago
commit
c3a7388592
38 changed files with 3189 additions and 0 deletions
  1. +6
    -0
      .gitignore
  2. +0
    -0
     
  3. +87
    -0
      lang/en.json
  4. +87
    -0
      lang/sv.json
  5. +501
    -0
      module/actors/actor-sheet.js
  6. +18
    -0
      module/actors/actor.js
  7. +124
    -0
      module/helpers/dice-helper.js
  8. +35
    -0
      module/helpers/item-helpers.js
  9. +157
    -0
      module/items/item-sheet.js
  10. +62
    -0
      module/items/item.js
  11. +26
    -0
      module/kh-config.js
  12. +96
    -0
      module/kh-dice.js
  13. +23
    -0
      module/kh-hooks.js
  14. +131
    -0
      module/kh-main.js
  15. +35
    -0
      packs/skills.db
  16. +191
    -0
      styles/actors.css
  17. +140
    -0
      styles/common.css
  18. +65
    -0
      styles/dice-roller.css
  19. +113
    -0
      styles/items.css
  20. +51
    -0
      system.json
  21. +307
    -0
      template.json
  22. +51
    -0
      templates/actors/adversary-sheet.html
  23. +95
    -0
      templates/actors/character-sheet.html
  24. +105
    -0
      templates/chat/item-card.html
  25. +26
    -0
      templates/dice/roll.html
  26. +39
    -0
      templates/items/adversaryAttack-sheet.html
  27. +72
    -0
      templates/items/armor-sheet.html
  28. +30
    -0
      templates/items/gear-sheet.html
  29. +36
    -0
      templates/items/skill-sheet.html
  30. +42
    -0
      templates/items/spell-sheet.html
  31. +20
    -0
      templates/items/talent-sheet.html
  32. +63
    -0
      templates/items/weapon-sheet.html
  33. +32
    -0
      templates/parts/actor/bio.html
  34. +76
    -0
      templates/parts/actor/combat.html
  35. +117
    -0
      templates/parts/actor/gear.html
  36. +66
    -0
      templates/parts/actor/skills.html
  37. +42
    -0
      templates/parts/actor/talent.html
  38. +22
    -0
      templates/parts/shared/modifications.html

+ 6
- 0
.gitignore View File

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

+ 0
- 0
View File


+ 87
- 0
lang/en.json 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
- 0
lang/sv.json 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"
}

+ 501
- 0
module/actors/actor-sheet.js 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
- 0
module/actors/actor.js 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")
}
}

+ 124
- 0
module/helpers/dice-helper.js 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,
},
});
}
}

+ 35
- 0
module/helpers/item-helpers.js 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
- 0
module/items/item-sheet.js 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
- 0
module/items/item.js 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
- 0
module/kh-config.js 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
- 0
module/kh-dice.js 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
- 0
module/kh-hooks.js 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
- 0
module/kh-main.js 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
- 0
packs/skills.db 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
- 0
styles/actors.css 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;
}