mirror of
https://github.com/Nighthawk42/roll20-character-sheets.git
synced 2026-08-30 03:40:32 +00:00
Added a campaign-setting-neutral version of my Roleplaying is Magic 4E character sheet.
This commit is contained in:
@@ -0,0 +1,333 @@
|
||||
/**
|
||||
* Provides a dice rolling command specialized for MLP: RiM season 4 edition.
|
||||
* The syntax for the command is
|
||||
* !r [{Character name}:]{skill name} [+/- Advantages/Drawbacks]
|
||||
*
|
||||
* e.g.
|
||||
* !r Spectrum Square:Energy Weapons +4 -2
|
||||
*
|
||||
* The character name and skill name are case-insensitive. You should only
|
||||
* need to enter the character name if you control more than one character,
|
||||
* otherwise it automatically use the first character you control (which for
|
||||
* most players is just one).
|
||||
*
|
||||
* There are a couple other assumptions the script makes about a character's
|
||||
* attributes in order for it to work:
|
||||
* - All attributes related to skills have the word 'skill' in them somewhere.
|
||||
* - Skill attributes are divided into 4 parts:
|
||||
* repeating_skills{mind|body|heart}_{repeating skills index}_{SkillAttributeName}
|
||||
*
|
||||
* * The 1st part is just a literal used by roll20 for all repeating fieldset attributes
|
||||
* on a character sheet.
|
||||
* * The 2nd part is the name of the repeating field set. The script assumes that
|
||||
* your character sheets have skillsmind, skillsbody, and skillsheart repeating
|
||||
* fieldsets, which are identical accept for the primary attribute they're
|
||||
* based off of.
|
||||
* * The 3rd part is the index of the skill's group of attributes in the
|
||||
* repeating fieldset. All attributes for one skill share this index. It too
|
||||
* is automatically generated by Roll20's character sheet API.
|
||||
* * The 4th part is the name of an attribute for the skill. This script assumes
|
||||
* that each skill's repeating field set contains fields with these names:
|
||||
* skillX, skillXT, skillXI, skillXG, skillXMisc, where X is M, B, or H for
|
||||
* mind, body, and heart respectively.
|
||||
*
|
||||
* skillX is the text field actually containing the skill's name.
|
||||
*
|
||||
* skillXT is a checkbox field marking whether the skill is trained. I.E.
|
||||
* The character took the Skill Training edge for that skill.
|
||||
* The checkbox's value when checked is 1.
|
||||
*
|
||||
* skillXI is a checkbox for Improved Skill Training for the skill, again
|
||||
* with a checked value of 1.
|
||||
*
|
||||
* skillXG is a checkbox for Greater Skill Training for the skill, again
|
||||
* with a checked value of 1.
|
||||
*
|
||||
* skillXMisc is a number field for the total of any other modifiers for
|
||||
* the skill, which aren't Advantages and Drawbacks.
|
||||
*/
|
||||
(function() {
|
||||
|
||||
var cmd = "!r ";
|
||||
|
||||
/**
|
||||
* Gets the character the player is currently speaking as.
|
||||
* @param {String} playerId The player's ID.
|
||||
* @return {Character}
|
||||
*/
|
||||
function getCharacter(playerId) {
|
||||
var player = findObjs({
|
||||
_type: 'player',
|
||||
_id: playerId
|
||||
})[0];
|
||||
|
||||
log(player);
|
||||
|
||||
var speakingAs = player.get('speakingas') || player.get('_displayname');
|
||||
if(speakingAs.indexOf('player') === 0)
|
||||
throw new Error('You are not currently speaking as a character.');
|
||||
else if(speakingAs.indexOf('character') === 0) {
|
||||
var characterId = speakingAs.replace('character|', '');
|
||||
log(speakingAs, characterId);
|
||||
return findObjs({
|
||||
_type: 'character',
|
||||
_id: characterId
|
||||
})[0];
|
||||
}
|
||||
else {
|
||||
var character = findObjs({
|
||||
_type: 'character',
|
||||
name: speakingAs
|
||||
})[0];
|
||||
if(character)
|
||||
return character;
|
||||
else
|
||||
throw new Error('Bad speakingas value: ' + speakingAs);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets all the skill attributes for a character.
|
||||
* @param {Character} character
|
||||
* @return {Attribute[]}
|
||||
*/
|
||||
function getAllSkills(character) {
|
||||
return _.filter(findObjs({
|
||||
_type: "attribute",
|
||||
_characterid: character.id
|
||||
}), function(attr) {
|
||||
return (attr.get("name").indexOf("skill") !== -1);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets information about a skill.
|
||||
* @param {Character} character
|
||||
* @param {String} name The name of the skill.
|
||||
* @return {Object}
|
||||
*/
|
||||
function getSkill(character, name) {
|
||||
var skills = getAllSkills(character);
|
||||
var skill = _.find(skills, function(attr) {
|
||||
var value = getSkillName(attr);
|
||||
return (value.indexOf(name) !== -1);
|
||||
});
|
||||
|
||||
if(skill) {
|
||||
var name = getSkillName(skill);
|
||||
var toks = splitSkill(skill);
|
||||
var index = toks[0];
|
||||
var type = toks[1];
|
||||
|
||||
var trained = (parseInt(getSkillField(skills, type, index, "T")) == 1);
|
||||
var improved = (parseInt(getSkillField(skills, type, index, "I")) == 1);
|
||||
var greater = (parseInt(getSkillField(skills, type, index, "G")) == 1);
|
||||
var bonus = parseInt(getSkillField(skills, type, index, "Misc")) || 0;
|
||||
var notes = getSkillField(skills, type, index, 'Conds') || '';
|
||||
|
||||
var adv = parseInt(getSkillField(skills, type, index, 'Adv')) || 0;
|
||||
var dis = parseInt(getSkillField(skills, type, index, 'Dis')) || 0;
|
||||
var advDis = adv - dis;
|
||||
|
||||
var attr = {};
|
||||
if(type === "skillM")
|
||||
attr.name = "mind";
|
||||
if(type === "skillB")
|
||||
attr.name = "body";
|
||||
if(type === "skillH")
|
||||
attr.name = "heart";
|
||||
|
||||
attr.value = getAttrByName(character.id, attr.name);
|
||||
|
||||
return {
|
||||
name: name,
|
||||
attr: attr,
|
||||
trained: trained,
|
||||
improved: improved,
|
||||
greater: greater,
|
||||
advDis: advDis,
|
||||
bonus: bonus,
|
||||
notes: notes
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* [splitSkill description]
|
||||
* @param {[type]} skill
|
||||
* @return {[type]}
|
||||
*/
|
||||
function splitSkill(skill) {
|
||||
var toks = skill.get("name").split("_");
|
||||
return [toks[2], toks[3]];
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the value of the attribute for a skill's name.
|
||||
*/
|
||||
function getSkillName(skill) {
|
||||
return skill.get("current").toLowerCase();
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts a field value for a skill.
|
||||
*/
|
||||
function getSkillField(skills, type, index, field) {
|
||||
var field = _.find(skills, function(skill) {
|
||||
var toks = splitSkill(skill);
|
||||
return (toks[0] === index && toks[1] === (type + field));
|
||||
});
|
||||
if(field)
|
||||
return field.get("current");
|
||||
else
|
||||
return undefined;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Parses the Advantage/Disadvantage total from an expression of
|
||||
* of +N advantages and -N disadvantages. E.g. "+2 -1 + 4"
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function parseAdvDis(expr) {
|
||||
if(!expr)
|
||||
return 0;
|
||||
|
||||
expr = expr.replace(' ', '');
|
||||
var total = 0;
|
||||
var regex = /([+]|-)(\d+)/g
|
||||
|
||||
// Get the first match.
|
||||
var match = regex.exec(expr);
|
||||
while(match) {
|
||||
if(match[1] === '+')
|
||||
total += parseInt(match[2]);
|
||||
else
|
||||
total -= parseInt(match[2]);
|
||||
|
||||
// Get the next match.
|
||||
match = regex.exec(expr);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* An object representing a skillcheck.
|
||||
* @typedef {object} SkillCheck
|
||||
* @property {string} skillName The name of the skill, or what its name starts with.
|
||||
* @property {int} advDis The total Advantage/Disadvantage modifier.
|
||||
* @property {string} note A string appended to the roll's notes.
|
||||
*/
|
||||
|
||||
/**
|
||||
* An object representing a skill and its attributes.
|
||||
* @typedef {object} Skill
|
||||
* @property {string} name
|
||||
* @property {SkillAttr} attr
|
||||
* @property {boolean} trained
|
||||
* @property {boolean} improved
|
||||
* @property {boolean} greater
|
||||
* @property {int} advDis
|
||||
* @property {int} bonus
|
||||
* @property {string} notes
|
||||
*/
|
||||
|
||||
/**
|
||||
* The attribute used for a skillcheck.
|
||||
* @typedef {object} SkillAttr
|
||||
* @property {string} name
|
||||
* @property {int} value
|
||||
*/
|
||||
|
||||
/**
|
||||
* Rolls a skill check for a character using the skillcheck template.
|
||||
* @param {Character} character
|
||||
* @param {SkillCheck} skillCheck
|
||||
*/
|
||||
function rollSkillCheck(character, skillCheck) {
|
||||
var charName = character.get('name');
|
||||
|
||||
var skill = getSkill(character, skillCheck.skillName);
|
||||
|
||||
var notes = skill.notes;
|
||||
if(skillCheck.note) {
|
||||
if(skill.notes)
|
||||
notes += '<br>'
|
||||
notes += skillCheck.note;
|
||||
}
|
||||
var advDis = skill.advDis + skillCheck.advDis;
|
||||
if(advDis >= 0)
|
||||
advDis = '+' + advDis;
|
||||
|
||||
var training = 'untrained';
|
||||
var dice = '2d6';
|
||||
if(skill.greater) {
|
||||
training = 'greater';
|
||||
dice = '4d6d2';
|
||||
}
|
||||
else if(skill.improved) {
|
||||
training = 'improved';
|
||||
dice = '4d6d2';
|
||||
}
|
||||
else if(skill.trained) {
|
||||
training = 'trained';
|
||||
dice = '3d6d1';
|
||||
}
|
||||
|
||||
var roll = '{{ ' + dice + ' ' + advDis + ', 12 + 1d0}kl1, 2 + 1d0}kh1 ';
|
||||
if(skill.greater) {
|
||||
roll += '+1 [G] ';
|
||||
}
|
||||
roll += ' +' + skill.attr.value + '[' + skill.attr.name + '] + ' + skill.bonus;
|
||||
|
||||
var templateStr = '&{template:skillcheck} {{charName=' + charName + '}} ';
|
||||
templateStr += '{{skillName=' + skillCheck.skillName + '}} {{result=[[' + roll + ']]}} ';
|
||||
templateStr += '{{' + training + '=true}} ';
|
||||
if(notes) {
|
||||
templateStr += '{{notes=' + notes + '}}';
|
||||
}
|
||||
|
||||
sendChat(character.get('name'), templateStr);
|
||||
}
|
||||
|
||||
on("chat:message", function(msg) {
|
||||
try {
|
||||
if(msg.type == "api" && msg.content.indexOf(cmd) !== -1) {
|
||||
var playerId = msg.playerid;
|
||||
var character = getCharacter(playerId);
|
||||
var str = msg.content.replace(cmd, "");
|
||||
|
||||
// Process the roll command as a regular expression.
|
||||
//
|
||||
// group 1 is the skill name.
|
||||
// group 2 is the advantage/disadvantage modifier.
|
||||
// group 6 is a string appended to the notes for the roll.
|
||||
var regex = /([^+\-\\"]+)(( *([+]|-) *\d+)*)? *("(.*?)")?/;
|
||||
var match = regex.exec(str);
|
||||
|
||||
var skillName = match[1].trim().toLowerCase();
|
||||
var advDis = parseAdvDis(match[2]);
|
||||
var note = match[6];
|
||||
|
||||
if(match) {
|
||||
var skillCheck = {
|
||||
skillName: skillName,
|
||||
advDis: advDis,
|
||||
note: note
|
||||
};
|
||||
rollSkillCheck(character, skillCheck);
|
||||
}
|
||||
else
|
||||
throw new Error('Bad roll format. Expected format: {skill name} [+/- Advantage/Disadvantage modifier] ["any notes about the roll"]');
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
sendChat("ERROR", "/w " + msg.who + " Error processing roll: " + msg.content);
|
||||
log('MLP Dice ERROR: ' + err.message);
|
||||
}
|
||||
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,280 @@
|
||||
.charsheet {
|
||||
|
||||
}
|
||||
|
||||
.charsheet .sheet-everything {
|
||||
background-image: url("https://sites.google.com/site/roleplayingismagichome/_/rsrc/1385907608270/config/WebsiteBackground_Rough.png.1385907608112.png");
|
||||
height: 90%;
|
||||
position: absolute;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.charsheet .sheet-banner {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
.charsheet .sheet-banner img.sheet-mlprimLogo {
|
||||
height: 0.75in;
|
||||
position: absolute;
|
||||
right: 0.5in;
|
||||
}
|
||||
|
||||
.charsheet .sheet-tabsContainer {
|
||||
bottom: 0.5in;
|
||||
left: 0.5in;
|
||||
min-height: 5.5in;
|
||||
min-width: 7in;
|
||||
position: absolute;
|
||||
right: 0.5in;
|
||||
top: 1in;
|
||||
}
|
||||
.charsheet .sheet-tab {
|
||||
float: left;
|
||||
width: 16%;
|
||||
height: 32px;
|
||||
}
|
||||
.charsheet .sheet-tab .sheet-tabInput {
|
||||
position: absolute;
|
||||
top: -9999px;
|
||||
}
|
||||
.charsheet .sheet-tab > label {
|
||||
background: #82c;
|
||||
border-top-left-radius: 16px;
|
||||
border-top-right-radius: 16px;
|
||||
height: 22px;
|
||||
margin: 0;
|
||||
padding: 10px 0 0 0;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
width: 100%;
|
||||
}
|
||||
.charsheet .sheet-tab > label {
|
||||
cursor: pointer;
|
||||
}
|
||||
.charsheet .sheet-tab > input:checked + label {
|
||||
background: #a5f;
|
||||
}
|
||||
.charsheet .sheet-tab .sheet-tabPanel {
|
||||
background: #404;
|
||||
border: 4px solid #a5f;
|
||||
border-radius: 10px;
|
||||
color: #fff;
|
||||
display: none;
|
||||
height: 80%;
|
||||
left: -32px;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
position: absolute;
|
||||
top: 32px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.charsheet .sheet-tab input:checked + label + .sheet-tabPanel {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
|
||||
.charsheet button {
|
||||
background: #a5f;
|
||||
border-color: #82c;
|
||||
color: #fff;
|
||||
padding: 4px;
|
||||
}
|
||||
.charsheet input,select,textarea {
|
||||
background: #000;
|
||||
border: none;
|
||||
color: #0ff;
|
||||
}
|
||||
.charsheet input[type=number]::-webkit-inner-spin-button,
|
||||
.charsheet input[type=number]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
.charsheet textarea {
|
||||
height: auto;
|
||||
resize: none;
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
.charsheet .sheet-fraction {
|
||||
display: inline;
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.charsheet h2 {
|
||||
border-bottom: 1px solid #a5f;
|
||||
color: #fff;
|
||||
line-height: normal;
|
||||
margin: 0 0 5px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.charsheet .sheet-hLayout > div {
|
||||
display: inline;
|
||||
}
|
||||
.charsheet .sheet-vLayout > div {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.charsheet .sheet-field label {
|
||||
color: white;
|
||||
display: inline-block;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.charsheet .sheet-column {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.charsheet #sheet-mainPanel input[type=text],select {
|
||||
max-width: 150px;
|
||||
}
|
||||
.charsheet #sheet-mainPanel .sheet-attrBox {
|
||||
margin: auto;
|
||||
width: 150px;
|
||||
}
|
||||
.charsheet #sheet-mainPanel .sheet-attrBox label {
|
||||
text-align: right;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.charsheet #sheet-skillsPanel .repitem {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.charsheet #sheet-edgesPanel .repitem {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.charsheet .sheet-rankBox {
|
||||
border: 1px solid #a5f;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
height: 16px;
|
||||
text-align: center;
|
||||
width: 16px;
|
||||
}
|
||||
.charsheet .sheet-rankBox input{
|
||||
height: 100%;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
.charsheet .sheet-rankBox input + div{
|
||||
background: #000;
|
||||
color: #a5f;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.charsheet .sheet-rankBox input:checked + div{
|
||||
background: #a5f;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.charsheet .sheet-expandBox {
|
||||
display: inline-block;
|
||||
left: 1em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.charsheet #sheet-magicPanel .sheet-spellBody{
|
||||
border-bottom: 1px solid #a5f;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.charsheet .sheet-conditionalModifiers {
|
||||
margin-left: 1em;
|
||||
margin-top: 0.25em;
|
||||
width: 90%;
|
||||
}
|
||||
.charsheet .sheet-conditionalModifiers input {
|
||||
padding: 1px;
|
||||
width: 50%;
|
||||
}
|
||||
.charsheet .sheet-conditionalModifiers * {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.charsheet ::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.charsheet ::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.charsheet ::-webkit-scrollbar-thumb {
|
||||
background: #a5f;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.charsheet .sheet-readOnlyField {
|
||||
background: #000;
|
||||
border-radius: 3px;
|
||||
color: #08f;
|
||||
cursor: not-allowed;
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
width: 3em;
|
||||
}
|
||||
|
||||
|
||||
/* roll template styles */
|
||||
|
||||
.sheet-rolltemplate-skillcheck table {
|
||||
background: #404;
|
||||
border: 2px solid #a5f;
|
||||
border-radius: 1em;
|
||||
color: #fff;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sheet-rolltemplate-skillcheck table thead th {
|
||||
background: #a5f;
|
||||
color: #000;
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
|
||||
.sheet-rolltemplate-skillcheck table tbody {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sheet-rolltemplate-skillcheck table tbody tr td{
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
|
||||
.sheet-rolltemplate-skillcheck table tbody .sheet-rollNotes {
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.sheet-rolltemplate-skillcheck table tbody .sheet-trainingLevel {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sheet-rolltemplate-skillcheck table tbody .sheet-untrained {
|
||||
color: #118844;
|
||||
}
|
||||
|
||||
.sheet-rolltemplate-skillcheck table tbody .sheet-trained {
|
||||
color: #2f8;
|
||||
}
|
||||
|
||||
.sheet-rolltemplate-skillcheck table tbody .sheet-improved {
|
||||
color: #2cf;
|
||||
}
|
||||
|
||||
.sheet-rolltemplate-skillcheck table tbody .sheet-greater {
|
||||
color: #fc4;
|
||||
}
|
||||
|
||||
.sheet-rolltemplate-skillcheck table tbody tr td .inlinerollresult {
|
||||
background: #000;
|
||||
}
|
||||
@@ -0,0 +1,738 @@
|
||||
<div class="everything">
|
||||
<div class="banner"><div class="space"></div><img class="mlprimLogo" src="https://sites.google.com/site/roleplayingismagichome/_/rsrc/1404003237260/0-0/RiM-Logo-Redesign.png" /></div>
|
||||
<div class="tabsContainer">
|
||||
|
||||
<div class="tab">
|
||||
<input id="sheet-mainRadio" type="radio" class="tabInput" name="tab" checked />
|
||||
<label for="sheet-mainRadio">Main</label>
|
||||
<div id="mainPanel" class="tabPanel">
|
||||
<h2>General</h2>
|
||||
|
||||
<div class='column vLayout' style="width: 50%;">
|
||||
<div class="field">
|
||||
<label>Name:</label>
|
||||
<input type="text" name="attr_name">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Race:</label>
|
||||
<input type="text" name="attr_race">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Guiding Element:</label>
|
||||
<select name="attr_guiding_elem">
|
||||
<option value="def">Select</option>
|
||||
<option value="generosity">Generosity</option>
|
||||
<option value="honesty">Honesty</option>
|
||||
<option value="kindness">Kindness</option>
|
||||
<option value="laughter">Laughter</option>
|
||||
<option value="loyalty">Loyalty</option>
|
||||
<option value="magic">Magic</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Fatal Flaw:</label>
|
||||
<input type="text" name="attr_flaw">
|
||||
</div>
|
||||
</div>
|
||||
<div class="column vLayout">
|
||||
<div class="field">
|
||||
<label>XP Spent:</label>
|
||||
<span class="readOnlyField" name="attr_xpSpent"></span>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Fortitude:</label>
|
||||
<input type="number" name="attr_fortitude"><div class="fraction">/</div>
|
||||
<span class="readOnlyField" name="attr_fortitude_max"></span>
|
||||
Modifiers: <input type="number" name="attr_fortitudeOtherMods" value="0" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Willpower:</label>
|
||||
<input type="number" name="attr_willpower"><div class="fraction">/</div>
|
||||
<span class="readOnlyField" name="attr_willpower_max"></span>
|
||||
Modifiers: <input type="number" name="attr_willpowerOtherMods" value="0" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Valor:</label>
|
||||
<input type="number" name="attr_valor"><div class="fraction">/</div>
|
||||
<span class="readOnlyField" name="attr_valor_max"></span>
|
||||
Modifiers: <input type="number" name="attr_valorOtherMods" value="0" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Attributes</h2>
|
||||
<table width="100%">
|
||||
<tr>
|
||||
<td>
|
||||
<div class="attrBox field">
|
||||
<label>MIND</label>
|
||||
<input type="number" name="attr_mind">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="attrBox field">
|
||||
<label>BODY</label>
|
||||
<input type="number" name="attr_body">
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="attrBox field">
|
||||
<label>HEART</label>
|
||||
<input type="number" name="attr_heart">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h2>Description</h2>
|
||||
<textarea rows="3" name="attr_description"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="tab">
|
||||
<input id="skillsRadio" type="radio" class="tabInput" name="tab" />
|
||||
<label for="skillsRadio">Skills</label>
|
||||
<div id="skillsPanel" class="tabPanel scrollingY">
|
||||
<h2>MIND Skills</h2>
|
||||
<fieldset name="skillsMind" class="repeating_skillsMind">
|
||||
<div>
|
||||
Name: <input type="text" name="attr_skillM">
|
||||
Skill Training: <div class="rankBox trained" title="Trained">
|
||||
<input type="checkbox" name="attr_skillMT" value="1">
|
||||
<div>T</div>
|
||||
</div>
|
||||
<div class="rankBox improved" title="Improved">
|
||||
<input type="checkbox" name="attr_skillMI" value="1">
|
||||
<div>I</div>
|
||||
</div>
|
||||
<div class="rankBox greater" title="Greater">
|
||||
<input type="checkbox" name="attr_skillMG" value="1">
|
||||
<div>G</div>
|
||||
</div>
|
||||
Modifier: <input class="skillMod" type="number" name="attr_skillMMisc" value="0" title="Misc. Modifier"/>
|
||||
<button type="roll" value="!r @{skillM}"></button>
|
||||
</div>
|
||||
<div class="conditionalModifiers">
|
||||
<span>Conditional Modifiers:</span>
|
||||
<input type="text" name="attr_skillMconditionals">
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<h2>BODY Skills</h2>
|
||||
<fieldset name="skillsBody" class="repeating_skillsBody">
|
||||
<div>
|
||||
Name: <input type="text" name="attr_skillB">
|
||||
Skill Training: <div class="rankBox trained" title="Trained">
|
||||
<input type="checkbox" name="attr_skillBT" value="1">
|
||||
<div>T</div>
|
||||
</div>
|
||||
<div class="rankBox improved" title="Improved">
|
||||
<input type="checkbox" name="attr_skillBI value="1"">
|
||||
<div>I</div>
|
||||
</div>
|
||||
<div class="rankBox greater" title="Greater">
|
||||
<input type="checkbox" name="attr_skillBG" value="1">
|
||||
<div>G</div>
|
||||
</div>
|
||||
Modifier: <input class="skillMod" type="number" name="attr_skillBMisc" value="0" title="Misc. Modifier"/>
|
||||
<button type="roll" value="!r @{skillB}"></button>
|
||||
</div>
|
||||
<div class="conditionalModifiers">
|
||||
<span>Conditional Modifiers:</span>
|
||||
<input type="text" name="attr_skillBconditionals">
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<h2>HEART Skills</h2>
|
||||
<fieldset name="skillsHeart" class="repeating_skillsHeart">
|
||||
<div>
|
||||
Name: <input type="text" name="attr_skillH">
|
||||
Skill Training: <div class="rankBox trained" title="Trained">
|
||||
<input type="checkbox" name="attr_skillHT" value="1">
|
||||
<div>T</div>
|
||||
</div>
|
||||
<div class="rankBox improved" title="Improved">
|
||||
<input type="checkbox" name="attr_skillHI" value="1">
|
||||
<div>I</div>
|
||||
</div>
|
||||
<div class="rankBox greater" title="Greater">
|
||||
<input type="checkbox" name="attr_skillHG" value="1">
|
||||
<div>G</div>
|
||||
</div>
|
||||
Modifier: <input class="skillMod" type="number" name="attr_skillHMisc" value="0" title="Misc. Modifier"/>
|
||||
<button type="roll" value="!r @{skillH}"></button>
|
||||
</div>
|
||||
<div class="conditionalModifiers">
|
||||
<span>Conditional Modifiers:</span>
|
||||
<input type="text" name="attr_skillHconditionals">
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="tab">
|
||||
<input id="edgesRadio" type="radio" class="tabInput" name="tab" />
|
||||
<label for="edgesRadio">Edges</label>
|
||||
<div id="edgesPanel" class="tabPanel">
|
||||
<p>
|
||||
This section is for all edges other than Skill Training. Those go over in the Skills section.
|
||||
</p>
|
||||
<fieldset name="edges" class="repeating_edges">
|
||||
Name: <input type="text" name="attr_edgeName" style="width: 100px;"/>
|
||||
Effect: <input type="text" name="attr_edgeEffect" />
|
||||
Edge rank:
|
||||
<div class="rankBox trained" title="Trained">
|
||||
<input type="checkbox" name="attr_edgeT" value="1">
|
||||
<div>T</div>
|
||||
</div>
|
||||
<div class="rankBox trained" title="Improved">
|
||||
<input type="checkbox" name="attr_edgeI" value="1">
|
||||
<div>I</div>
|
||||
</div>
|
||||
<div class="rankBox trained" title="Greater">
|
||||
<input type="checkbox" name="attr_edgeG" value="1">
|
||||
<div>G</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="tab">
|
||||
<input id="magicRadio" type="radio" class="tabInput" name="tab" />
|
||||
<label for="magicRadio">Magic</label>
|
||||
<div id="magicPanel" class="tabPanel">
|
||||
<fieldset name="spells" class="repeating_spells">
|
||||
<div class="panel">
|
||||
<div style="display: inline-block;">
|
||||
<div class="rankBox" title="Prepared?">
|
||||
<input type="checkbox" name="attr_spellPrepared"/>
|
||||
<div>P</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: inline-block; vertical-align: text-top; width: 90%;">
|
||||
<h2>
|
||||
<input type="text" name="attr_spellName" placeholder="Spell name"/>
|
||||
Lv. <input type="number" name="attr_spellLevel" disabled="true"
|
||||
value="(@{spellTarget} + @{spellRange} + @{spellDuration} + @{spellEffectsTotal} + @{spellSubjectsTotal})"/>
|
||||
</h2>
|
||||
<div class="spellBody">
|
||||
Target: <select name="attr_spellTarget" style="width: 100px;">
|
||||
<option value=1>Individual</option>
|
||||
<option value=2>Group</option>
|
||||
<option value=4>Area</option>
|
||||
<option value=8>Mass</option>
|
||||
</select>
|
||||
Range: <select name="attr_spellRange" style="width: 100px;">
|
||||
<option value=1>Contact</option>
|
||||
<option value=2>Seen</option>
|
||||
<option value=4>Known</option>
|
||||
<option value=8>Unknown</option>
|
||||
</select>
|
||||
Duration: <select name="attr_spellDuration" style="width: 100px;">
|
||||
<option value=1>Immediate</option>
|
||||
<option value=2>Sustained</option>
|
||||
<option value=4>Temporary</option>
|
||||
<option value=8>Persistent</option>
|
||||
</select> <br/>
|
||||
<div style="display: inline-block;">
|
||||
Effects:
|
||||
</div>
|
||||
<div style="display: inline-block; margin-bottom: 10px; vertical-align: text-top;">
|
||||
<input type="hidden" name="attr_spellEffectsTotal" disabled="true"
|
||||
value="(@{spellAnimate} + @{spellCombine} + @{spellDeceive} + @{spellDiminish} +
|
||||
@{spellForge} + @{spellModify} + @{spellReveal} + @{spellSeparate})"/>
|
||||
<table class="layoutTable">
|
||||
<tr>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellAnimate" value="1"/>
|
||||
<div> </div>
|
||||
</div> Animate
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellCombine" value="1"/>
|
||||
<div> </div>
|
||||
</div> Combine
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellDeceive" value="1"/>
|
||||
<div> </div>
|
||||
</div> Deceive
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellDiminish" value="1"/>
|
||||
<div> </div>
|
||||
</div> Diminish
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellForge" value="1"/>
|
||||
<div> </div>
|
||||
</div> Forge
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellModify" value="1"/>
|
||||
<div> </div>
|
||||
</div> Modify
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellReveal" value="1"/>
|
||||
<div> </div>
|
||||
</div> Reveal
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellSeparate" value="1"/>
|
||||
<div> </div>
|
||||
</div> Separate
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div> <br/>
|
||||
<div style="display: inline-block;">
|
||||
Subjects:
|
||||
</div>
|
||||
<div style="display: inline-block; margin-bottom: 10px; vertical-align: text-top;">
|
||||
<input type="hidden" name="attr_spellSubjectsTotal" disabled="true"
|
||||
value="(@{spellAir} + @{spellAnimal} + @{spellBody} + @{spellConstruct} + @{spellEarth} +
|
||||
@{spellEnergy} + @{spellForce} + @{spellHeat} + @{spellLight} + @{spellMagic} +
|
||||
@{spellMind} + @{spellPlant} + @{spellShadow} + @{spellSound} + @{spellSpace} +
|
||||
@{spellTime} + @{spellWater} + @{spellWeather})"/>
|
||||
<table class="layoutTable">
|
||||
<tr>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellAir" value="1"/>
|
||||
<div> </div>
|
||||
</div> Air
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellAnimal" value="1"/>
|
||||
<div> </div>
|
||||
</div> Animal
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellBody" value="1"/>
|
||||
<div> </div>
|
||||
</div> Body
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellConstruct" value="1"/>
|
||||
<div> </div>
|
||||
</div> Construct
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellEarth" value="1"/>
|
||||
<div> </div>
|
||||
</div> Earth
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellEnergy" value="1"/>
|
||||
<div> </div>
|
||||
</div> Energy
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellForce" value="1"/>
|
||||
<div> </div>
|
||||
</div> Force
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellHeat" value="1"/>
|
||||
<div> </div>
|
||||
</div> Heat
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellLight" value="1"/>
|
||||
<div> </div>
|
||||
</div> Light
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellMagic" value="1"/>
|
||||
<div> </div>
|
||||
</div> Magic
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellMind" value="1"/>
|
||||
<div> </div>
|
||||
</div> Mind
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellPlant" value="1"/>
|
||||
<div> </div>
|
||||
</div> Plant
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellShadow" value="1"/>
|
||||
<div> </div>
|
||||
</div> Shadow
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellSound" value="1"/>
|
||||
<div> </div>
|
||||
</div> Sound
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellSpace" value="1"/>
|
||||
<div> </div>
|
||||
</div> Space
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellTime" value="1"/>
|
||||
<div> </div>
|
||||
</div> Time
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellWater" value="1"/>
|
||||
<div> </div>
|
||||
</div> Water
|
||||
</td>
|
||||
<td>
|
||||
<div class="rankBox">
|
||||
<input type="checkbox" name="attr_spellWeather" value="1"/>
|
||||
<div> </div>
|
||||
</div> Weather
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div> <!-- End effects and subjects -->
|
||||
|
||||
<textarea name="attr_spellDescription" rows="3" placeholder="Spell description"></textarea>
|
||||
</div> <!-- End spellBody -->
|
||||
</div>
|
||||
</div> <!-- End panel -->
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="tab">
|
||||
<input id="lootRadio" type="radio" class="tabInput" name="tab" />
|
||||
<label for="lootRadio">Loot</label>
|
||||
<div id="lootPanel" class="tabPanel">
|
||||
<div class="field money">
|
||||
<label>Bits</label>
|
||||
<input type="number" name="attr_money">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Equipped</label>
|
||||
<textarea rows="5" name="attr_equipped"></textarea>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Inventory</label>
|
||||
<textarea rows="9" name="attr_inventory"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<input id="notesRadio" type="radio" class="tabInput" name="tab" />
|
||||
<label for="notesRadio">Notes</label>
|
||||
<div id="notesPanel" class="tabPanel">
|
||||
<textarea rows="20" name="attr_notes"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span name='attr_debug' style="display:none;"></span>
|
||||
|
||||
<script type="text/worker">
|
||||
|
||||
|
||||
var Asem = function(initCount, cb) {
|
||||
this.count = initCount;
|
||||
this.cb = cb;
|
||||
this.hasFired = false;
|
||||
};
|
||||
_.extend(Asem.prototype, {
|
||||
signal: function() {
|
||||
this.count--;
|
||||
if(this.count <= 0 && !this.hasFired) {
|
||||
this.hasFired = true;
|
||||
this.cb();
|
||||
}
|
||||
},
|
||||
|
||||
wait: function() {
|
||||
this.count++;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Gets the specified attributes and parses them as an integer or 0.
|
||||
*/
|
||||
function parseAttrs(attrs, cb) {
|
||||
getAttrs(attrs, function(values) {
|
||||
_.each(attrs, function(attr) {
|
||||
values[attr] = parseInt(values[attr]) || 0;
|
||||
});
|
||||
cb(values);
|
||||
});
|
||||
}
|
||||
|
||||
function updateFortMax() {
|
||||
parseAttrs(['body', 'heart', 'edgeVitality', 'edgeVitalityI', 'edgeVitalityG', 'fortitudeOtherMods', 'fortitudeMaxMods'], function(values) {
|
||||
var fortMax = 10*values.body +
|
||||
5*(values.heart + values.edgeVitality + values.edgeVitalityI + values.edgeVitalityG) +
|
||||
values.fortitudeOtherMods;
|
||||
|
||||
setAttrs({
|
||||
fortitude_max: fortMax
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function updateWillMax() {
|
||||
parseAttrs(['mind', 'heart', 'edgeDetermined', 'edgeDeterminedI', 'edgeDeterminedG', 'willpowerOtherMods'], function(values) {
|
||||
var willMax = 10*values.mind +
|
||||
5*(values.heart + values.edgeDetermined + values.edgeDeterminedI + values.edgeDeterminedG) +
|
||||
values.willpowerOtherMods;
|
||||
|
||||
setAttrs({
|
||||
willpower_max: willMax
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function updateValorMax() {
|
||||
parseAttrs(['heart', 'edgeNobleSoul', 'edgeNobleSoulI', 'edgeNobleSoulG', 'valorOtherMods'], function(values) {
|
||||
var valorMax = values.heart + values.edgeNobleSoul + values.edgeNobleSoulI + values.edgeNobleSoulG +
|
||||
values.valorOtherMods;
|
||||
|
||||
setAttrs({
|
||||
valor_max: valorMax
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function updateHealthGraphic() {
|
||||
parseAttrs(['fortitude', 'fortitude_max', 'willpower', 'willpower_max'], function(values) {
|
||||
if(values.fortitude_max === 0 || values.willpower_max === 0) {
|
||||
setAttrs({
|
||||
statusImage: 'healthy'
|
||||
});
|
||||
}
|
||||
else {
|
||||
var alpha = Math.min(values.fortitude/values.fortitude_max, values.willpower/values.willpower_max);
|
||||
if(alpha > 0.5) {
|
||||
setAttrs({
|
||||
statusImage: 'healthy'
|
||||
});
|
||||
}
|
||||
else if(alpha > 0.25) {
|
||||
setAttrs({
|
||||
statusImage: 'bloodied'
|
||||
});
|
||||
}
|
||||
else if(alpha > 0) {
|
||||
setAttrs({
|
||||
statusImage: 'critical'
|
||||
});
|
||||
}
|
||||
else {
|
||||
setAttrs({
|
||||
statusImage: 'sidelined'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateXpSpent() {
|
||||
var xpSpent = 0;
|
||||
|
||||
var asem = new Asem(1, function() {
|
||||
setAttrs({
|
||||
xpSpent: xpSpent
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Mind skill training
|
||||
getSectionIDs('repeating_skillsMind', function(ids) {
|
||||
_.each(ids, function(id) {
|
||||
var baseAttr = 'repeating_skillsMind_' + id +'_';
|
||||
parseAttrs([baseAttr + 'skillMT', baseAttr + 'skillMI', baseAttr + 'skillMG'], function(values) {
|
||||
xpSpent += 5*values[baseAttr + 'skillMT'] + 10*values[baseAttr + 'skillMI'] + 15*values[baseAttr + 'skillMG'];
|
||||
asem.signal();
|
||||
});
|
||||
asem.wait();
|
||||
})
|
||||
asem.signal();
|
||||
});
|
||||
asem.wait();
|
||||
|
||||
|
||||
// Body skill training
|
||||
getSectionIDs('repeating_skillsBody', function(ids) {
|
||||
_.each(ids, function(id) {
|
||||
var baseAttr = 'repeating_skillsBody_' + id +'_';
|
||||
parseAttrs([baseAttr + 'skillBT', baseAttr + 'skillBI', baseAttr + 'skillBG'], function(values) {
|
||||
xpSpent += 5*values[baseAttr + 'skillBT'] + 10*values[baseAttr + 'skillBI'] + 15*values[baseAttr + 'skillBG'];
|
||||
asem.signal();
|
||||
});
|
||||
asem.wait();
|
||||
})
|
||||
asem.signal();
|
||||
});
|
||||
asem.wait();
|
||||
|
||||
// Heart skill training
|
||||
getSectionIDs('repeating_skillsHeart', function(ids) {
|
||||
_.each(ids, function(id) {
|
||||
var baseAttr = 'repeating_skillsHeart_' + id +'_';
|
||||
parseAttrs([baseAttr + 'skillHT', baseAttr + 'skillHI', baseAttr + 'skillHG'], function(values) {
|
||||
xpSpent += 5*values[baseAttr + 'skillHT'] + 10*values[baseAttr + 'skillHI'] + 15*values[baseAttr + 'skillHG'];
|
||||
asem.signal();
|
||||
});
|
||||
asem.wait();
|
||||
})
|
||||
asem.signal();
|
||||
});
|
||||
asem.wait();
|
||||
|
||||
// Built-in Edges
|
||||
parseAttrs(['edgeDetermined', 'edgeDeterminedI', 'edgeDeterminedG',
|
||||
'edgeVitality', 'edgeVitalityI', 'edgeVitalityG',
|
||||
'edgeNobleSoul', 'edgeNobleSoulI', 'edgeNobleSoulG'], function(values) {
|
||||
xpSpent += 5*values.edgeDetermined + 10*values.edgeDeterminedI + 15*values.edgeDeterminedG;
|
||||
xpSpent += 5*values.edgeVitality + 10*values.edgeVitalityI + 15*values.edgeVitalityG;
|
||||
xpSpent += 5*values.edgeNobleSoul + 10*values.edgeNobleSoulI + 15*values.edgeNobleSoulG;
|
||||
|
||||
asem.signal();
|
||||
});
|
||||
asem.wait();
|
||||
|
||||
// Custom edges
|
||||
getSectionIDs('repeating_edges', function(ids) {
|
||||
_.each(ids, function(id) {
|
||||
var baseAttr = 'repeating_edges_' + id + '_';
|
||||
parseAttrs([baseAttr + 'edgeT', baseAttr + 'edgeI', baseAttr + 'edgeG'], function(values) {
|
||||
setAttrs({
|
||||
debug: JSON.stringify(values)
|
||||
});
|
||||
|
||||
xpSpent += 5*values[baseAttr + 'edgeT'] + 10*values[baseAttr + 'edgeI'] + 15*values[baseAttr + 'edgeG'];
|
||||
asem.signal();
|
||||
});
|
||||
asem.wait();
|
||||
});
|
||||
asem.signal();
|
||||
});
|
||||
asem.wait();
|
||||
|
||||
asem.signal();
|
||||
}
|
||||
|
||||
on('change:body change:heart change:edgeVitality change:edgeVitalityI change:edgeVitalityG change:fortitudeOtherMods', function() {
|
||||
updateFortMax();
|
||||
});
|
||||
|
||||
on('change:mind change:heart change:edgeDetermined change:edgeDeterminedI change:edgeDeterminedG change:willpowerOtherMods', function() {
|
||||
updateWillMax();
|
||||
});
|
||||
|
||||
on('change:heart change:edgeNobleSoul change:edgeNobleSoulI change:edgeNobleSoulG change:valorOtherMods', function() {
|
||||
updateValorMax();
|
||||
});
|
||||
|
||||
|
||||
on('change:repeating_skillsMind:skillMT change:repeating_skillsMind:skillMI change:repeating_skillsMind:skillMG ' +
|
||||
'change:repeating_skillsBody:skillBT change:repeating_skillsBody:skillBI change:repeating_skillsBody:skillBG ' +
|
||||
'change:repeating_skillsHeart:skillHT change:repeating_skillsHeart:skillHI change:repeating_skillsHeart:skillHG ' +
|
||||
'change:edgeDetermined change:edgeDeterminedI change:edgeDeterminedG ' +
|
||||
'change:edgeVitality change:edgeVitalityI change:edgeVitalityG ' +
|
||||
'change:edgeNobleSoul change:edgeNobleSoulI change:edgeNobleSoulG ' +
|
||||
'change:repeating_edges:edgeT change:repeating_edges:edgeI change:repeating_edges:edgeG', function() {
|
||||
|
||||
updateXpSpent();
|
||||
});
|
||||
|
||||
|
||||
on('sheet:opened', function() {
|
||||
updateFortMax();
|
||||
updateWillMax();
|
||||
updateValorMax();
|
||||
updateXpSpent();
|
||||
});
|
||||
</script>
|
||||
|
||||
<rolltemplate class="sheet-rolltemplate-skillcheck">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
{{charName}} ⇒ {{skillName}}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="trainingLevel">
|
||||
{{#untrained}}
|
||||
<td class="untrained">Untrained</td>
|
||||
{{/untrained}}
|
||||
{{#trained}}
|
||||
<td class="trained">Trained</td>
|
||||
{{/trained}}
|
||||
{{#improved}}
|
||||
<td class="improved">Improved</td>
|
||||
{{/improved}}
|
||||
{{#greater}}
|
||||
<td class="greater">Greater</td>
|
||||
{{/greater}}
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Result: {{result}}</td>
|
||||
</tr>
|
||||
{{#notes}}
|
||||
<tr class="rollNotes">
|
||||
<td>
|
||||
<div>Notes:</div>
|
||||
<div>{{notes}}</div>
|
||||
</td>
|
||||
</tr>
|
||||
{{/notes}}
|
||||
</tbody>
|
||||
</table>
|
||||
</rolltemplate>
|
||||
@@ -0,0 +1,19 @@
|
||||
# Roleplaying is Magic: Season 4 edition character sheet
|
||||
|
||||
This character sheet is for [Roleplaying is Magic: Season 4 edition](http://roleplayingismagic.com/),
|
||||
a popular fan-created tabletop RPG system based upon the world of
|
||||
My Little Pony: Friendship is Magic and developed by Roan Arts.
|
||||
|
||||
## RiM Dice script
|
||||
|
||||
The skill rolling buttons in this sheet make use of an API script for rolling
|
||||
dice and skill checks specifically for the Roleplaying is Magic system. This
|
||||
script is published with the sheet as the file ```mlp_dice.js```. If you use
|
||||
this sheet, it is highly recommended that you also install this API script for
|
||||
your campaign.
|
||||
|
||||
For the skill rolling buttons to work, you must also be speaking as the
|
||||
character you are rolling the skill for.
|
||||
|
||||
The script can also be used as a
|
||||
chat command. This is documented in the ```mlp_dice.js``` script.
|
||||
Reference in New Issue
Block a user