Created
February 16, 2015 15:44
-
-
Save Cazra/cdc8784a009a26b53dda to your computer and use it in GitHub Desktop.
Roll20 API: MLP:RiM s4 skill roller
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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 a character controlled by the player. | |
* @param {String} playerId The player's ID. | |
* @param {String} [name] The character's name. If not provided, | |
* the player's first controlled token is returned. | |
* @return {Character} | |
*/ | |
function getCharacter(playerId, name) { | |
var allCharacters = findObjs({ | |
_type: "character" | |
}); | |
if(name) { | |
// If a name was provided, get that character if it exists and | |
// is controlled by playerId. | |
return _.find(allCharacters, function(ch) { | |
return (ch.get("name").toLowerCase().trim().indexOf(name) !== -1 && controlledBy(ch, playerId)); | |
}); | |
} | |
else { | |
// If no name was provided, get the first character controlled by | |
// playerId. | |
return _.find(allCharacters, function(ch) { | |
return controlledBy(ch, playerId); | |
}); | |
} | |
}; | |
/** | |
* Checks if a character is controllable by a player. | |
* @param {Character} ch The character | |
* @param {String} playerId The ID of the player. | |
* @return {boolean} true iff the character is controllable by the player. | |
*/ | |
function controlledBy(ch, playerId) { | |
return (ch.get("controlledby").indexOf(playerId) !== -1); | |
}; | |
/** | |
* 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 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, | |
bonus: bonus | |
}; | |
} | |
}; | |
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; | |
}; | |
function getSkillRoll(skill, advDis) { | |
advDis = advDis || 0; | |
var name = skill.name; | |
var attr = skill.attr; | |
var dice = "2d6"; | |
var tier = "untrained"; | |
var bonus = skill.bonus; | |
if(skill.trained) { | |
dice = "3d6d1"; | |
tier = "trained"; | |
} | |
if(skill.improved) { | |
dice = "4d6d2"; | |
tier = "improved"; | |
} | |
if(skill.greater) { | |
tier = "greater"; | |
} | |
var roll = "/r [" + name + " " + tier +"]" + " {{" + dice + " + " + advDis + ", 12 + 1d0}kl1, 2 + 1d0}kh1"; | |
if(skill.greater) | |
roll += " + 1[G]"; | |
roll += " + " + attr.value + "[" + attr.name + "]"; | |
roll += " + " + bonus; | |
return roll; | |
} | |
function getAdvantages(args) { | |
args = _.drop(args, 1); | |
return _.reduce(args, function(memo, arg) { | |
arg = arg.toLowerCase(); | |
var value = 0; | |
if(arg.indexOf("a") !== -1) { | |
arg.replace("a", "").trim(); | |
value = parseInt(arg) || 0; | |
} | |
return memo + value; | |
}, 0); | |
} | |
function getDrawbacks(args) { | |
args = _.drop(args, 1); | |
return _.reduce(args, function(memo, arg) { | |
arg = arg.toLowerCase(); | |
var value = 0; | |
if(arg.indexOf("d") !== -1) { | |
arg.replace("d", "").trim(); | |
value = Math.abs(parseInt(arg) || 0); | |
} | |
return memo + value; | |
}, 0); | |
} | |
on("chat:message", function(msg) { | |
if(msg.type == "api" && msg.content.indexOf(cmd) !== -1) { | |
var str = msg.content.replace(cmd, ""); | |
var args; | |
var sign; | |
if(str.indexOf("+") !== -1) { | |
args = str.split("+"); | |
sign = ""; | |
} | |
else if(str.indexOf("-") !== -1) { | |
args = str.split("-"); | |
sign = "-"; | |
} | |
else { | |
args = [str]; | |
} | |
args = _.map(args, function(arg) { | |
return arg.trim().toLowerCase(); | |
}); | |
var playerId = msg.playerid; | |
var charName = ""; | |
var skillName = args[0]; | |
var adv = sign + args[1]; | |
var character; | |
if(skillName && skillName.indexOf(":") !== -1) { | |
var toks = _.map(skillName.split(":"), function(tok) { | |
return tok.trim(); | |
}); | |
charName = toks[0]; | |
character = getCharacter(playerId, charName); | |
skillName = toks[1]; | |
} | |
else { | |
character = getCharacter(playerId); | |
} | |
if(character) { | |
var skill = getSkill(character, skillName); | |
if(skill) { | |
var roll = getSkillRoll(skill, adv); | |
sendChat(msg.who, roll); | |
} | |
else | |
sendChat("ERROR", "/w " + msg.who + " Could not find skill: " + skillName); | |
} | |
else | |
sendChat("ERROR", "/w " + msg.who + " Could not find character: " + charName); | |
} | |
}); | |
})() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment