18 Commits

Author SHA1 Message Date
aad90144fe Better import and various stuff 2024-08-10 09:48:43 +02:00
2b1da44f3a Gestion de l'import skill/categories 2024-08-10 00:26:22 +02:00
420f912c8c Gestion de l'import skill/categories 2024-08-10 00:26:05 +02:00
6cfedca162 Gestion de l'import skill/categories 2024-08-10 00:24:15 +02:00
de884cbd22 Gestion de l'import skill/categories 2024-08-10 00:15:44 +02:00
8b37e16915 Gestion de l'import skill/categories 2024-08-09 23:58:38 +02:00
46d991fb65 Fix compendiums 2024-08-09 08:47:23 +02:00
5ca0362773 Fix compendiums 2024-08-09 08:44:14 +02:00
f777131374 Fix compendiums 2024-08-08 23:16:50 +02:00
c11f7d23a8 Add game system for skills 2024-08-08 05:04:53 +02:00
98e6b70b1b Add game system for skills 2024-08-07 23:08:58 +02:00
6597334682 Add game system for skills 2024-08-07 23:08:46 +02:00
534fcb04fb Manage game systems 2024-08-07 17:27:08 +02:00
8be95c20bd Add new rank option 2024-08-04 20:44:34 +02:00
cba4394a37 Rework skill/skill categ links 2024-08-03 16:41:04 +02:00
1f2558b680 Skills below Skills categories + skills rollable 2024-08-02 17:07:02 +02:00
e25c51a570 Rework roll window 2024-07-29 09:28:42 +02:00
3ba8fdd641 Actualiser README.md 2024-07-27 18:20:30 +02:00
57 changed files with 945 additions and 531 deletions

View File

@ -1,16 +1,15 @@
# Rolemaster Standard System for Foundry VTT # Rolemaster Standard System for Foundry VTT
## Introduction ## Introduction
This is an in-development implementation of the Rolemaster Standard System for FoundryVTT. All rights to the system remain property of Iron Crown Enterprises (http://ironcrown.com/). This system will not include any of the book content and aims to provide only what is on the character sheet.
If you need access to the Rolemaster Standard System you may be out of luck as the system has been out of print for some time. However it's successor Rolemaster Fantasy Roleplaying is broadly compatible and can be purchased as a PDF via Drive Thru RPG here: https://www.drivethrurpg.com/product/91995/Rolemaster-Fantasy-Role-Playing?cPath=1361_6495 This repo is an independant fork from https://github.com/Cynicide/RMSS-FoundryVTT from Cynicide, in order to implement a fully playable Rolemaster FRP system in FoundryVTT.
All rights to the system remain property of Iron Crown Enterprises (http://ironcrown.com/). This system will not include any of the book content and aims to provide only what is on the character sheet.
## How to Use ## How to Use
Download the latest release from this repository and then extract the rmss folder. Drag it into the systems folder of your FoundryVTT Data Directory. More detailed instructions can be found [here](docs/INSTALL.md) in the docs directory. Download the latest release from this repository and then extract the rmss folder. Drag it into the systems folder of your FoundryVTT Data Directory. More detailed instructions can be found [here](docs/INSTALL.md) in the docs directory (to be updated).
If you are using this system I highly recommend the "Simple Dice Roller" module which will allow you to roll dice easily while this feature is in development. You can install it in the Foundry Modules section or you can visit the following link for more instructions: https://foundryvtt.com/packages/simple-dice-roller/ Further information on how the system works can be found [here](docs/USAGE.md) (to be updated)
Further information on how the system works can be found [here](docs/USAGE.md)
## Goals/ToDo ## Goals/ToDo
Project goals include: Project goals include:
@ -20,13 +19,11 @@ Project goals include:
I maintain a public Trello board here where I log my work items: https://trello.com/b/JkXd5EGx/rmss-for-foundryvtt-features I maintain a public Trello board here where I log my work items: https://trello.com/b/JkXd5EGx/rmss-for-foundryvtt-features
## About Me
My name is Anthony, I'm a DevOps Engineer who has been playing Roleplaying Games since the mid 80's. I've been involved in numerous small programming projects. This system is by far the largest thing I've done. If the code here is awful it's because I'm learning CSS/Javascript as I go.
## Credits ## Credits
This system is based on: This system is based on:
- The Foundry VTT Boilerplate system by Asacolips. (https://gitlab.com/asacolips-projects/foundry-mods/boilerplate) - The Foundry VTT Boilerplate system by Asacolips. (https://gitlab.com/asacolips-projects/foundry-mods/boilerplate)
- The video tutorials by Cédric Hauteville you can watch them here: https://www.youtube.com/playlist?list=PLFV9z59nkHDccUbRXVt623UdloPTclIrz - The video tutorials by Cédric Hauteville you can watch them here: https://www.youtube.com/playlist?list=PLFV9z59nkHDccUbRXVt623UdloPTclIrz
- The Rolemaster FRP is derivated from the RMSS system from Cynicide, available here https://github.com/Cynicide/RMSS-FoundryVTT
Icons used in this system have been sourced from https://game-icons.net Icons used in this system have been sourced from https://game-icons.net

View File

@ -1,4 +1,5 @@
{ {
"rmss": { "rmss": {
"player_character": { "player_character": {
"name": "Name:", "name": "Name:",
@ -43,7 +44,8 @@
"equipment": "Equipment", "equipment": "Equipment",
"spells": "Spells", "spells": "Spells",
"status_record": "Status Record", "status_record": "Status Record",
"experience_points": "Experience Points" "experience_points": "Experience Points",
"background": "Background"
}, },
"pc_sheet_fixed_info": { "pc_sheet_fixed_info": {
"race": "Race:", "race": "Race:",

View File

@ -46,4 +46,8 @@
font-weight: bold; font-weight: bold;
border-bottom: 1px solid; border-bottom: 1px solid;
background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0); background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0);
}
.skill-box {
margin-left: 1.2rem;
background-color: lightgrey;
} }

View File

@ -12,7 +12,7 @@
padding-bottom: 3px; padding-bottom: 3px;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
flex-direction: column; //flex-direction: column;
justify-content: center; justify-content: center;
} }
@ -36,7 +36,7 @@
padding-bottom: 3px; padding-bottom: 3px;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
flex-direction: column; //flex-direction: column;
justify-content: center; justify-content: center;
background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0); background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0);
} }
@ -45,4 +45,9 @@
font-weight: bold; font-weight: bold;
border-bottom: 1px solid; border-bottom: 1px solid;
background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0); background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0);
}
.skills-name-left-align {
text-align: left;
padding-left: 2px;
} }

View File

@ -27,4 +27,7 @@
font-family: Signika, sans-serif; font-family: Signika, sans-serif;
font-size: 12px; font-size: 12px;
text-align: center; text-align: center;
}
.select-fixed-width {
width: 100px;
} }

View File

@ -15,6 +15,11 @@
.roll-button-container { .roll-button-container {
display: flex; display: flex;
flex-direction: column; flex-direction: row;
justify-content: flex-end; justify-content: flex-start;
}
.roll-button {
margin-top: 2px;
width: 96px;
max-width: 96px;
} }

View File

@ -1,6 +1,6 @@
export const rmss = {}; export const rmss = {};
rmss.curreny_type = { rmss.currency_type = {
mp: "rmss.curreny_type.mp", mp: "rmss.curreny_type.mp",
pp: "rmss.curreny_type.pp", pp: "rmss.curreny_type.pp",
gp: "rmss.curreny_type.gp", gp: "rmss.curreny_type.gp",
@ -9,6 +9,66 @@ rmss.curreny_type = {
cp: "rmss.curreny_type.cp" cp: "rmss.curreny_type.cp"
}; };
rmss.difficulties = {
routine: {key: "routine", label:"Routine (+30)", value: 30},
easy: {key: "easy", label:"Easy (+20)", value: 20},
light: {key: "light", label:"Light (+10)", value: 10},
medium: {key: "medium", label:"Medium (0)", value: 0},
hard: {key: "hard", label:"Hard (-10)", value: -10},
very_hard: {key: "very_hard", label:"Very Hard (-20)", value: -20},
extremely_hard: {key: "extremely_hard", label:"Extremely Hard (-30)", value: -30},
sheer_folly: {key: "sheer_folly", label:"Sheer Folly (-50)", value: -50},
absurd: {key: "absurd", label:"Absurd (-70)", value: -70},
};
rmss.gameSystems = [
{key: "common", label:"Common"},
{key: "merp", label:"Middle Earth Role Playing (MERP)"},
{key: "rmfrp", label:"Rolemaster Fantasy Role Playing (RMFRP)"},
];
rmss.combatSituations = [
{key: "none", label:"None (0)", modifier: 0},
{key: "melee", label:"Melee environment (-20)", modifier: -20},
{key: "missile", label:"Under missile fire (-10)", modifier: -10},
];
rmss.rankBonusProgressionList = [
{key: "standard", label:"Standard"},
{key: "limited", label:"Limited"},
{key: "combined", label:"Combined"},
{key: "race", label:"Linked to Race"},
]
rmss.lightOrDarknessModifiers = [
{key: "none", label:"None", modifierLight: 0, modifierDark: 0},
{key: "noshadows", label:"No shadows", modifierLight: 10, modifierDark: -30},
{key: "lightshadows", label:"Light shadows", modifierLight: 5, modifierDark: -20},
{key: "mediumshadows", label:"Medium shadows", modifierLight: 0, modifierDark: 0},
{key: "heavyshadows", label:"Heavy shadows", modifierLight: -10, modifierDark: 10},
{key: "dark", label:"Darkness", modifierLight: -25, modifierDark: 30},
{key: "pitchblack", label:"Pitch black", modifierLight: -40, modifierDark: 40},
]
rmss.hitsPerRound = [
{key: "none", label:"None", modifier: 0},
{key: "one", label:"One", modifier: -5},
{key: "two", label:"Two", modifier: -10},
{key: "three", label:"Three", modifier: -20},
{key: "four", label:"Four", modifier: -25},
{key: "five", label:"Five", modifier: -30},
{key: "six", label:"Six", modifier: -35},
{key: "seven", label:"Seven", modifier: -40},
{key: "eight", label:"Eight", modifier: -45},
{key: "nine", label:"Nine", modifier: -50},
{key: "ten", label:"Ten", modifier: -55},
{key: "eleven", label:"Eleven", modifier: -60},
{key: "twelve", label:"Twelve", modifier: -65},
{key: "thirteen", label:"Thirteen", modifier: -70},
{key: "fourteen", label:"Fourteen", modifier: -75},
{key: "fifteen", label:"Fifteen", modifier: -80}
];
rmss.stats = { rmss.stats = {
agility: { agility: {
fullname: "Agility", fullname: "Agility",

View File

@ -27,6 +27,12 @@ export class RMSSActor extends Actor {
_prepareCharacterData(actorData) { _prepareCharacterData(actorData) {
if (actorData.type !== "character") return; if (actorData.type !== "character") return;
console.log("*****Flag", this.getFlag("world", "importing"));
if (this.getFlag("world", "importing")) {
return; // Don't calculate skill bonuses if we are importing
}
this.calculateBasicStatBonus(actorData); this.calculateBasicStatBonus(actorData);
// Calculate Stat Bonuses for the Actor // Calculate Stat Bonuses for the Actor
@ -40,6 +46,31 @@ export class RMSSActor extends Actor {
// Iterate through and apply Skill Category Bonuses for Skill items // Iterate through and apply Skill Category Bonuses for Skill items
this.calculateSkillBonuses(); this.calculateSkillBonuses();
this.computeWoundsMalus();
}
getStunnedModifier() {
if (this.system.state.stunned) {
return Math.min(-50 + (3*this.system.stats.self_discipline.stat_bonus), 0)
} else {
return 0;
}
}
computeWoundsMalus() {
// Compute % of wounds
let percent = 100 - (this.system.attributes.hits.current*100/this.system.attributes.hits.max);
let modifier = 0;
if (percent > 25 && percent < 50) {
modifier = -10;
} else if (percent >= 51 && percent < 75) {
modifier = -20;
} else if (percent >= 76) {
modifier = -30;
}
this.system.modifiers.woundsModifier = modifier;
console.log(`rmss | actor.js | Wounds Malus: ${this.system.modifiers.woundsModifier} ${percent}`);
} }
/** /**
@ -208,9 +239,7 @@ export class RMSSActor extends Actor {
for (const item of this.items) { for (const item of this.items) {
if (item.type === "skill") { if (item.type === "skill") {
console.log(`rmss | actor.js | Calculating skill bonus for Skill: ${item.name}`); console.log(`rmss | actor.js | Calculating skill bonus for Skill: ${item.name}`);
console.log(`rmss | actor.js | Updating Skill Category Bonus for Skill: ${item.name}`);
item.calculateSelectedSkillCategoryBonus(item); item.calculateSelectedSkillCategoryBonus(item);
console.log(`rmss | actor.js | Updating Skill Total Bonus for Skill: ${item.name}`);
item.calculateSkillTotalBonus(item); item.calculateSkillTotalBonus(item);
} }
} }
@ -233,51 +262,35 @@ export class RMSSActor extends Actor {
} }
else else
{ {
let applicable_stat_bonus = 0; let applicable_stat_bonuses = [];
let app_stat_1_found = false;
let app_stat_2_found = false;
let app_stat_3_found = false;
// Iterate through the applicable stats and find their full names // Iterate through the applicable stats and find their full names
for (const stat in CONFIG.rmss.stats) { for (const stat in CONFIG.rmss.stats) {
// If the configured App Stat matches the one of the stats in config // If the configured App Stat matches the one of the stats in config
if (app_stat_1 === CONFIG.rmss.stats[stat].shortname) { if (app_stat_1 === CONFIG.rmss.stats[stat].shortname) {
app_stat_1_found = true; // Add the Stat Bonus to the array
// Get the Stat Bonus applicable_stat_bonuses.push(this.system.stats[stat].stat_bonus);
applicable_stat_bonus = applicable_stat_bonus + this.system.stats[stat].stat_bonus;
} }
if (app_stat_2 === CONFIG.rmss.stats[stat].shortname) { if (app_stat_2 === CONFIG.rmss.stats[stat].shortname) {
app_stat_2_found = true; // Add the Stat Bonus to the array
applicable_stat_bonus = applicable_stat_bonus + this.system.stats[stat].stat_bonus; applicable_stat_bonuses.push(this.system.stats[stat].stat_bonus);
} }
if (app_stat_3 === CONFIG.rmss.stats[stat].shortname) { if (app_stat_3 === CONFIG.rmss.stats[stat].shortname) {
app_stat_3_found = true; // Add the Stat Bonus to the array
applicable_stat_bonus = applicable_stat_bonus + this.system.stats[stat].stat_bonus; applicable_stat_bonuses.push(this.system.stats[stat].stat_bonus);
} }
} }
// Compute the total bonus for the applicable stats
if (app_stat_1_found === true && app_stat_2_found === true && app_stat_3_found === true) { let applicable_stat_bonus = 0;
// Apply the update if we found stat bonuses for every applicable stat for (const bonus of applicable_stat_bonuses) {
item.system.stat_bonus = applicable_stat_bonus; applicable_stat_bonus += bonus;
// Update the total in the Item
item.calculateSkillCategoryTotalBonus(item);
} }
else if (app_stat_1_found === true && app_stat_2_found === true && app_stat_3_found === false) { // Apply the update if we found stat bonuses for every applicable stat
// Apply the update if we found stat bonuses for the first two applicable stats if ( item.system.stat_bonus != applicable_stat_bonus ) {
item.system.stat_bonus = applicable_stat_bonus; item.system.stat_bonus = applicable_stat_bonus;
// Update the total in the Item
item.calculateSkillCategoryTotalBonus(item);
}
else if (app_stat_1_found === true && app_stat_2_found === false && app_stat_3_found === false) {
// Apply the update if we found stat bonuses for the first applicable stat
item.system.stat_bonus = applicable_stat_bonus;
// Update the total in the Item
item.calculateSkillCategoryTotalBonus(item);
} }
// Update the total in the Item
item.calculateSkillCategoryTotalBonus(item);
} }
} }
} }

View File

@ -1,3 +1,5 @@
import { RFRPUtility } from "../rfrp-utility.js";
export class RMSSItem extends Item { export class RMSSItem extends Item {
/** @override */ /** @override */
@ -44,10 +46,11 @@ export class RMSSItem extends Item {
prepareDerivedData() { prepareDerivedData() {
const itemData = this; const itemData = this;
const systemData = itemData.system; const systemData = itemData.system;
const flags = itemData.flags.rmss || {};
if (this.parent?.getFlag("world", "importing")) {
return; // Don't calculate skill bonuses if we are importing
}
// Make separate methods for each item type to keep things organized. // Make separate methods for each item type to keep things organized.
if (itemData.type === "skill") { if (itemData.type === "skill") {
this._prepareSkillCategoryData(itemData); this._prepareSkillCategoryData(itemData);
} }
@ -66,6 +69,7 @@ export class RMSSItem extends Item {
_prepareSkillData(itemData) { _prepareSkillData(itemData) {
if (itemData.type !== "skill") return; if (itemData.type !== "skill") return;
console.log(`rmss | item.js | Preparing Skill Data for: ${itemData.name}`); console.log(`rmss | item.js | Preparing Skill Data for: ${itemData.name}`);
// Make modifications to data here. For example: // Make modifications to data here. For example:
// const systemData = itemData.system; // const systemData = itemData.system;
@ -100,21 +104,19 @@ export class RMSSItem extends Item {
} }
calculateSelectedSkillCategoryBonus(itemData) { calculateSelectedSkillCategoryBonus(itemData) {
if (this.isEmbedded === null) { // Find the relevant skill category
console.log(`rmss | item.js | Skill ${this.name} has no owner. Not calculating Skill Category bonus`); if (!this.parent) { return; } // Only if attached to an actor
}
else let skillC = this.parent?.items || RFRPUtility.getSkillCategories();
{ if (skillC) {
const items = this.parent?.items; let item = skillC.find(it => it.type == "skill_category" && it.name.toLowerCase() == itemData.system.category.toLowerCase());
console.log(`rmss | item.js | Skill ${this.name} has owner, calculating skill category bonus.`); if (item) {
if (items) { this.system.category_bonus = item.system.total_bonus;
for (const item of items) { } else {
if (item.type === "skill_category" && item._id === itemData.system.category) { ui.notifications.warn(`Skill Category ${itemData.system.category} not found for Skill ${itemData.name}`);
console.log(`rmss | item.js | Calculating Skill Category bonus for skill: ${this.name}`);
this.system.category_bonus = item.system.total_bonus;
}
}
} }
} else {
ui.notifications.info("No Skill Categories found. Please create a Skill Category.");
} }
} }
} }

201
module/rfrp-utility.js Normal file
View File

@ -0,0 +1,201 @@
/* -------------------------------------------- */
export class RFRPUtility {
/* -------------------------------------------- */
static async init() {
}
/* -------------------------------------------- */
static async ready() {
this.registerSettings();
this.gameSystem = game.settings.get("fvtt-rolemaster-frp", "game_system");
const skillCategories = await RFRPUtility.loadCompendium("fvtt-rolemaster-frp.skill_categories")
this.skillCategories = skillCategories.map(i => i.toObject()).filter( i => i.system.game_system == "common" || i.system.game_system == this.gameSystem);
// Sort skill categories by name
this.skillCategories.sort((a, b) => a.name.localeCompare(b.name));
}
static getGameSystem() {
return this.gameSystem;
}
/* -------------------------------------------- */
static getSkillCategories() {
return this.skillCategories
}
/* -------------------------------------------- */
static registerSettings() {
game.settings.register("fvtt-rolemaster-frp", "game_system", {
name: "Game System",
hint: "List of Game Systems",
scope: "world",
default: "rmfrp",
requiresReload: true,
type: String,
config: true,
choices: {
rmfrp: "Rolemaster Fantasy Role Playing (RMFRP)",
merp: "Middle Earth Role Playing (MERP)"
}
});
}
/* -------------------------------------------- */
static async loadCompendiumData(compendium) {
const pack = game.packs.get(compendium);
return await pack?.getDocuments() ?? [];
}
/* -------------------------------------------- */
static async loadCompendium(compendium, filter = item => true) {
let compendiumData = await RFRPUtility.loadCompendiumData(compendium);
return compendiumData.filter(filter);
}
/* -------------------------------------------- */
static removeChatMessageId(messageId) {
if (messageId) {
game.messages.get(messageId)?.delete();
}
}
static findChatMessageId(current) {
return RFRPUtility.getChatMessageId(HeritiersUtility.findChatMessage(current));
}
static getChatMessageId(node) {
return node?.attributes.getNamedItem('data-message-id')?.value;
}
static findChatMessage(current) {
return RFRPUtility.findNodeMatching(current, it => it.classList.contains('chat-message') && it.attributes.getNamedItem('data-message-id'))
}
static findNodeMatching(current, predicate) {
if (current) {
if (predicate(current)) {
return current;
}
return RFRPUtility.findNodeMatching(current.parentElement, predicate);
}
return undefined;
}
/* -------------------------------------------- */
static getUsers(filter) {
return game.users.filter(filter).map(user => user._id);
}
/* -------------------------------------------- */
static getWhisperRecipients(rollMode, name) {
switch (rollMode) {
case "blindroll": return this.getUsers(user => user.isGM);
case "gmroll": return this.getWhisperRecipientsAndGMs(name);
case "selfroll": return [game.user.id];
}
return undefined;
}
/* -------------------------------------------- */
static getWhisperRecipientsAndGMs(name) {
let recep1 = ChatMessage.getWhisperRecipients(name) || [];
return recep1.concat(ChatMessage.getWhisperRecipients('GM'));
}
/* -------------------------------------------- */
static blindMessageToGM(chatOptions) {
let chatGM = foundry.utils.duplicate(chatOptions);
chatGM.whisper = this.getUsers(user => user.isGM);
chatGM.content = "Blinde message of " + game.user.name + "<br>" + chatOptions.content;
console.log("blindMessageToGM", chatGM);
game.socket.emit("system.fvtt-rolemaster-frp", { msg: "msg_gm_chat_message", data: chatGM });
}
/* -------------------------------------------- */
static async searchItem(dataItem) {
let item
if (dataItem.pack) {
let id = dataItem.id || dataItem._id
let items = await this.loadCompendium(dataItem.pack, item => item.id == id)
item = items[0] || undefined
} else {
item = game.items.get(dataItem.id)
}
return item
}
/* -------------------------------------------- */
static loadHandlebarsTemplates() {
const templatePaths = [
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-stats.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-fixed-info.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-armor-info.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-resistance.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-race-stat-fixed-info.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-role-traits.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-background-info.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-skill-categories.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-skills.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-fav-skills.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-items.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-weapons.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-money.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-skill-categories.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-skills.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-armor.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-herbs.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-spells.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-fav-spells.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-fav-items.html",
"systems/fvtt-rolemaster-frp/templates/sheets/apps/app_skill_category_importer.html"
];
return loadTemplates(templatePaths);
}
/* -------------------------------------------- */
static loadHandlebarsHelpers() {
// Handlebars Helpers
Handlebars.registerHelper('count', function (list) {
return list.length;
})
Handlebars.registerHelper('includes', function (array, val) {
return array.includes(val);
})
Handlebars.registerHelper('upper', function (text) {
return text.toUpperCase();
})
Handlebars.registerHelper('lower', function (text) {
return text.toLowerCase()
})
Handlebars.registerHelper('upperFirst', function (text) {
if (typeof text !== 'string') return text
return text.charAt(0).toUpperCase() + text.slice(1)
})
Handlebars.registerHelper('notEmpty', function (list) {
return list.length > 0;
})
Handlebars.registerHelper('mul', function (a, b) {
return parseInt(a) * parseInt(b);
})
Handlebars.registerHelper("switch", function (value, options) {
this.switch_value = value;
return options.fn(this);
});
Handlebars.registerHelper("case", function (value, options) {
if (value === this.switch_value) {
return options.fn(this);
}
});
// Handle v12 removal of this helper
Handlebars.registerHelper('select', function (selected, options) {
const escapedValue = RegExp.escape(Handlebars.escapeExpression(selected));
const rgx = new RegExp(' value=[\"\']' + escapedValue + '[\"\']');
const html = options.fn(this);
return html.replace(rgx, "$& selected");
});
}
}

View File

@ -90,11 +90,11 @@ export default class RMSSPlayerSheet extends ActorSheet {
context.system.attributes.power_points.modifier = "PP Exhaustion Penalty: -20 "; context.system.attributes.power_points.modifier = "PP Exhaustion Penalty: -20 ";
break; break;
case (powerpointPercentage < 75): case (powerpointPercentage < 75):
console.log("Less than 75"); //console.log("Less than 75");
context.system.attributes.power_points.modifier = "PP Exhaustion Penalty: -10 "; context.system.attributes.power_points.modifier = "PP Exhaustion Penalty: -10 ";
break; break;
default: default:
console.log("Setting Default"); //console.log("Setting Default");
context.system.attributes.power_points.modifier = "PP Exhaustion Penalty: 0 "; context.system.attributes.power_points.modifier = "PP Exhaustion Penalty: 0 ";
} }
@ -117,11 +117,11 @@ export default class RMSSPlayerSheet extends ActorSheet {
context.system.attributes.exhaustion_points.modifier = "Exhaustion Penalty: -15 "; context.system.attributes.exhaustion_points.modifier = "Exhaustion Penalty: -15 ";
break; break;
case (exhaustionPercentage < 75): case (exhaustionPercentage < 75):
console.log("Less than 75"); //console.log("Less than 75");
context.system.attributes.exhaustion_points.modifier = "Exhaustion Penalty: -5 "; context.system.attributes.exhaustion_points.modifier = "Exhaustion Penalty: -5 ";
break; break;
default: default:
console.log("Setting Default"); //console.log("Setting Default");
context.system.attributes.exhaustion_points.modifier = "Exhaustion Penalty: 0 "; context.system.attributes.exhaustion_points.modifier = "Exhaustion Penalty: 0 ";
} }
@ -167,28 +167,25 @@ export default class RMSSPlayerSheet extends ActorSheet {
spells.push(i); spells.push(i);
} }
} }
// Parse skill categories and re+levant skills
for (let s of skillcat) {
s.skills = [];
for (let sk of playerskill) {
if (sk.system.category.toLowerCase() === s.name.toLowerCase()) {
s.skills.push(sk);
}
}
// Sort skills with localcompare
s.skills.sort((a, b) => a.name.localeCompare(b.name));
}
// Sort all items
// Sort Skill/Skillcat Arrays skillcat.sort((a, b) => a.name.localeCompare(b.name));
skillcat.sort(function(a, b) { gear.sort((a, b) => a.name.localeCompare(b.name));
if (a.name < b.name) { weapons.sort((a, b) => a.name.localeCompare(b.name));
return -1; armor.sort((a, b) => a.name.localeCompare(b.name));
} herbs.sort((a, b) => a.name.localeCompare(b.name));
if (a.name > b.name) { spells.sort((a, b) => a.name.localeCompare(b.name));
return 1;
}
return 0;
});
playerskill.sort(function(a, b) {
if (a.name < b.name) {
return -1;
}
if (a.name > b.name) {
return 1;
}
return 0;
});
// Assign and return // Assign and return
context.gear = gear; context.gear = gear;
@ -198,6 +195,10 @@ export default class RMSSPlayerSheet extends ActorSheet {
context.armor = armor; context.armor = armor;
context.herbs = herbs; context.herbs = herbs;
context.spells = spells; context.spells = spells;
// Dump context to console
console.log("ACTOR CONTEXT", context);
} }
async renderCharacterSettings(data) { async renderCharacterSettings(data) {
@ -217,7 +218,7 @@ export default class RMSSPlayerSheet extends ActorSheet {
html.find(".item-roll").click(ev => { html.find(".item-roll").click(ev => {
const item = this.actor.items.get(ev.currentTarget.getAttribute("data-item-id")); const item = this.actor.items.get(ev.currentTarget.getAttribute("data-item-id"));
new game.rmss.applications.RMSSToolsDiceRoller(item.name, item.system.total_bonus).render(true); new game.rmss.applications.RMSSToolsDiceRoller(item, this.actor).render(true);
}); });
// ------------------------------------------------------------- // -------------------------------------------------------------
@ -239,7 +240,9 @@ export default class RMSSPlayerSheet extends ActorSheet {
let selectOptions = {}; let selectOptions = {};
for (const pack of game.packs) { for (const pack of game.packs) {
selectOptions[pack.metadata.id] = pack.metadata.label; if (pack.metadata.type === "Item") {
selectOptions[pack.metadata.id] = pack.metadata.label;
}
} }
new game.rmss.applications.RMSSToolsSCImporter(selectOptions, this.actor).render(true); new game.rmss.applications.RMSSToolsSCImporter(selectOptions, this.actor).render(true);
}); });

View File

@ -1,27 +1,17 @@
import {
roll_one_to_onehundred,
roll_low_open_ended,
roll_high_open_ended,
roll_open_ended
} from "./rmss_dice_roller_rolls.js";
import {
getOpenEndedRollModifier,
processOpenEndedSixtySixRoll,
processOpenEndedRoll,
processHighOpenEndedRoll,
processLowOpenEndedRoll
} from "./rmss_dice_roller_processing.js";
export default class RMSSToolsDiceRoller extends FormApplication { export default class RMSSToolsDiceRoller extends FormApplication {
constructor(itemName, characterBonus) { constructor(item, actor) {
super(); super();
this.itemName = itemName; this.item = foundry.utils.duplicate(item);
this.characterBonus = characterBonus; this.actor = actor;
this.itemName = item.name;
this.characterBonus = Number(item.system.total_bonus);
this.stunnedModifier = actor.getStunnedModifier()
this.rollType = [ this.rollType = [
{value: "one_to_onehundred", text: "1-100", selected: false}, { value: "one_to_onehundred", text: "1-100", selected: false },
{value: "open_ended", text: "Open-Ended", selected: true}, { value: "open_ended", text: "Open-Ended", selected: true },
{value: "high_open_ended", text: "High Open-Ended", selected: false}, { value: "high_open_ended", text: "High Open-Ended", selected: false },
{value: "low_open_ended", text: "Low Open-Ended", selected: false} { value: "low_open_ended", text: "Low Open-Ended", selected: false }
]; ];
} }
@ -30,8 +20,8 @@ export default class RMSSToolsDiceRoller extends FormApplication {
classes: ["form"], classes: ["form"],
title: "Rolemaster Dice Roller", title: "Rolemaster Dice Roller",
popOut: true, popOut: true,
width: 380, width: 480,
height: 210, height: 440,
template: "systems/fvtt-rolemaster-frp/templates/sheets/apps/app_dice_roller.html" template: "systems/fvtt-rolemaster-frp/templates/sheets/apps/app_dice_roller.html"
}); });
} }
@ -42,6 +32,15 @@ export default class RMSSToolsDiceRoller extends FormApplication {
itemName: this.itemName, itemName: this.itemName,
characterBonus: this.characterBonus, characterBonus: this.characterBonus,
selectOptions: this.rollType, selectOptions: this.rollType,
woundsModifier: this.actor.system.modifiers.woundsModifier,
config: CONFIG.rmss,
difficulty: 0,
combatSituation: 0,
lightningModifier: 0,
darknessModifier: 0,
hitsPerRound: 0,
isStunned: this.actor.system.state.stunned,
stunnedModifier: this.stunnedModifier
}; };
} }
@ -53,33 +52,112 @@ export default class RMSSToolsDiceRoller extends FormApplication {
console.log("Rolling Dice"); console.log("Rolling Dice");
console.log(formData); console.log(formData);
console.log(event); console.log(event);
switch (formData.rollType) { this.roll(event.submitter?.dataset?.value, formData);
case "one_to_onehundred": }
this.roll_one_to_onehundred();
break; /* -------------------------------------------- */
case "open_ended": async showDiceSoNice(roll, rollMode) {
this.roll_open_ended(); if (game.modules.get("dice-so-nice")?.active) {
break; if (game.dice3d) {
case "high_open_ended": let whisper = null;
this.roll_high_open_ended(); let blind = false;
break; rollMode = rollMode ?? game.settings.get("core", "rollMode");
case "low_open_ended": switch (rollMode) {
this.roll_low_open_ended(); case "blindroll": //GM only
break; blind = true;
case "gmroll": //GM + rolling player
whisper = this.getUsers(user => user.isGM);
break;
case "roll": //everybody
whisper = this.getUsers(user => user.active);
break;
case "selfroll":
whisper = [game.user.id];
break;
}
await game.dice3d.showForRoll(roll, game.user, true, whisper, blind);
}
} }
} }
/* -------------------------------------------- */
async roll(rollKey, formData) {
let baseRoll = await new Roll("1d100").roll();
await this.showDiceSoNice(baseRoll, game.settings.get("core", "rollMode"))
let rollType = this.rollType.find(r => r.value == rollKey)?.text;
let rollData = {
name: this.itemName,
rollKey: rollKey,
rollType: rollType,
difficulty: Number(formData.difficulty),
combatSituation: Number(formData?.combatSituation || 0),
lightningModifier: Number(formData?.lightningModifier || 0),
darknessModifier: Number(formData?.darknessModifier || 0),
characterBonus: Number(this.characterBonus),
woundsModifier: Number(this.actor.system.modifiers.woundsModifier),
hitsPerRound: Number(formData.hitsPerRound),
isStunned: this.actor.system.state.stunned,
stunnedModifier: this.stunnedModifier,
rolls: [baseRoll],
}
if (baseRoll.result == 66) {
rollData.content = "You rolled a 66!";
}
// Process the for low open ended rolls
if (rollKey === "open_ended" || rollKey === "low_open_ended") {
if (baseRoll.result < 6) {
rollData.lowopen = true
let newRoll = await new Roll("-1d100").roll();
await this.showDiceSoNice(newRoll, game.settings.get("core", "rollMode"))
rollData.rolls.push(newRoll);
while (newRoll.result > 95) {
newRoll = await new Roll("-1d100").roll();
await this.showDiceSoNice(newRoll, game.settings.get("core", "rollMode"))
rollData.rolls.push(newRoll);
}
}
}
// Process the for high open ended rolls
if (rollKey === "open_ended" || rollKey === "high_open_ended") {
if (baseRoll.result > 95) {
rollData.highopen = true
let newRoll = await new Roll("1d100").roll();
await this.showDiceSoNice(newRoll, game.settings.get("core", "rollMode"))
rollData.rolls.push(newRoll);
while (newRoll.result > 95) {
newRoll = await new Roll("1d100").roll();
await this.showDiceSoNice(newRoll, game.settings.get("core", "rollMode"))
rollData.rolls.push(newRoll);
}
}
}
// Compute total of rolls
rollData.totalRolls = rollData.rolls.reduce((acc, roll) => Number(acc) + Number(roll.result), 0);
rollData.totalFinal = rollData.totalRolls + Number(rollData.combatSituation) +
Number(rollData.lightningModifier) +
Number(rollData.darknessModifier) +
Number(this.actor.system.modifiers.woundsModifier) +
Number(formData.difficulty) +
Number(rollData.hitsPerRound) +
Number(rollData.stunnedModifier) +
Number(this.characterBonus);
console.log(">>> Roll Data: ", rollData);
// Define the Chat Message Template
let chatTemplate = "systems/fvtt-rolemaster-frp/templates/chat/chat_dice_roll.html";
// Pass the Data through to be used in the Chat Message
let chatData = rollData
// Render the Rolls to the Chat Window
renderTemplate(chatTemplate, chatData).then((html) => {
let chatOptions = {
style: CONST.CHAT_MESSAGE_STYLES.ROLL,
flavor: rollType,
rollMode: game.settings.get("core", "rollMode"),
content: html,
};
ChatMessage.create(chatOptions);
});
}
} }
RMSSToolsDiceRoller.prototype.roll_one_to_onehundred = roll_one_to_onehundred;
RMSSToolsDiceRoller.prototype.roll_low_open_ended = roll_low_open_ended;
RMSSToolsDiceRoller.prototype.roll_high_open_ended = roll_high_open_ended;
RMSSToolsDiceRoller.prototype.roll_open_ended = roll_open_ended;
RMSSToolsDiceRoller.prototype.getOpenEndedRollModifier =
getOpenEndedRollModifier;
RMSSToolsDiceRoller.prototype.processOpenEndedSixtySixRoll =
processOpenEndedSixtySixRoll;
RMSSToolsDiceRoller.prototype.processOpenEndedRoll = processOpenEndedRoll;
RMSSToolsDiceRoller.prototype.processHighOpenEndedRoll =
processHighOpenEndedRoll;
RMSSToolsDiceRoller.prototype.processLowOpenEndedRoll = processLowOpenEndedRoll;

View File

@ -1,131 +0,0 @@
export async function getOpenEndedRollModifier() {
return await new Roll("1d100x>95").roll();
}
export function processOpenEndedSixtySixRoll(baseroll, rolltype) {
// Log the Roll to Chat
let chatOptions = {
type: CONST.CHAT_MESSAGE_TYPES.ROLL,
rolls: [baseroll],
flavor: rolltype,
rollMode: game.settings.get("core", "rollMode"),
content: "You rolled a 66!",
};
ChatMessage.create(chatOptions);
}
export function processOpenEndedRoll(baseroll, rolltype) {
// Define the Chat Message Template
let chatTemplate = "systems/fvtt-rolemaster-frp/templates/chat/chat_dice_roll.html";
// Pass the Data through to be used in the Chat Message
let chatData = {
baseroll: baseroll,
total: baseroll.result,
highopen: false,
lowopen: false,
};
// Render the Rolls to the Chat Window
renderTemplate(chatTemplate, chatData).then((html) => {
let chatOptions = {
type: CONST.CHAT_MESSAGE_TYPES.ROLL,
rolls: [baseroll],
flavor: rolltype,
rollMode: game.settings.get("core", "rollMode"),
content: html,
};
ChatMessage.create(chatOptions);
});
}
export async function processHighOpenEndedRoll(baseroll, rolltype) {
// Get the Base Roll followed by the High Open Ended Roll
let originalRoll = baseroll;
let openendedRoll = await this.getOpenEndedRollModifier();
// Create a rolls array for Dice So Nice integration.
let rolls = [originalRoll, openendedRoll];
// Create an Array to hold the High Open Ended Roll Results and set the total to base roll
let openendedResults = [];
let total = Number(originalRoll.result);
// Each time the High Open Ended Roll is triggered add the result to an array and add it to the total.
for (const rollResult of openendedRoll.dice[0].results) {
openendedResults.push(rollResult.result);
total = total + Number(rollResult.result);
}
// Define the Chat Message Template
let chatTemplate = "systems/fvtt-rolemaster-frp/templates/chat/chat_dice_roll.html";
// Pass the Data through to be used in the Chat Message
let chatData = {
baseroll: baseroll,
opeendedresults: [openendedResults],
highopen: true,
lowopen: false,
total: total,
};
// Render the Rolls to the Chat Window
renderTemplate(chatTemplate, chatData).then((html) => {
let chatOptions = {
style: CONST.CHAT_MESSAGE_STYLES.ROLL,
rolls: rolls,
flavor: rolltype,
rollMode: game.settings.get("core", "rollMode"),
content: html,
};
ChatMessage.create(chatOptions);
});
}
export async function processLowOpenEndedRoll(baseroll, rolltype) {
// Get the Base Roll followed by the Low Open Ended Roll
let originalRoll = baseroll;
let openendedRoll = await this.getOpenEndedRollModifier();
// Create a rolls array for Dice So Nice integration.
let rolls = [originalRoll, openendedRoll];
// Create an Array to hold the Low Open Ended Roll Results and set the total to base roll
let openendedResults = [];
let total = Number(originalRoll.result);
// Each time the Low Open Ended Roll is triggered add the result to an array and subtract it from the total.
for (const rollResult of openendedRoll.dice[0].results) {
openendedResults.push(rollResult.result);
total = total - Number(rollResult.result);
}
// Define the Chat Message Template
let chatTemplate = "systems/fvtt-rolemaster-frp/templates/chat/chat_dice_roll.html";
// Pass the Data through to be used in the Chat Message
let chatData = {
baseroll: baseroll,
rolls: rolls,
opeendedresults: [openendedResults],
highopen: false,
lowopen: true,
total: total,
};
// Render the Rolls to the Chat Window
renderTemplate(chatTemplate, chatData).then((html) => {
let chatOptions = {
style: CONST.CHAT_MESSAGE_STYLES.ROLL,
rolls: rolls,
flavor: rolltype,
rollMode: game.settings.get("core", "rollMode"),
content: html,
};
ChatMessage.create(chatOptions);
});
}

View File

@ -1,71 +0,0 @@
export async function roll_one_to_onehundred() {
// Construct the Roll instance
let baseroll = await new Roll("1d100 + @characterBonus", {characterBonus: this.characterBonus}).roll({async: true});
let rolltype = "Roll Type: 1-100";
// Log the Roll to Chat
let chatOptions = {
type: CONST.CHAT_MESSAGE_TYPES.ROLL,
rolls: [baseroll],
flavor: rolltype,
rollMode: game.settings.get("core", "rollMode")
};
ChatMessage.create(chatOptions);
this.close();
};
export async function roll_low_open_ended() {
// Construct the Roll instance
let baseroll = await new Roll("1d100").roll({async: true});
console.log(baseroll.result);
let rolltype = "Roll Type: Low Open-Ended";
switch (true) {
case (baseroll.result < 6):
this.processLowOpenEndedRoll(baseroll, rolltype);
break;
case (baseroll.result === 66):
this.processOpenEndedSixtySixRoll(baseroll, rolltype);
break;
default:
this.processOpenEndedRoll(baseroll, rolltype);
}
}
export async function roll_high_open_ended() {
// Construct the Roll instance
let baseroll = await new Roll("1d100").roll({async: true});
console.log(baseroll.result);
let rolltype = "Roll Type: High Open-Ended";
switch (true) {
case (baseroll.result === 66):
this.processOpenEndedSixtySixRoll(baseroll, rolltype);
break;
case (baseroll.result > 95):
this.processHighOpenEndedRoll(baseroll, rolltype);
break;
default:
this.processOpenEndedRoll(baseroll, rolltype);
}
}
export async function roll_open_ended() {
// Construct the Roll instance
let baseroll = await new Roll("1d100").roll({async: true});
console.log(baseroll.result);
let rolltype = "Roll Type: Open-Ended";
switch (true) {
case (baseroll.result < 6):
this.processLowOpenEndedRoll(baseroll, rolltype);
break;
case (baseroll.result === 66):
this.processOpenEndedSixtySixRoll(baseroll, rolltype);
break;
case (baseroll.result > 95):
this.processHighOpenEndedRoll(baseroll, rolltype);
break;
default:
this.processOpenEndedRoll(baseroll, rolltype);
}
}

View File

@ -1,3 +1,5 @@
import { RFRPUtility } from "../../rfrp-utility.js";
export default class RMSSToolsSCImporter extends FormApplication { export default class RMSSToolsSCImporter extends FormApplication {
constructor(selectOptions, character) { constructor(selectOptions, character) {
@ -9,6 +11,7 @@ export default class RMSSToolsSCImporter extends FormApplication {
static get defaultOptions() { static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, { return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["form"], classes: ["form"],
width: 520,
popOut: true, popOut: true,
title: "Import Skill Categories", title: "Import Skill Categories",
template: "systems/fvtt-rolemaster-frp/templates/sheets/apps/app_skill_category_importer.html" template: "systems/fvtt-rolemaster-frp/templates/sheets/apps/app_skill_category_importer.html"
@ -27,30 +30,38 @@ export default class RMSSToolsSCImporter extends FormApplication {
} }
async _updateObject(event, formData) { async _updateObject(event, formData) {
console.log("Deleting Old Skill Categories."); await this.character.setFlag("world", "importing", true);
let itemType = event.submitter.value;
let toDelete = [];
for (const item of this.character.items) { for (const item of this.character.items) {
if (item.type === "skill_category") { if (item.type === itemType) {
item.delete(); toDelete.push(item.id);
}
if (itemType == "skill_category" && item.type === "skill") {
toDelete.push(item.id); // Delete also skill when re-importing skill categories.
} }
} }
await this.character.deleteEmbeddedDocuments("Item", toDelete);
const pack = game.packs.get(formData.selectOptions); let comp = (itemType == "skill") ? formData.selectOptionsSkills : formData.selectOptionsCategories;
const pack = game.packs.get(comp);
const skillCategoryData = await pack.getIndex(); const skillCategoryData = await pack.getIndex();
console.log("Importing New Skill Categories."); console.log("Importing New Skills/Skill Categories.");
let newDocuments = [];
let gameSystem = RFRPUtility.getGameSystem();
for (const sc of skillCategoryData) { for (const sc of skillCategoryData) {
const newitem = await pack.getDocument(sc._id); const newitem = await pack.getDocument(sc._id);
if (newitem.type === itemType && (newitem.system.game_system === "common" || newitem.system.game_system === gameSystem)) {
let newDocuments = [];
if (newitem.type === "skill_category") {
console.log(newitem);
newDocuments.push(newitem); newDocuments.push(newitem);
} }
if (newDocuments.length > 0) {
await Item.createDocuments(newDocuments, {parent: this.character});
}
} }
if (newDocuments.length > 0) {
await Item.createDocuments(newDocuments, {parent: this.character});
}
await this.character.setFlag("world", "importing", false);
} }
} }

View File

@ -1,3 +1,5 @@
import { RFRPUtility } from "../../rfrp-utility.js";
// Our Item Sheet extends the default // Our Item Sheet extends the default
export default class RMSSSkillSheet extends ItemSheet { export default class RMSSSkillSheet extends ItemSheet {
@ -70,21 +72,20 @@ export default class RMSSSkillSheet extends ItemSheet {
// If this Skill is owned then we will return a list of Skill Categories and allow them to choose // If this Skill is owned then we will return a list of Skill Categories and allow them to choose
// Otherwise we'll just return 'Skill has no owner' // Otherwise we'll just return 'Skill has no owner'
prepareSkillCategoryValues() { prepareSkillCategoryValues() {
let skillNoOwner = { None: "Skill Has No Owner" }; let skillCategories = RFRPUtility.getSkillCategories();
if (this.item.isEmbedded) {
if (!this.item.isEmbedded) { skillCategories = this.item.parent.items.filter(it => it.type == "skill_category");
return (skillNoOwner);
} else {
const skillCategories = this.item.parent.getOwnedItemsByType("skill_category");
return (skillCategories);
} }
//console.log("CATEG", skillCategories);
return (skillCategories);
} }
// Determine which Skill Category is selected and test that it is in the current list of categories. // Determine which Skill Category is selected and test that it is in the current list of categories.
// If it isn't set it to None. // If it isn't set it to None.
prepareSelectedSkillCategory(ownedSkillCategories, selectedSkillCategory) { prepareSelectedSkillCategory(ownedSkillCategories, selectedSkillCategory) {
let defaultSelectedCategory = "None"; let defaultSelectedCategory = "None";
if (Object.keys(ownedSkillCategories).includes(selectedSkillCategory)) { let skillC = ownedSkillCategories.find(it => it.name.toLowerCase() == selectedSkillCategory.toLowerCase());
if (skillC) {
return (selectedSkillCategory); return (selectedSkillCategory);
} else { } else {
return (defaultSelectedCategory); return (defaultSelectedCategory);
@ -95,18 +96,14 @@ export default class RMSSSkillSheet extends ItemSheet {
// Iterate through the owned skill categories and if one of them matches the item id of currently // Iterate through the owned skill categories and if one of them matches the item id of currently
// selected skill category then set the Skill Category Bonus field to the Total Bonus field of the Skill Category // selected skill category then set the Skill Category Bonus field to the Total Bonus field of the Skill Category
prepareSelectedSkillCategoryBonus(selected_skillcat) { prepareSelectedSkillCategoryBonus(selected_skillcat) {
if (this.item.isEmbedded === null) { let skillC = this.parent?.items || RFRPUtility.getSkillCategories();
console.log("Skill has no owner"); if (skillC) {
} let item = skillC.find(it => it.type == "skill_category" && it.name.toLowerCase() == itemData.system.category.toLowerCase());
else { if (item) {
const items = this.object.parent.items; this.system.category_bonus = item.system.total_bonus;
return
for (const item of items) {
if (item.type === "skill_category" && item._id === selected_skillcat) {
console.log(`rmss | rmss_skill_sheet | Calculating Skill Category bonus for skill: ${this.object.name}`);
this.object.system.category_bonus = item.system.total_bonus;
}
} }
} }
ui.notifications.warn("No Skill Categories found for " + this.name + ". Please create and link a Skill Category.");
} }
} }

Binary file not shown.

Binary file not shown.

View File

@ -1 +1 @@
MANIFEST-000010 MANIFEST-000137

View File

@ -1,8 +1,15 @@
2024/07/26-14:23:44.308792 7f70f96006c0 Recovering log #8 2024/08/10-08:47:18.899008 7f9286a006c0 Recovering log #135
2024/07/26-14:23:44.319859 7f70f96006c0 Delete type=3 #6 2024/08/10-08:47:18.967334 7f9286a006c0 Delete type=3 #133
2024/07/26-14:23:44.320012 7f70f96006c0 Delete type=0 #8 2024/08/10-08:47:18.967424 7f9286a006c0 Delete type=0 #135
2024/07/26-18:27:58.297420 7f70f3e006c0 Level-0 table #13: started 2024/08/10-09:47:32.985517 7f9285a006c0 Level-0 table #140: started
2024/07/26-18:27:58.297474 7f70f3e006c0 Level-0 table #13: 0 bytes OK 2024/08/10-09:47:32.989699 7f9285a006c0 Level-0 table #140: 25092 bytes OK
2024/07/26-18:27:58.371139 7f70f3e006c0 Delete type=0 #11 2024/08/10-09:47:32.997934 7f9285a006c0 Delete type=0 #138
2024/07/26-18:27:58.497430 7f70f3e006c0 Manual compaction at level-0 from '!items!1HevhbCbvMonyQXe' @ 72057594037927935 : 1 .. '!items!yRIFroc5VC9Oj3qY' @ 0 : 0; will stop at (end) 2024/08/10-09:47:33.008747 7f9285a006c0 Manual compaction at level-0 from '!items!1HevhbCbvMonyQXe' @ 72057594037927935 : 1 .. '!items!yRIFroc5VC9Oj3qY' @ 0 : 0; will stop at (end)
2024/07/26-18:27:58.497486 7f70f3e006c0 Manual compaction at level-1 from '!items!1HevhbCbvMonyQXe' @ 72057594037927935 : 1 .. '!items!yRIFroc5VC9Oj3qY' @ 0 : 0; will stop at (end) 2024/08/10-09:47:33.020954 7f9285a006c0 Manual compaction at level-1 from '!items!1HevhbCbvMonyQXe' @ 72057594037927935 : 1 .. '!items!yRIFroc5VC9Oj3qY' @ 0 : 0; will stop at '!items!yRIFroc5VC9Oj3qY' @ 192 : 1
2024/08/10-09:47:33.020989 7f9285a006c0 Compacting 1@1 + 1@2 files
2024/08/10-09:47:33.025637 7f9285a006c0 Generated table #141@1: 57 keys, 26383 bytes
2024/08/10-09:47:33.025675 7f9285a006c0 Compacted 1@1 + 1@2 files => 26383 bytes
2024/08/10-09:47:33.032592 7f9285a006c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2024/08/10-09:47:33.032827 7f9285a006c0 Delete type=2 #80
2024/08/10-09:47:33.033191 7f9285a006c0 Delete type=2 #140
2024/08/10-09:47:33.050945 7f9285a006c0 Manual compaction at level-1 from '!items!yRIFroc5VC9Oj3qY' @ 192 : 1 .. '!items!yRIFroc5VC9Oj3qY' @ 0 : 0; will stop at (end)

View File

@ -1,8 +1,8 @@
2024/07/26-13:01:33.184951 7f70fa0006c0 Recovering log #4 2024/08/10-00:31:40.615380 7f9287e006c0 Recovering log #131
2024/07/26-13:01:33.276590 7f70fa0006c0 Delete type=0 #4 2024/08/10-00:31:40.668948 7f9287e006c0 Delete type=3 #129
2024/07/26-13:01:33.276711 7f70fa0006c0 Delete type=3 #2 2024/08/10-00:31:40.669000 7f9287e006c0 Delete type=0 #131
2024/07/26-14:15:40.712242 7f70f3e006c0 Level-0 table #9: started 2024/08/10-08:46:35.462850 7f9285a006c0 Level-0 table #136: started
2024/07/26-14:15:40.712297 7f70f3e006c0 Level-0 table #9: 0 bytes OK 2024/08/10-08:46:35.462903 7f9285a006c0 Level-0 table #136: 0 bytes OK
2024/07/26-14:15:40.750893 7f70f3e006c0 Delete type=0 #7 2024/08/10-08:46:35.469319 7f9285a006c0 Delete type=0 #134
2024/07/26-14:15:40.806643 7f70f3e006c0 Manual compaction at level-0 from '!items!1HevhbCbvMonyQXe' @ 72057594037927935 : 1 .. '!items!yRIFroc5VC9Oj3qY' @ 0 : 0; will stop at (end) 2024/08/10-08:46:35.492643 7f9285a006c0 Manual compaction at level-0 from '!items!1HevhbCbvMonyQXe' @ 72057594037927935 : 1 .. '!items!yRIFroc5VC9Oj3qY' @ 0 : 0; will stop at (end)
2024/07/26-14:15:40.863267 7f70f3e006c0 Manual compaction at level-1 from '!items!1HevhbCbvMonyQXe' @ 72057594037927935 : 1 .. '!items!yRIFroc5VC9Oj3qY' @ 0 : 0; will stop at (end) 2024/08/10-08:46:35.510777 7f9285a006c0 Manual compaction at level-1 from '!items!1HevhbCbvMonyQXe' @ 72057594037927935 : 1 .. '!items!yRIFroc5VC9Oj3qY' @ 0 : 0; will stop at (end)

Binary file not shown.

Binary file not shown.

View File

View File

@ -0,0 +1 @@
MANIFEST-000010

0
packs/skills-merp/LOCK Normal file
View File

8
packs/skills-merp/LOG Normal file
View File

@ -0,0 +1,8 @@
2024/08/10-00:18:04.261355 7f92874006c0 Recovering log #8
2024/08/10-00:18:04.271365 7f92874006c0 Delete type=3 #6
2024/08/10-00:18:04.271415 7f92874006c0 Delete type=0 #8
2024/08/10-00:18:22.906854 7f9285a006c0 Level-0 table #13: started
2024/08/10-00:18:22.906926 7f9285a006c0 Level-0 table #13: 0 bytes OK
2024/08/10-00:18:22.913782 7f9285a006c0 Delete type=0 #11
2024/08/10-00:18:22.934544 7f9285a006c0 Manual compaction at level-0 from '!items!5Sg9t8YQubtRoghF' @ 72057594037927935 : 1 .. '!items!zvdsAxlRZnL6gqms' @ 0 : 0; will stop at (end)
2024/08/10-00:18:22.934626 7f9285a006c0 Manual compaction at level-1 from '!items!5Sg9t8YQubtRoghF' @ 72057594037927935 : 1 .. '!items!zvdsAxlRZnL6gqms' @ 0 : 0; will stop at (end)

View File

@ -0,0 +1,8 @@
2024/08/10-00:16:16.693967 7f92874006c0 Recovering log #4
2024/08/10-00:16:16.704014 7f92874006c0 Delete type=3 #2
2024/08/10-00:16:16.704134 7f92874006c0 Delete type=0 #4
2024/08/10-00:17:37.874387 7f9285a006c0 Level-0 table #9: started
2024/08/10-00:17:37.874475 7f9285a006c0 Level-0 table #9: 0 bytes OK
2024/08/10-00:17:37.881498 7f9285a006c0 Delete type=0 #7
2024/08/10-00:17:37.894547 7f9285a006c0 Manual compaction at level-0 from '!items!5Sg9t8YQubtRoghF' @ 72057594037927935 : 1 .. '!items!zvdsAxlRZnL6gqms' @ 0 : 0; will stop at (end)
2024/08/10-00:17:37.894586 7f9285a006c0 Manual compaction at level-1 from '!items!5Sg9t8YQubtRoghF' @ 72057594037927935 : 1 .. '!items!zvdsAxlRZnL6gqms' @ 0 : 0; will stop at (end)

Binary file not shown.

Binary file not shown.

View File

View File

@ -0,0 +1 @@
MANIFEST-000010

0
packs/skills-rmfrp/LOCK Normal file
View File

8
packs/skills-rmfrp/LOG Normal file
View File

@ -0,0 +1,8 @@
2024/08/10-00:18:04.273721 7f928cc006c0 Recovering log #8
2024/08/10-00:18:04.284604 7f928cc006c0 Delete type=3 #6
2024/08/10-00:18:04.284657 7f928cc006c0 Delete type=0 #8
2024/08/10-00:18:22.934766 7f9285a006c0 Level-0 table #13: started
2024/08/10-00:18:22.934803 7f9285a006c0 Level-0 table #13: 0 bytes OK
2024/08/10-00:18:22.944819 7f9285a006c0 Delete type=0 #11
2024/08/10-00:18:22.973938 7f9285a006c0 Manual compaction at level-0 from '!items!03BgTdeYE7TYk9LN' @ 72057594037927935 : 1 .. '!items!zYdJP3YQjsK9c3kh' @ 0 : 0; will stop at (end)
2024/08/10-00:18:22.973982 7f9285a006c0 Manual compaction at level-1 from '!items!03BgTdeYE7TYk9LN' @ 72057594037927935 : 1 .. '!items!zYdJP3YQjsK9c3kh' @ 0 : 0; will stop at (end)

View File

@ -0,0 +1,8 @@
2024/08/10-00:16:16.707097 7f9287e006c0 Recovering log #4
2024/08/10-00:16:16.718136 7f9287e006c0 Delete type=3 #2
2024/08/10-00:16:16.718187 7f9287e006c0 Delete type=0 #4
2024/08/10-00:17:37.881615 7f9285a006c0 Level-0 table #9: started
2024/08/10-00:17:37.881641 7f9285a006c0 Level-0 table #9: 0 bytes OK
2024/08/10-00:17:37.888187 7f9285a006c0 Delete type=0 #7
2024/08/10-00:17:37.894563 7f9285a006c0 Manual compaction at level-0 from '!items!03BgTdeYE7TYk9LN' @ 72057594037927935 : 1 .. '!items!zYdJP3YQjsK9c3kh' @ 0 : 0; will stop at (end)
2024/08/10-00:17:37.894604 7f9285a006c0 Manual compaction at level-1 from '!items!03BgTdeYE7TYk9LN' @ 72057594037927935 : 1 .. '!items!zYdJP3YQjsK9c3kh' @ 0 : 0; will stop at (end)

Binary file not shown.

0
packs/skills/000045.log Normal file
View File

BIN
packs/skills/000047.ldb Normal file

Binary file not shown.

1
packs/skills/CURRENT Normal file
View File

@ -0,0 +1 @@
MANIFEST-000043

0
packs/skills/LOCK Normal file
View File

15
packs/skills/LOG Normal file
View File

@ -0,0 +1,15 @@
2024/08/10-08:47:18.971370 7f928cc006c0 Recovering log #41
2024/08/10-08:47:19.065211 7f928cc006c0 Delete type=3 #39
2024/08/10-08:47:19.065338 7f928cc006c0 Delete type=0 #41
2024/08/10-09:47:33.068934 7f9285a006c0 Level-0 table #46: started
2024/08/10-09:47:33.075322 7f9285a006c0 Level-0 table #46: 140828 bytes OK
2024/08/10-09:47:33.081650 7f9285a006c0 Delete type=0 #44
2024/08/10-09:47:33.090270 7f9285a006c0 Manual compaction at level-0 from '!folders!Lr9SCthdWWHecwEI' @ 72057594037927935 : 1 .. '!items!zvdsAxlRZnL6gqms' @ 0 : 0; will stop at (end)
2024/08/10-09:47:33.097522 7f9285a006c0 Manual compaction at level-1 from '!folders!Lr9SCthdWWHecwEI' @ 72057594037927935 : 1 .. '!items!zvdsAxlRZnL6gqms' @ 0 : 0; will stop at '!items!zYdJP3YQjsK9c3kh' @ 574 : 1
2024/08/10-09:47:33.097537 7f9285a006c0 Compacting 1@1 + 1@2 files
2024/08/10-09:47:33.105028 7f9285a006c0 Generated table #47@1: 260 keys, 146683 bytes
2024/08/10-09:47:33.105065 7f9285a006c0 Compacted 1@1 + 1@2 files => 146683 bytes
2024/08/10-09:47:33.111688 7f9285a006c0 compacted to: files[ 0 0 1 0 0 0 0 ]
2024/08/10-09:47:33.111910 7f9285a006c0 Delete type=2 #10
2024/08/10-09:47:33.112302 7f9285a006c0 Delete type=2 #46
2024/08/10-09:47:33.126561 7f9285a006c0 Manual compaction at level-1 from '!items!zYdJP3YQjsK9c3kh' @ 574 : 1 .. '!items!zvdsAxlRZnL6gqms' @ 0 : 0; will stop at (end)

8
packs/skills/LOG.old Normal file
View File

@ -0,0 +1,8 @@
2024/08/10-00:31:40.672178 7f9286a006c0 Recovering log #37
2024/08/10-00:31:40.729626 7f9286a006c0 Delete type=3 #35
2024/08/10-00:31:40.729689 7f9286a006c0 Delete type=0 #37
2024/08/10-08:46:35.486154 7f9285a006c0 Level-0 table #42: started
2024/08/10-08:46:35.486208 7f9285a006c0 Level-0 table #42: 0 bytes OK
2024/08/10-08:46:35.492434 7f9285a006c0 Delete type=0 #40
2024/08/10-08:46:35.510744 7f9285a006c0 Manual compaction at level-0 from '!folders!Lr9SCthdWWHecwEI' @ 72057594037927935 : 1 .. '!items!zvdsAxlRZnL6gqms' @ 0 : 0; will stop at (end)
2024/08/10-08:46:35.510832 7f9285a006c0 Manual compaction at level-1 from '!folders!Lr9SCthdWWHecwEI' @ 72057594037927935 : 1 .. '!items!zvdsAxlRZnL6gqms' @ 0 : 0; will stop at (end)

Binary file not shown.

View File

@ -41,6 +41,10 @@
border-bottom: 1px solid; border-bottom: 1px solid;
background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0); background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0);
} }
.skill-box {
margin-left: 1.2rem;
background-color: lightgrey;
}
.container { .container {
display: flex; display: flex;
} }
@ -199,7 +203,6 @@
padding-bottom: 3px; padding-bottom: 3px;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
flex-direction: column;
justify-content: center; justify-content: center;
} }
.skills-grid-container > div:nth-child(22n+1), .skills-grid-container > div:nth-child(22n+1),
@ -221,7 +224,6 @@
padding-bottom: 3px; padding-bottom: 3px;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
flex-direction: column;
justify-content: center; justify-content: center;
background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0); background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0);
} }
@ -230,6 +232,10 @@
border-bottom: 1px solid; border-bottom: 1px solid;
background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0); background-image: linear-gradient(rgba(0, 0, 0, 0.1) 0 0);
} }
.skills-name-left-align {
text-align: left;
padding-left: 2px;
}
.money-column { .money-column {
flex-direction: column; flex-direction: column;
} }
@ -492,6 +498,9 @@
font-size: 12px; font-size: 12px;
text-align: center; text-align: center;
} }
.select-fixed-width {
width: 100px;
}
.applicable-stats-grid-container { .applicable-stats-grid-container {
display: grid; display: grid;
grid-template-columns: [app-stat-1] 33% [app-stat-2] 33% [app-stat-3] 33%; grid-template-columns: [app-stat-1] 33% [app-stat-2] 33% [app-stat-3] 33%;
@ -522,8 +531,13 @@
} }
.roll-button-container { .roll-button-container {
display: flex; display: flex;
flex-direction: column; flex-direction: row;
justify-content: flex-end; justify-content: flex-start;
}
.roll-button {
margin-top: 2px;
width: 96px;
max-width: 96px;
} }
.sheet form { .sheet form {
display: flex; display: flex;

74
rmss.js
View File

@ -19,40 +19,13 @@ import RMSSSkillSheet from "./module/sheets/skills/rmss_skill_sheet.js";
import RMSSPlayerSheet from "./module/sheets/actors/rmss_player_sheet.js"; import RMSSPlayerSheet from "./module/sheets/actors/rmss_player_sheet.js";
import RMSSToolsSCImporter from "./module/sheets/apps/rmss_import_skill_categories.js"; import RMSSToolsSCImporter from "./module/sheets/apps/rmss_import_skill_categories.js";
import RMSSToolsDiceRoller from "./module/sheets/apps/rmss_dice_roller.js"; import RMSSToolsDiceRoller from "./module/sheets/apps/rmss_dice_roller.js";
import { RFRPUtility } from "./module/rfrp-utility.js";
/** Preload handlebars templates for character sheets */
async function preloadHandlebarsTemplates() {
const templatePaths = [
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-stats.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-fixed-info.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-armor-info.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-resistance.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-race-stat-fixed-info.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-role-traits.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-background-info.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-skill-categories.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-skills.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-fav-skills.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-items.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-weapons.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-money.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-skill-categories.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-skills.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-armor.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-herbs.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-spells.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-fav-spells.html",
"systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-fav-items.html",
"systems/fvtt-rolemaster-frp/templates/sheets/apps/app_skill_category_importer.html"
];
return loadTemplates(templatePaths);
}
// Register Scene Controls // Register Scene Controls
// registerGetSceneControlButtonsHook(); // registerGetSceneControlButtonsHook();
// Hook the init function and set up our system // Hook the init function and set up our system
Hooks.once("init", function() { Hooks.once("init", function () {
console.log("rmss | Initialising Rolemaster Standard System"); console.log("rmss | Initialising Rolemaster Standard System");
// Load our custom actor and item classes // Load our custom actor and item classes
@ -74,7 +47,7 @@ Hooks.once("init", function() {
CONFIG.fontDefinitions["PragRoman"] = { CONFIG.fontDefinitions["PragRoman"] = {
editor: true, editor: true,
fonts: [ fonts: [
{urls: ["systems/fvtt-rolemaster-frp/assets/fonts/PragRoman.woff2"]} { urls: ["systems/fvtt-rolemaster-frp/assets/fonts/PragRoman.woff2"] }
] ]
}; };
@ -91,35 +64,30 @@ Hooks.once("init", function() {
console.log("rmss | Registering RMSS sheets"); console.log("rmss | Registering RMSS sheets");
// Items // Items
Items.registerSheet("fvtt-rolemaster-frp", RMSSItemSheet, {makeDefault: true, label: "rmss.entity_sheet.item", types: ["item"]}); Items.registerSheet("fvtt-rolemaster-frp", RMSSItemSheet, { makeDefault: true, label: "rmss.entity_sheet.item", types: ["item"] });
Items.registerSheet("fvtt-rolemaster-frp", RMSSArmorSheet, {makeDefault: true, label: "rmss.entity_sheet.armor", types: ["armor"]}); Items.registerSheet("fvtt-rolemaster-frp", RMSSArmorSheet, { makeDefault: true, label: "rmss.entity_sheet.armor", types: ["armor"] });
Items.registerSheet("fvtt-rolemaster-frp", RMSSTransportSheet, {makeDefault: true, label: "rmss.entity_sheet.transport", types: ["transport"]}); Items.registerSheet("fvtt-rolemaster-frp", RMSSTransportSheet, { makeDefault: true, label: "rmss.entity_sheet.transport", types: ["transport"] });
Items.registerSheet("fvtt-rolemaster-frp", RMSSWeaponSheet, {makeDefault: true, label: "rmss.entity_sheet.weapon", types: ["weapon"]}); Items.registerSheet("fvtt-rolemaster-frp", RMSSWeaponSheet, { makeDefault: true, label: "rmss.entity_sheet.weapon", types: ["weapon"] });
Items.registerSheet("fvtt-rolemaster-frp", RMSSHerbOrPoisonSheet, {makeDefault: true, label: "rmss.entity_sheet.herb_or_poison", types: ["herb_or_poison"]}); Items.registerSheet("fvtt-rolemaster-frp", RMSSHerbOrPoisonSheet, { makeDefault: true, label: "rmss.entity_sheet.herb_or_poison", types: ["herb_or_poison"] });
// Spells // Spells
Items.registerSheet("fvtt-rolemaster-frp", RMSSSpellSheet, {makeDefault: true, label: "rmss.entity_sheet.spell", types: ["spell"]}); Items.registerSheet("fvtt-rolemaster-frp", RMSSSpellSheet, { makeDefault: true, label: "rmss.entity_sheet.spell", types: ["spell"] });
// Skills // Skills
Items.registerSheet("fvtt-rolemaster-frp", RMSSSkillCategorySheet, {makeDefault: true, label: "rmss.entity_sheet.skill_category", types: ["skill_category"]}); Items.registerSheet("fvtt-rolemaster-frp", RMSSSkillCategorySheet, { makeDefault: true, label: "rmss.entity_sheet.skill_category", types: ["skill_category"] });
Items.registerSheet("fvtt-rolemaster-frp", RMSSSkillSheet, {makeDefault: true, label: "rmss.entity_sheet.skill", types: ["skill"]}); Items.registerSheet("fvtt-rolemaster-frp", RMSSSkillSheet, { makeDefault: true, label: "rmss.entity_sheet.skill", types: ["skill"] });
// Actors // Actors
Actors.registerSheet("fvtt-rolemaster-frp", RMSSPlayerSheet, {makeDefault: true, label: "rmss.entity_sheet.player_characrer", types: ["character"]}); Actors.registerSheet("fvtt-rolemaster-frp", RMSSPlayerSheet, { makeDefault: true, label: "rmss.entity_sheet.player_characrer", types: ["character"] });
// Preload Handlebars Templates RFRPUtility.loadHandlebarsTemplates();
console.log("rmss | Preloading Handlebars Templates"); RFRPUtility.loadHandlebarsHelpers();
preloadHandlebarsTemplates();
// Handlebars Helpers
Handlebars.registerHelper("switch", function(value, options) {
this.switch_value = value;
return options.fn(this);
});
Handlebars.registerHelper("case", function(value, options) {
if (value === this.switch_value) {
return options.fn(this);
}
});
}); });
Hooks.once("ready", async function () {
console.log("rmss | Ready");
// Load Utility
await RFRPUtility.ready();
})

View File

@ -3,7 +3,7 @@
"title": "Rolemaster FRP System", "title": "Rolemaster FRP System",
"description": "The Rolemaster FRP system for FoundryVTT.", "description": "The Rolemaster FRP system for FoundryVTT.",
"manifest": "https://www.uberwald.me/gitea/public/fvtt-rolemaster-frp/raw/branch/develop/system.json", "manifest": "https://www.uberwald.me/gitea/public/fvtt-rolemaster-frp/raw/branch/develop/system.json",
"download": "https://www.uberwald.me/gitea/public/fvtt-rolemaster-frp/archive/v12.0.1.zip", "download": "https://www.uberwald.me/gitea/public/fvtt-rolemaster-frp/archive/v12.0.16.zip",
"authors": [ "authors": [
{ {
"name": "Cynicide", "name": "Cynicide",
@ -14,7 +14,7 @@
"email": "" "email": ""
} }
], ],
"version": "12.0.1", "version": "12.0.16",
"compatibility": { "compatibility": {
"minimum": "12", "minimum": "12",
"verified": "12" "verified": "12"
@ -27,7 +27,14 @@
"name": "skill_categories", "name": "skill_categories",
"label": "Skill Categories", "label": "Skill Categories",
"system": "fvtt-rolemaster-frp", "system": "fvtt-rolemaster-frp",
"path": "./packs/skill_categories.db", "path": "./packs/skill_categories",
"type": "Item"
},
{
"name": "skill",
"label": "Skills",
"system": "fvtt-rolemaster-frp",
"path": "./packs/skills",
"type": "Item" "type": "Item"
} }
], ],

View File

@ -222,6 +222,14 @@
}, },
"character": { "character": {
"templates": ["background", "stats", "resistance_rolls", "armor_info", "fixed_info", "race_stat_fixed_info", "role_traits", "money", "description"], "templates": ["background", "stats", "resistance_rolls", "armor_info", "fixed_info", "race_stat_fixed_info", "role_traits", "money", "description"],
"modifiers": {
"woundsModifier": 0
},
"state": {
"stunned": false,
"prone": false,
"noparry": false
},
"attributes": { "attributes": {
"level": { "level": {
"value": 1 "value": 1
@ -305,6 +313,7 @@
"skill": { "skill": {
"templates": ["base"], "templates": ["base"],
"category" : "", "category" : "",
"game_system": "common",
"ranks": 0, "ranks": 0,
"new_ranks": { "new_ranks": {
"value": 0, "value": 0,
@ -318,11 +327,13 @@
"special_bonus_2": 0, "special_bonus_2": 0,
"total_bonus": 0, "total_bonus": 0,
"favorite": false, "favorite": false,
"bonus_progression": "standard",
"designation": "None" "designation": "None"
}, },
"skill_category": { "skill_category": {
"templates": ["base"], "templates": ["base"],
"applicable_stats": "None", "applicable_stats": "None",
"game_system": "common",
"app_stat_1": "None", "app_stat_1": "None",
"app_stat_2": "None", "app_stat_2": "None",
"app_stat_3": "None", "app_stat_3": "None",
@ -339,6 +350,7 @@
"special_bonus_1": 0, "special_bonus_1": 0,
"special_bonus_2": 0, "special_bonus_2": 0,
"total_bonus": 0, "total_bonus": 0,
"bonus_progression": "standard",
"favorite": false "favorite": false
}, },
"spell": { "spell": {

View File

@ -1,28 +1,70 @@
<div class="dice-roll"> <div class="dice-roll">
<div class="dice-result"> <div class="dice-result">
Original Roll: Rolling for : {{name}}
<h4 class="dice-total" >{{ baseroll.result }}</h4> </div>
</div> {{#if highopen }}
{{#if highopen }} <div class=dice-result>
<div class=dice-result> Rolled over 95 - High Open-Ended!
Rolled over 95 - High Open-Ended! </div>
{{#each opeendedresults as |rollresult|}} {{/if}}
<h4 class="dice-total" >{{ rollresult }}</h4> {{#if lowopen }}
{{/each}} <div class=dice-result>
</div> Rolled under 6 - Low Open-Ended!
{{/if}} </div>
{{/if}}
{{#if lowopen }} {{#if difficulty}}
<div class=dice-result> <div class=dice-result>
Rolled under 6 - Low Open-Ended! Difficulty : {{difficulty}}
{{#each opeendedresults as |rollresult|}} </div>
<h4 class="dice-total" >{{ rollresult }}</h4> {{/if}}
{{/each}}
</div>
{{/if}}
<div class="dice-result"> {{#if combatSituation}}
Total: <div class=dice-result>
<h4 class="dice-total" >{{ total }}</h4> Combat situation : {{combatSituation}}
</div> </div>
{{/if}}
{{#if stunnedModifier}}
<div class=dice-result>
Stunned : {{stunnedModifier}}
</div>
{{/if}}
{{#if hitsPerRound}}
<div class=dice-result>
Hits/round malus : {{hitsPerRound}}
</div>
{{/if}}
{{#if lightningModifier}}
<div class=dice-result>
Lightning modifierLight : {{lightningModifier}}
</div>
{{/if}}
{{#if darknessModifier}}
<div class=dice-result>
Darkness modifierDark : {{darknessModifier}}
</div>
{{/if}}
{{#if woundsModifier}}
<div class="dice-result">
<div>% of hits malus : {{woundsModifier}}</div>
</div>
{{/if}}
<div class="dice-result">
Total of d100 rolls:
<h4 class="dice-total">{{totalRolls}}<br>
({{#each rolls as |roll|}}{{roll.total}}{{#unless @last}}, {{/unless}}{{/each}})</h4>
</div>
<div class=dice-result>
Character Bonus : {{characterBonus}}
</div>
<div class="dice-result">
Total :
<h4 class="dice-total">{{totalFinal}}</h4>
</div>
</div> </div>

View File

@ -15,8 +15,11 @@
<button type="button" class="import-skillcats" title="Import" acotr_id="">{{ localize "rmss.pc_sheet.import_skillcat" }}</button> <button type="button" class="import-skillcats" title="Import" acotr_id="">{{ localize "rmss.pc_sheet.import_skillcat" }}</button>
</div> </div>
</div> </div>
</div>
{{#each skillcat as |skill_category id|}} {{#each skillcat as |skill_category id|}}
<div>{{skill_category.name}}</div> <div class="skillcat-grid-container">
<div><a class="item-roll" title="Roll Check" data-item-id="{{skill_category._id}}">{{skill_category.name}}</a> <i class="fas fa-dice"></i></div>
<div>{{skill_category.system.applicable_stats}}</div> <div>{{skill_category.system.applicable_stats}}</div>
<div>{{skill_category.system.development_cost}}</div> <div>{{skill_category.system.development_cost}}</div>
<div>{{skill_category.system.ranks}}</div> <div>{{skill_category.system.ranks}}</div>
@ -36,8 +39,14 @@
<div>{{skill_category.system.total_bonus}}</div> <div>{{skill_category.system.total_bonus}}</div>
<div class="skillcat-icons"> <div class="skillcat-icons">
<a class="item-edit" title="Edit Category" data-item-id="{{skill_category._id}}"><i class="fas fa-edit"></i></a> <a class="item-edit" title="Edit Category" data-item-id="{{skill_category._id}}"><i class="fas fa-edit"></i></a>
<a class="item-delete item" title="Delete Category" data-item-id="{{skill_category._id}}"><i class="fas fa-trash"></i></a> <a class="item-delete item" title="Delete Category" data-item-id="{{skill_category._id}}"><i class="fas fa-trash"></i></a>
<a class="item-roll" title="Roll Check" data-item-id="{{skill_category._id}}"><i class="fas fa-dice"></i></a>
</div> </div>
</div>
{{#if (count skill_category.skills)}}
<div class="skill-box">
{{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-skills.html" }}
</div>
{{/if}}
{{/each}} {{/each}}
</div>

View File

@ -10,16 +10,19 @@
<div class="skills-grid-heading">{{ localize "rmss.pc_sheet_skills.special_bonus" }}</div> <div class="skills-grid-heading">{{ localize "rmss.pc_sheet_skills.special_bonus" }}</div>
<div class="skills-grid-heading">{{ localize "rmss.pc_sheet_skills.total_bonus" }}</div> <div class="skills-grid-heading">{{ localize "rmss.pc_sheet_skills.total_bonus" }}</div>
<div class="skills-grid-heading"> <div class="skills-grid-heading">
<!--<a class="item-create" title="Create Skill" data-type="skill"><i class="fas fa-plus"></i>{{ localize "rmss.pc_sheet_skills.add_skill" }}</a>--> <a class="item-create" title="Create Skill" data-type="skill"><i class="fas fa-plus"></i>{{ localize "rmss.pc_sheet_skills.add_skill" }}</a>
</div> </div>
{{#each playerskill as |skill id|}} </div>
{{#if skill.system.favorite}}
{{#each skills as |skill id|}}
<div class="skills-grid-container">
{{#if skill.system.favorite}}
<div><a class="skill-favorite" data-item-id="{{skill._id}}"><i class="fa-regular fa-square-check"></i></a></div> <div><a class="skill-favorite" data-item-id="{{skill._id}}"><i class="fa-regular fa-square-check"></i></a></div>
{{else}} {{else}}
<div><a class="skill-favorite" data-item-id="{{skill._id}}"><i class="fa-regular fa-square"></i></a></div> <div><a class="skill-favorite" data-item-id="{{skill._id}}"><i class="fa-regular fa-square"></i></a></div>
{{/if}} {{/if}}
<!--<div><input type="checkbox" name="system.favorite" {{checked skill.system.favorite}}/></div>--> <!--<div><input type="checkbox" name="system.favorite" {{checked skill.system.favorite}}/></div>-->
<div>{{skill.name}}</div> <div class="skills-name-left-align"><a class="item-roll" title="Roll Check" data-item-id="{{skill._id}}">{{skill.name}}<i class="fas fa-dice"></i></a></div>
<div>{{skill.system.ranks}}</div> <div>{{skill.system.ranks}}</div>
<div> <div>
{{#switch skill.system.new_ranks.value}} {{#switch skill.system.new_ranks.value}}
@ -35,9 +38,10 @@
<div>{{skill.system.special_bonus_1}}</div> <div>{{skill.system.special_bonus_1}}</div>
<div>{{skill.system.special_bonus_2}}</div> <div>{{skill.system.special_bonus_2}}</div>
<div>{{skill.system.total_bonus}}</div> <div>{{skill.system.total_bonus}}</div>
<div> <div >
<a class="item-edit" title="Edit Skill" data-item-id="{{skill._id}}"><i class="fas fa-edit"></i></a> <a class="item-edit" title="Edit Skill" data-item-id="{{skill._id}}"><i class="fas fa-edit"></i></a>
<a class="item-delete" title="Delete Skill" data-item-id="{{skill._id}}"><i class="fas fa-trash"></i></a> <a class="item-delete" title="Delete Skill" data-item-id="{{skill._id}}"><i class="fas fa-trash"></i></a>
</div> </div>
</div>
{{/each}} {{/each}}
</div>

View File

@ -19,6 +19,7 @@
<div class="resource-entry"> <div class="resource-entry">
{{ localize "rmss.player_character.resources.hits" }} {{ localize "rmss.player_character.resources.hits" }}
<h4><input name="system.attributes.hits.current" type="Number" value="{{system.attributes.hits.current}}"/>/<input name="system.attributes.hits.max" type="Number" value="{{system.attributes.hits.max}}"/></h4> <h4><input name="system.attributes.hits.current" type="Number" value="{{system.attributes.hits.current}}"/>/<input name="system.attributes.hits.max" type="Number" value="{{system.attributes.hits.max}}"/></h4>
<div>Wounds Modifier : {{system.modifiers.woundsModifier}}</div>
</div> </div>
<div class="resource-entry"> <div class="resource-entry">
@ -70,9 +71,9 @@
{{!-- Default tab is specified in actor-sheet.mjs --}} {{!-- Default tab is specified in actor-sheet.mjs --}}
<a class="item" data-tab="Record">{{ localize "rmss.pc_sheet_tabs.record" }}</a> <a class="item" data-tab="Record">{{ localize "rmss.pc_sheet_tabs.record" }}</a>
<a class="item" data-tab="SkillCategory">{{ localize "rmss.pc_sheet_tabs.skill_categories" }}</a> <a class="item" data-tab="SkillCategory">{{ localize "rmss.pc_sheet_tabs.skill_categories" }}</a>
<a class="item" data-tab="Skills">{{ localize "rmss.pc_sheet_tabs.skills" }}</a>
<a class="item" data-tab="Equipment">{{ localize "rmss.pc_sheet_tabs.equipment" }}</a> <a class="item" data-tab="Equipment">{{ localize "rmss.pc_sheet_tabs.equipment" }}</a>
<a class="item" data-tab="Spells">{{ localize "rmss.pc_sheet_tabs.spells" }}</a> <a class="item" data-tab="Spells">{{ localize "rmss.pc_sheet_tabs.spells" }}</a>
<a class="item" data-tab="Background">{{ localize "rmss.pc_sheet_tabs.background" }}</a>
<a class="item" data-tab="StatusRecord">{{ localize "rmss.pc_sheet_tabs.status_record" }}</a> <a class="item" data-tab="StatusRecord">{{ localize "rmss.pc_sheet_tabs.status_record" }}</a>
<a class="item" data-tab="ExperiencePoints">{{ localize "rmss.pc_sheet_tabs.experience_points" }}</a> <a class="item" data-tab="ExperiencePoints">{{ localize "rmss.pc_sheet_tabs.experience_points" }}</a>
</nav> </nav>
@ -84,14 +85,16 @@
{{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-fixed-info.html" }} {{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-fixed-info.html" }}
{{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-armor-info.html" }} {{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-armor-info.html" }}
{{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-resistance.html" }} {{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-resistance.html" }}
{{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-race-stat-fixed-info.html" }}
{{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-role-traits.html" }}
{{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-background-info.html" }}
</div> </div>
<div class="stat-container"> <div class="stat-container">
{{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-stats.html" }} {{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-stats.html" }}
<div class="favorite-container">
<span class="label-name">Stunned : </span><input type="checkbox" name="system.state.stunned" {{checked system.state.stunned}}/>
<span class="label-name">Prone : </span><input type="checkbox" name="system.state.prone" {{checked system.state.prone}}/>
</div>
<div class="favorite-container"> <div class="favorite-container">
<h2>Favorite Skills</h2> <h2>Favorite Skills</h2>
{{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-fav-skills.html" }} {{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-fav-skills.html" }}
@ -103,8 +106,6 @@
{{editor enrichedDescription target="system.description" button=true owner=owner editable=editable}} {{editor enrichedDescription target="system.description" button=true owner=owner editable=editable}}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@ -112,10 +113,6 @@
{{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-skill-categories.html" }} {{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-skill-categories.html" }}
</div> </div>
<div class="tab skills" data-group="primary" data-tab="Skills">
{{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-skills.html" }}
</div>
<div class="tab equipment" data-group="primary" data-tab="Equipment"> <div class="tab equipment" data-group="primary" data-tab="Equipment">
<div class="container"> <div class="container">
<div class="equipment-container"> <div class="equipment-container">
@ -140,6 +137,19 @@
{{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-spells.html" }} {{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-spells.html" }}
</div> </div>
</div> </div>
<div class="tab background" data-group="primary" data-tab="Background">
<div class="container">
<div class="fixed-info-container">
{{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-race-stat-fixed-info.html" }}
</div>
<div class="fixed-info-container">
{{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-role-traits.html" }}
{{> "systems/fvtt-rolemaster-frp/templates/sheets/actors/parts/actor-background-info.html" }}
</div>
</div>
</div>
</section> </section>
</form> </form>

View File

@ -1,27 +1,96 @@
<form> <form>
<div class="dice-roller-container"> <div class="dice-roller-container">
<div class="dice-roller-options"> <div class="dice-roller-options">
<div class="dice-roller-item"> <div class="dice-roller-item">
<div>Rolling for:</div><div>{{itemName}}</div> <div>Rolling for:</div>
</div> <div>{{itemName}}</div>
<div class="dice-roller-item"> </div>
<div>Character Bonus:</div><div>{{characterBonus}}</div> <div class="dice-roller-item">
</div> <div>Character Bonus:</div>
<div class="dice-roller-item"> <div>{{characterBonus}}</div>
<div>Misc Bonus:</div><div><input name="miscBonus" value="0" /></div> </div>
</div> <div class="dice-roller-item">
<div class="dice-roller-item"> <div>Difficulty:</div>
<div>Select Roll Type:</div> <div>
<div> <select name="difficulty">
<select name="rollType" value="None" itemid="blah"> {{selectOptions config.difficulties selected="0" valueKey="value" labelKey="label"}}
{{#each selectOptions as |option|}} </select>
<option value="{{option.value}}" {{#if option.selected}}selected{{/if}}>{{option.text}}</option>
{{/each}}
</select>
</div>
</div> </div>
<div class="roll-button-container"> </div>
<button type="submit">Roll</button>
<div class="dice-roller-item">
<div>% of hits taken malus:</div>
<div>{{woundsModifier}}</div>
</div>
{{#if isStunned}}
<div class="dice-roller-item">
<div>Stunned:</div>
<div>{{stunnedModifier}}</div>
</div>
{{/if}}
<div class="dice-roller-item">
<div>Combat situations:</div>
<div>
<select name="combatSituation">
{{#select combatSituation}}
{{#each config.combatSituations as |situation|}}
<option value="{{situation.modifier}}">{{situation.label}}</option>
{{/each}}
{{/select}}
</select>
</div> </div>
</div>
<div class="dice-roller-item">
<div>Hits/round taken:</div>
<div>
<select name="hitsPerRound">
{{#select hitsPerRound}}
{{#each config.hitsPerRound as |hit|}}
<option value="{{hit.modifier}}">{{hit.label}}</option>
{{/each}}
{{/select}}
</select>
</div>
</div>
<div class="dice-roller-item">
<div>Lightning, if required:</div>
<div>
<select name="lightningModifier">
{{#select lightningModifier}}
{{#each config.lightOrDarknessModifiers as |lightning|}}
<option value="{{lightning.modifierLight}}">{{lightning.label}}</option>
{{/each}}
{{/select}}
</select>
</div>
</div>
<div class="dice-roller-item">
<div>Darkness, if advantageous:</div>
<div>
<select name="darknessModifier">
{{#select lightningModifier}}
{{#each config.lightOrDarknessModifiers as |darkness|}}
<option value="{{darkness.modifierDark}}">{{darkness.label}}</option>
{{/each}}
{{/select}}
</select>
</div>
</div>
<div class="dice-roller-item">
<div>Misc Bonus:</div>
<div><input name="miscBonus" value="0" /></div>
</div>
<div class="roll-button-container">
{{#each selectOptions as |option|}}
<button type="submit" class="roll-button" data-value="{{option.value}}">{{option.text}}</button>
{{/each}}
</div>
</div> </div>
</div>
</form> </form>

View File

@ -1,17 +1,28 @@
<form> <form>
<div> <div>
<h3>Import Skill Categories</h3>
<h3>Import Skill Categories/Skills</h3>
<div> <div>
WARNING: This will erase your existing Skill Categories and import all Skill Categories from the selected Compendium. <span>
WARNING: This will erase your existing Skill Categories or Skills and import all Skill Categories/Skills from the selected Compendium.
Note that the import will select only skills and categories matching the "Common" items and the selected game system.
</span>
</div> </div>
<div> <div class="flexrow">
Select Compendium: <span >Select Skill Categories Compendium:</span>
<select name="selectOptions" class="compendium-selector" value="None" itemid="blah"> <select name="selectOptionsCategories" class="compendium-selector" value="None" itemid="blah">
{{selectOptions selectOptions}} {{selectOptions selectOptions}}
</select></div> </select>
<div> <button class="import-skillcats" value="skill_category" name="skill_category" title="Import">Import Skill Categories</button>
<button class="import-skillcats" title="Import">Import</button>
</div> </div>
<div class="flexrow">
<span>Select Skills Compendium:</span>
<select name="selectOptionsSkills" class="compendium-selector" value="None" itemid="blah">
{{selectOptions selectOptions}}
</select>
<button class="import-skills" value="skill" name="skill" title="Import">Import Skills</button>
</div>
</div> </div>
</form> </form>

View File

@ -4,7 +4,19 @@
<h1><input name="name" type="text" value="{{item.name}}" placeholder="{{ localize 'Name' }}"/></h1> <h1><input name="name" type="text" value="{{item.name}}" placeholder="{{ localize 'Name' }}"/></h1>
</header> </header>
<div class="sheet-content"> <div class="sheet-content">
<div class="applicable-stats-grid-container"> <div class="flexrow">
<div>Rank Bonus Progression
<select class="select-fixed-width" name="system.bonus_progression" value="{{system.bonus_progression}}" itemid="{{ item._id }}">
{{selectOptions config.rankBonusProgressionList selected=system.bonus_progression valueAttr="key" labelAttr="label"}}
</select>
</div>
<div>Game system
<select class="select-fixed-width" name="system.game_system" value="{{system.game_system}}" itemid="{{ item._id }}">
{{selectOptions config.gameSystems selected=system.game_system valueAttr="key" labelAttr="label"}}
</select>
</div>
</div>
<div class="applicable-stats-grid-container">
<div> <div>
Applicable Stat 1 Applicable Stat 1
<select name="system.app_stat_1" class="app-stat-selector" value="{{system.app_stat_1}}" itemid="{{ item._id }}"> <select name="system.app_stat_1" class="app-stat-selector" value="{{system.app_stat_1}}" itemid="{{ item._id }}">

View File

@ -4,10 +4,22 @@
<h1><input name="name" type="text" value="{{item.name}}" placeholder="{{ localize 'Name' }}"/></h1> <h1><input name="name" type="text" value="{{item.name}}" placeholder="{{ localize 'Name' }}"/></h1>
</header> </header>
<div class="sheet-content"> <div class="sheet-content">
<div> <div class="flexrow">
<div>Rank Bonus Progression
<select class="select-fixed-width" name="system.bonus_progression" value="{{system.bonus_progression}}" itemid="{{ item._id }}">
{{selectOptions config.rankBonusProgressionList selected=system.bonus_progression valueAttr="key" labelAttr="label"}}
</select>
</div>
<div>Game system
<select class="select-fixed-width" name="system.game_system" value="{{system.game_system}}" itemid="{{ item._id }}">
{{selectOptions config.gameSystems selected=system.game_system valueAttr="key" labelAttr="label"}}
</select>
</div>
</div>
<div>
Skill Category Skill Category
<select name="system.category" class="app-stat-selector" value="{{system.category}}" itemid="{{ item._id }}"> <select name="system.category" class="app-stat-selector" value="{{system.category}}">
{{selectOptions owned_skillcats selected=selected_skillcat }} {{selectOptions owned_skillcats selected=selected_skillcat nameAttr="name" valueAttr="name" labelAttr="name"}}
</select> </select>
</div> </div>
<div> <div>