diff --git a/RoleplayingIsMagic_4E/mlp_dice.js b/RoleplayingIsMagic_4E/mlp_dice.js
new file mode 100644
index 0000000000..8b64cbe052
--- /dev/null
+++ b/RoleplayingIsMagic_4E/mlp_dice.js
@@ -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 += ' '
+ 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);
+ }
+
+ });
+})();
diff --git a/RoleplayingIsMagic_4E/mlp_rim4.css b/RoleplayingIsMagic_4E/mlp_rim4.css
new file mode 100644
index 0000000000..f88a690adf
--- /dev/null
+++ b/RoleplayingIsMagic_4E/mlp_rim4.css
@@ -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;
+}
diff --git a/RoleplayingIsMagic_4E/mlp_rim4.html b/RoleplayingIsMagic_4E/mlp_rim4.html
new file mode 100644
index 0000000000..e69fe11fb2
--- /dev/null
+++ b/RoleplayingIsMagic_4E/mlp_rim4.html
@@ -0,0 +1,738 @@
+
+
+
+
+
+
+
+
+
General
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/
+
+ Modifiers:
+
+
+
+
/
+
+ Modifiers:
+
+
+
+
/
+
+ Modifiers:
+
+
+
+
Attributes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Description
+
+
+
+
+
+
+
+
+
+
+
+
+
MIND Skills
+
+
+
BODY Skills
+
+
+
HEART Skills
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This section is for all edges other than Skill Training. Those go over in the Skills section.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{charName}} ⇒ {{skillName}}
+
+
+
+
+
+ {{#untrained}}
+
Untrained
+ {{/untrained}}
+ {{#trained}}
+
Trained
+ {{/trained}}
+ {{#improved}}
+
Improved
+ {{/improved}}
+ {{#greater}}
+
Greater
+ {{/greater}}
+
+
+
Result: {{result}}
+
+ {{#notes}}
+
+
+
Notes:
+
{{notes}}
+
+
+ {{/notes}}
+
+
+
diff --git a/RoleplayingIsMagic_4E/readme.md b/RoleplayingIsMagic_4E/readme.md
new file mode 100644
index 0000000000..14a0dbeb11
--- /dev/null
+++ b/RoleplayingIsMagic_4E/readme.md
@@ -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.