Vampire The Masquerade 5th edition
From Roll20 Wiki
// Vampire the Masquerade 5e Dice Mechanics by Momtahan K (Version 1.3). // // The following code is an adaptation of that produced by Roll20 user Konrad J. for "Hero Quest Dice Mechanics". // Many thanks for providing this code free to use. // // Legal // Portions of the materials are the copyrights and trademarks of White Wolf Publishing AB, and are used with permission. All rights reserved. For more in formation please visit whitewolf.com // // With the exception of materials under the copyright of White Wolf all extra code should be considered under // GNU General Public License v3 or later (GPL-3.0-or-later) Copyright 2018 held my Momtahan.K // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. // // This license MUST be copied on to any derivative work. // Effectively feel free to use, edit and modify the code to your hearts content. As I understand it this will only cause an issue if you // try to use it for commerical gain. This has been added to ensure the community benefits from it.
// Versions // Version 1.3 // Bug fix. Macros previously may have caused issues, removing some error checking to fix this issue. // Version 1.2 // Updated images to a different location. Fixed bugs where players name would not appear for specific rolls. Fixed bug in which speech marks in a players name would cause issues. // Version 1.1 // Bug fixes and updated images to a different location
// Guide to use the code
// The first two lines I am no longer sure if they work and require testing.
// !vtm log on|multi|single|off // default:on and multi
// outputs dice rolled to the chat window if "on", only the result if "off"
// dice rolled will be on single line if "single" and on multiple lines if "multi"
// !vtm graphics on|off|s|m|l //default:on and m
// shows dice rolled as graphic, small, medium, or large if "on" or as text if "off"
//
// !vtm test // this will output every side of every die to the chat window (currently does not work)
// !vtm hero // Only for true heroes
// !vtm lupine // When people ask for too much
//
// There are several different types of rolls.
// In these rolls the first two arguments must be !vtm [type] all other numbers are optional and may be ignored depending on the type of roll
// !vtm atr a# r# m# // Rolling for attributes a# is the number of dice associated with the attribute, r# number of dice associated with hunger, m# is a dice modifier to apply. Example !vtm atr a2 r2 m3 // !vtm skill a# r# m# // Rolling for skills a# is the number of dice associated with the attribute, s# the number of dice for the skill r# number of dice associated with hunger, m# is a dice modifier to apply. Example !vtm atr a2 s3 r2 m3 // !vtm will w# a# m# // Rolling for willpower w# is the number of willpower dice a# is the number of dice associated with an attribute, m# is a dice modifier to apply. Example !vtm atr a2 w2 m3 // !vtm will p# a# m# // Rolling for willpower p# is an odd number. By and large you should not use it unless you are creating your own multi-state health/willpower boxes. In such cases please look at the html to see how I have implemented it. // !vtm roll w# r# // Is a straight roll, the w# represents the number of black die, the r# represents the number of red die to roll Example !vtm roll w2 r2 // Note: For anyone reading the code, to make the frenzy roll work I had to invert the DC thus asking the play to roll less than 7 is instead interpretted as asking the player to roll 5 or higher (these are probabilistically equal statements). // !vtm rouse // Rolls one single rouse dice. Success on 6+ // !vtm reroll w# // Used for will power re-roll # is the number of die to roll // !vtm frenzy p# o# q# // Rolls for frenzy. This is used to add 1/3 humanity (rounded down) to willpower, to roll. As mentioned previously p# is a special case and the number of dice rolled is not equal to the number you enter. Unless you are looking at multistate boxes, don't use this. o# is similar but for humanity. q# Should be used to set the difficulty of the Frenzy roll // !vtm remorse x# m# // Used for remorse roll. x# is under a similar constraint as p# and o# due to multistate checkbox issues once again. // !vtm humanity o# m# // Used for humanity roll. // // Optional Flags: // An extra variable (c~custom name ~) may be added to any of these rolls to display a custom name in the roll template. Note: As a coding querk please have a space after the name but before the close in the tilde. // Example !vtm roll w5 r1 c~Prince Wolf ~ will roll 5 black die, 1 red die and the character will have the name - Prince Wolf // An extra variable (t~custom name~) may be added to any of these rolls to display the roll type. This is text below the custom name // Adding b# to a skill roll will add the value/2.0 to the number of vampire dice. This is used for blood potency when handling disciplines // If needs be both the Frenzy, Remorse and Humanity Roll can be updated to use real values. For now however I'm going to leave it.
var vtmCONSTANTS = { VTMCOMMAND: "!vtm", GRAPHICSIZE: { SMALL: 20, MEDIUM: 30, LARGE: 40, XLARGE: 50, XXLARGE: 60 }, VAMPIRE: { ONE: "https://i.imgur.com/BjURZvY.png", TWO: "https://i.imgur.com/MK6B9vM.png", THREE: "https://i.imgur.com/EiFDjZg.png", FOUR: "https://i.imgur.com/t7Lt0mx.png", FIVE: "https://i.imgur.com/UnW2Ao3.png", SIX: "https://i.imgur.com/7J9nx2w.png", SEVEN: "https://i.imgur.com/dVi0fZK.png", EIGHT: "https://i.imgur.com/4xBiXJM.png", NINE: "https://i.imgur.com/MMy0fJ1.png", TEN: "https://i.imgur.com/dPLPkMd.png" }, HUNGER: { ONE: "https://i.imgur.com/Q9U10Zh.png", TWO: "https://i.imgur.com/YFrNSut.png", THREE: "https://i.imgur.com/mUPaTMj.png", FOUR: "https://i.imgur.com/TagrNvs.png", FIVE: "https://i.imgur.com/S5IMrAB.png", SIX: "https://i.imgur.com/ttQDDt5.png", SEVEN: "https://i.imgur.com/MIrvVQW.png", EIGHT: "https://i.imgur.com/fw0j5pN.png", NINE: "https://i.imgur.com/5RkL2Se.png", TEN: "https://i.imgur.com/w87YVKQ.png" } };
var vtmGlobal = { diceLogChat: true, diceGraphicsChat: true, diceGraphicsChatSize: vtmCONSTANTS.GRAPHICSIZE.MEDIUM, diceTextResult: "", diceTextResultLog: "", diceGraphicResult: "", diceGraphicResultLog: "", diceTestEnabled: false, diceLogRolledOnOneLine: false, luckydice: false, reroll: "" };
function rollVTMDice(diceQty, type, dc) { var roll = 0; var diceResult = { nilScore: 0, successScore: 0, critScore: 0, muddyCritScore: 0, failScore: 0, diceGraphicsLog: "", diceTextLog: "" };
// Used to build images var i = 0; var s1 = '<img src="'; var s2 = '" title="'; var s3 = '" height="'; var s4 = '" width="'; var s5 = '"/>';
if (vtmGlobal.diceTestEnabled === true) { diceQty = 10; }
for (i = 1; i <= diceQty; i++) {
if (vtmGlobal.diceTestEnabled === true) { roll = roll + 1; } else { roll = randomInteger(10); }
let image = getDiceImage(type, roll); if (type === "v") { if (roll >= dc.critSuccess) { diceResult.diceTextLog = diceResult.diceTextLog + "(" + roll + ")"; diceResult.diceGraphicsLog = diceResult.diceGraphicsLog + s1 + image + s2 + roll + s3 + vtmGlobal.diceGraphicsChatSize + s4 + vtmGlobal.diceGraphicsChatSize + s5; diceResult.successScore = diceResult.successScore + 1; diceResult.critScore = diceResult.critScore + 1; } else if (roll >= dc.success) { diceResult.diceTextLog = diceResult.diceTextLog + "(" + roll + ")"; diceResult.diceGraphicsLog = diceResult.diceGraphicsLog + s1 + image + s2 + roll + s3 + vtmGlobal.diceGraphicsChatSize + s4 + vtmGlobal.diceGraphicsChatSize + s5; diceResult.successScore = diceResult.successScore + 1; } else if (roll >= dc.nil) { diceResult.diceTextLog = diceResult.diceTextLog + "(" + roll + ")"; diceResult.diceGraphicsLog = diceResult.diceGraphicsLog + s1 + image + s2 + roll + s3 + vtmGlobal.diceGraphicsChatSize + s4 + vtmGlobal.diceGraphicsChatSize + s5; diceResult.nilScore = diceResult.nilScore + 1; } else if (roll >= dc.critFail) { diceResult.diceTextLog = diceResult.diceTextLog + "(" + roll + ")"; diceResult.diceGraphicsLog = diceResult.diceGraphicsLog + s1 + image + s2 + roll + s3 + vtmGlobal.diceGraphicsChatSize + s4 + vtmGlobal.diceGraphicsChatSize + s5; diceResult.nilScore = diceResult.nilScore + 1; } } else if (type === "h") { if (roll >= dc.critSuccess) { diceResult.diceTextLog = diceResult.diceTextLog + "(" + roll + ")"; diceResult.diceGraphicsLog = diceResult.diceGraphicsLog + s1 + image + s2 + roll + s3 + vtmGlobal.diceGraphicsChatSize + s4 + vtmGlobal.diceGraphicsChatSize + s5; diceResult.successScore = diceResult.successScore + 1; diceResult.muddyCritScore = diceResult.muddyCritScore + 1; } else if (roll >= dc.success) { diceResult.diceTextLog = diceResult.diceTextLog + "(" + roll + ")"; diceResult.diceGraphicsLog = diceResult.diceGraphicsLog + s1 + image + s2 + roll + s3 + vtmGlobal.diceGraphicsChatSize + s4 + vtmGlobal.diceGraphicsChatSize + s5; diceResult.successScore = diceResult.successScore + 1; } else if (roll >= dc.nil) { diceResult.diceTextLog = diceResult.diceTextLog + "(" + roll + ")"; diceResult.diceGraphicsLog = diceResult.diceGraphicsLog + s1 + image + s2 + roll + s3 + vtmGlobal.diceGraphicsChatSize + s4 + vtmGlobal.diceGraphicsChatSize + s5; diceResult.nilScore = diceResult.nilScore + 1; } else if (roll >= dc.critFail) { diceResult.diceTextLog = diceResult.diceTextLog + "(" + roll + ")"; diceResult.diceGraphicsLog = diceResult.diceGraphicsLog + s1 + image + s2 + roll + s3 + vtmGlobal.diceGraphicsChatSize + s4 + vtmGlobal.diceGraphicsChatSize + s5; diceResult.failScore = diceResult.failScore + 1; } } }
return diceResult; }
/**
* Get the image associated with the roll. * @param {*} type The type (V) Vampire or (H) Hunger. * @param {*} roll The roll value. Returns null if not 1 - 10 */
function getDiceImage(type, roll) { if (type === "v") { switch (roll) { case 1: return vtmCONSTANTS.VAMPIRE.ONE; case 2: return vtmCONSTANTS.VAMPIRE.TWO; case 3: return vtmCONSTANTS.VAMPIRE.THREE; case 4: return vtmCONSTANTS.VAMPIRE.FOUR; case 5: return vtmCONSTANTS.VAMPIRE.FIVE; case 6: return vtmCONSTANTS.VAMPIRE.SIX; case 7: return vtmCONSTANTS.VAMPIRE.SEVEN; case 8: return vtmCONSTANTS.VAMPIRE.EIGHT; case 9: return vtmCONSTANTS.VAMPIRE.NINE; case 10: return vtmCONSTANTS.VAMPIRE.TEN; default: return null; } } else if (type === "h") { switch (roll) { case 1: return vtmCONSTANTS.HUNGER.ONE; case 2: return vtmCONSTANTS.HUNGER.TWO; case 3: return vtmCONSTANTS.HUNGER.THREE; case 4: return vtmCONSTANTS.HUNGER.FOUR; case 5: return vtmCONSTANTS.HUNGER.FIVE; case 6: return vtmCONSTANTS.HUNGER.SIX; case 7: return vtmCONSTANTS.HUNGER.SEVEN; case 8: return vtmCONSTANTS.HUNGER.EIGHT; case 9: return vtmCONSTANTS.HUNGER.NINE; case 10: return vtmCONSTANTS.HUNGER.TEN; default: return null; } } }
function processVampireDiceScript(run, dc) { var attackDiceResults = { nilScore: 0, successScore: 0, critScore: 0, muddyCritScore: 0, failScore: 0, diceGraphicsLog: "", diceTextLog: "" }; var defendDiceResults = { nilScore: 0, successScore: 0, critScore: 0, muddyCritScore: 0, failScore: 0, diceGraphicsLog: "", diceTextLog: "", hungerSustained: true };
var diceTextRolled = ""; var diceGraphicsRolled = "";
log("Roll Variables"); log(run); let user = run.user;
attackDiceResults = rollVTMDice(run.blackDice, "v", dc); defendDiceResults = rollVTMDice(run.redDice, "h", dc);
log(attackDiceResults); log(defendDiceResults);
var diceTotals = { nilScore: attackDiceResults.nilScore + defendDiceResults.nilScore, successScore: attackDiceResults.successScore + defendDiceResults.successScore, critScore: attackDiceResults.critScore + defendDiceResults.critScore, muddyCritScore: attackDiceResults.muddyCritScore + defendDiceResults.muddyCritScore, failScore: attackDiceResults.failScore + defendDiceResults.failScore, diceGraphicsLog: attackDiceResults.diceGraphicsLog + defendDiceResults.diceGraphicsLog, diceTextLog: "Normal" + attackDiceResults.diceTextLog + "Hunger" + defendDiceResults.diceTextLog };
if (vtmGlobal.diceTestEnabled === true) { sendChat("", "/desc " + user + ": v1 h1"); }
let endTemplateSection = "}} "; let outputMessage = "&{template:wod} {{name=" + user + endTemplateSection;
if (run.rollname) { outputMessage += "{{Rollname=" + run.rollname + endTemplateSection; }
if (vtmGlobal.diceLogChat === true) { if (vtmGlobal.diceLogRolledOnOneLine === true) { diceGraphicsRolled = diceTotals.diceGraphicsLog; diceTextRolled = diceTotals.diceTextLog; if (vtmGlobal.diceGraphicsChat === true) { outputMessage += "{{Roll=" + diceGraphicsRolled + endTemplateSection; } else { outputMessage += "{{Roll=" + diceTextRolled + endTemplateSection; } } else { if (vtmGlobal.diceGraphicsChat === true) { outputMessage += "{{Normal=" + attackDiceResults.diceGraphicsLog + endTemplateSection; outputMessage += "{{Hunger=" + defendDiceResults.diceGraphicsLog + endTemplateSection; } else { outputMessage += "{{Normal=" + attackDiceResults.diceTextLog + endTemplateSection; outputMessage += "{{Hunger=" + defendDiceResults.diceTextLog + endTemplateSection; } } }
let thebeast = '<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/vampire-v5/Banners/TheBeast.png" title="The Beast" height="20" width="228"/>';
if (run.rouseStatRoll) { if (diceTotals.successScore > 0) { outputMessage += "{{Beast=" + '<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/vampire-v5/Banners/RousingSuccess.png" title="Rousing Success" height="20" width="228"/>' + endTemplateSection; } else { outputMessage += "{{Beast=" + '<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/vampire-v5/Banners/HungerGain.png" title="Hunger Gain" height="20" width="228"/>' + endTemplateSection;
} } else if (run.frenzyRoll) { outputMessage += "{{Successes=" + diceTotals.successScore + endTemplateSection; if (diceTotals.successScore >= run.difficulty) { outputMessage += "{{Beast=" + '<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/vampire-v5/Banners/FrenzyRestrained.png" title="Frenzy Restrained" height="20" width="228"/>' + endTemplateSection; } else { outputMessage += "{{Beast=" + '<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/vampire-v5/Banners/Frenzy.png" title="Frenzy" height="20" width="228"/>' + endTemplateSection; } } else if (run.remorseRoll) { outputMessage += "{{Successes=" + diceTotals.successScore + endTemplateSection; if (diceTotals.successScore > 0) { outputMessage += "{{Beast=" + '<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/vampire-v5/Banners/HumanityPass.png" title="Guilty" height="20" width="228"/>' + endTemplateSection; } else { outputMessage += "{{Beast=" + '<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/vampire-v5/Banners/HumanityFail.png" title="Innocent" height="20" width="228"/>' + endTemplateSection;
}
if (vtmGlobal.luckydice) { let lastResort = '<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/vampire-v5/Banners/lastresort.png" title="Miss" height="20" width="228"/>'; outputMessage += "{{Fate=" + lastResort + endTemplateSection; } } else { outputMessage = addRollDeclarations(diceTotals, outputMessage, endTemplateSection, thebeast); }
vtmGlobal.luckydice = false; outputMessage += "{{Reroll=[Reroll](" + vtmGlobal.reroll + ")" + endTemplateSection;
log("Output"); log(outputMessage); if (vtmGlobal.diceTestEnabled != true) { sendChat(user, outputMessage); } }
function addRollDeclarations(diceTotals, outputMessage, endTemplateSection, thebeast) { // Crit bonus is + 2 successes for each PAIR of crits. Thus 2 crits is + 2 successs, 3 crits is + 2 successes. let critBonus = Math.floor((diceTotals.critScore + diceTotals.muddyCritScore) / 2.0) * 2.0; outputMessage += "{{Successes=" + (diceTotals.successScore + critBonus) + endTemplateSection;
if (diceTotals.successScore == 0 && vtmGlobal.luckydice) {
let lastResort = '<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/vampire-v5/Banners/lastresort.png" title="Miss" height="20" width="228"/>';
outputMessage += "{{Fate=" + lastResort + endTemplateSection;
let miss = '<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/vampire-v5/Banners/MissFail.png" title="Miss" height="20" width="228"/>';
outputMessage += "{{Miss=" + miss + endTemplateSection;
} else if (diceTotals.successScore == 0) {
//outputMessage += "{{Fate=" + "Total failure" + endTemplateSection;
let miss = '<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/vampire-v5/Banners/MissFail.png" title="Miss" height="20" width="228"/>';
outputMessage += "{{Miss=" + miss + endTemplateSection;
} else if (vtmGlobal.luckydice) {
let lastResort = '<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/vampire-v5/Banners/lastresort.png" title="Miss" height="20" width="228"/>';
outputMessage += "{{Fate=" + lastResort + endTemplateSection;
}
if ((diceTotals.muddyCritScore >= 2) || (diceTotals.muddyCritScore === 1 && (diceTotals.critScore >= 1))) { let messy = '<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/vampire-v5/Banners/MessyCritical.png" title="Messy" height="20" width="228"/>'; outputMessage += "{{Messy=" + messy + endTemplateSection; } else if (diceTotals.critScore >= 2) { let crit = '<img src="https://raw.githubusercontent.com/Roll20/roll20-character-sheets/master/vampire-v5/Banners/CriticalHit.png" title="Crit" height="20" width="228"/>'; outputMessage += "{{Crit=" + crit + endTemplateSection; }
if (diceTotals.failScore >= 5) { outputMessage += "{{Beast=" + thebeast + endTemplateSection; // outputMessage += "{{BeastTaunt=" + "I do say dear boy, I may be a mite bit peckish." + endTemplateSection; } else if (diceTotals.failScore >= 3) { outputMessage += "{{Beast=" + thebeast + endTemplateSection; // outputMessage += "{{BeastTaunt=" + "BLOOD BLOOD GIVE ME BLOOD!! I MUST FEED!" + endTemplateSection; } else if (diceTotals.failScore >= 2) { outputMessage += "{{Beast=" + thebeast + endTemplateSection; // outputMessage += "{{BeastTaunt=" + "Let the vitae flow!" + endTemplateSection; } else if (diceTotals.failScore >= 1) { outputMessage += "{{Beast=" + thebeast + endTemplateSection; // outputMessage += "{{BeastTaunt=" + "Feed Me! (Hunger causes you to be distracted)" + endTemplateSection; }
return outputMessage; }
var processScriptTabs = function (argv, who, dc) { // this will run the various other scripts depending upon the chat // window command. Just add another Case statement to add a new command. var tmpLogChat = false; var tmpGraphicsChat = false; var script = argv.shift(); switch (script) { case vtmCONSTANTS.VTMCOMMAND: switch (argv[0]) { case "log": switch (argv[1]) { case "on": vtmGlobal.diceLogChat = true; break; case "off": vtmGlobal.diceLogChat = false; break; case "multi": vtmGlobal.diceLogRolledOnOneLine = false; break; case "single": vtmGlobal.diceLogRolledOnOneLine = true; break;
} break; case "graphics": switch (argv[1]) { case "on": vtmGlobal.diceGraphicsChat = true; break; case "off": vtmGlobal.diceGraphicsChat = false; break; case "s": vtmGlobal.diceGraphicsChatSize = vtmCONSTANTS.GRAPHICSIZE.SMALL; break; case "m": vtmGlobal.diceGraphicsChatSize = vtmCONSTANTS.GRAPHICSIZE.MEDIUM; break; case "l": vtmGlobal.diceGraphicsChatSize = vtmCONSTANTS.GRAPHICSIZE.LARGE; break; } break; case "test": vtmGlobal.diceTestEnabled = true; tmpLogChat = vtmGlobal.diceLogChat; tmpGraphicsChat = vtmGlobal.diceGraphicsChat; vtmGlobal.diceLogChat = true; vtmGlobal.diceGraphicsChat = true; var run = { blackDice: 1, redDice: 1, user: who, roll: null }; processVampireDiceScript(run, dc); vtmGlobal.diceTestEnabled = false; vtmGlobal.diceLogChat = tmpLogChat; vtmGlobal.diceGraphicsChat = tmpGraphicsChat; break; case "hero": sendChat("The best thing about Hero Quest!", "https://www.youtube.com/watch?v=Cx8sl2uC46A"); break; case "lupine": //sendChat("Some players (and character sheet designers)...", ""); break; default: processVampireDiceScript(argv[0], dc); } break; } };
function performInlineRolls(msg) { log("Inline Roll"); msg.content = _.chain(msg.inlinerolls) .reduce(function (m, v, k) { m['$' + k + ''] = v.results.total || 0; return m; }, {}) .reduce(function (m, v, k) { return m.replace(k, v); }, msg.content) .value();
return msg; };
function handleSkillRoll(input) { log("Atr/Skill Roll"); log(input); let hunger = input.hunger; let dicepool = input.attribute + input.modifier; if (input.type === "skill") { dicepool += input.skill; }
var run = { blackDice: 0, redDice: 0, user: input.user, rollname: input.rollname };
if (dicepool <= 0) { vtmGlobal.luckydice = true; if (hunger > 0) { run.redDice = 1; return ["!vtm", run]; } else { run.blackDice = 1; return ["!vtm", run]; } }
run.blackDice = dicepool - hunger; run.redDice = ((dicepool + hunger) - Math.abs(dicepool - hunger)) / 2;
return ["!vtm", run]; }
function handleWillpowerRoll(input) { let dicepool = input.willpower + input.attribute + input.modifier;
var run = { blackDice: 0, redDice: 0, user: input.user, rollname: input.rollname };
if (dicepool <= 0) { vtmGlobal.luckydice = true; dicepool = 1; }
run.blackDice = dicepool;
return ["!vtm", run]; }
function handleRouseRoll(input) { var run = { blackDice: 0, redDice: 1, user: input.user, rollname: input.rollname, rouseStatRoll: true };
return ["!vtm", run]; }
function handleFrenzyRoll(input) { let dicepool = input.willpower + input.modifier + Math.floor(input.skill / 3.0);
var run = { blackDice: 0, redDice: 0, user: input.user, rollname: input.rollname, frenzyRoll: true, difficulty: input.difficulty };
if (dicepool <= 0) { vtmGlobal.luckydice = true; run.redDice = 1; return ["!vtm", run]; }
run.blackDice = 0; run.redDice = dicepool;
return ["!vtm", run]; }
function handleSimpleRoll(input) { log("Simple Roll"); log(input); var run = { blackDice: input.willpower, redDice: input.hunger, user: input.user, rollname: input.rollname };
return ["!vtm", run]; }
function handleRemorseRoll(input) { log("Remorse Roll"); log(input); let dice = input.willpower + input.modifier; if (dice <= 0) { vtmGlobal.luckydice = true; dice = 1; }
var run = { blackDice: dice, redDice: 0, user: input.user, rollname: input.rollname, remorseRoll: true };
return ["!vtm", run]; }
function handleHumanityRoll(input) { log("Humanity Roll") log(run); let dice = input.skill + input.modifier; if (dice <= 0) { vtmGlobal.luckydice = true; dice = 1; }
var run = { blackDice: dice, redDice: 0, user: input.user, rollname: input.rollname };
return ["!vtm", run]; }
function calculateVariables(argv, who) { var input = { type: argv[1], attribute: 0, skill: 0, hunger: 0, modifier: 0, willpower: 0, user: who, rollname: null, successDc: 6, difficulty: 1 };
for (i = 2; i < argv.length; i++) { let entry = argv[i]; let identifier = entry.substring(0, 1);
if (identifier === "a") { // Assign an int directly to an attribute let value = parseInt(entry.substring(1), 10); input.attribute = value; } else if (identifier === "s") { // Assign an int directly to a skill let value = parseInt(entry.substring(1), 10); input.skill = value; } else if (identifier === "o") { // Used to assign a trait much like "p", this is used in Willpower rolls to assign humanity let value = parseInt(entry.substring(1), 10); value = updateMultiboxValue(value); input.skill = value; } else if (identifier === "r") { // Red die. Used for assigning a value directly to hunger die. let value = parseInt(entry.substring(1), 10); input.hunger = value; } else if (identifier === "m") { // Adds a modifier value straight let value = parseInt(entry.substring(1), 10); input.modifier += value; } else if (identifier === "b") { // Adds half of value to modifier. Example Discipline let value = parseInt(entry.substring(1), 10); value = Math.floor(value / 2.0); input.modifier += value; } else if (identifier === "w") { // Used for willpower if you want to give it a value directly let value = parseInt(entry.substring(1), 10); input.willpower = value; } else if (identifier === "p") { // Used for traits which have 4 states such willpower and health let value = parseInt(entry.substring(1), 10); value = updateMultiboxValue(value); input.willpower = value; } else if (identifier === "d") { // Used for varying a difficulty let value = parseInt(entry.substring(1), 10); if (value < 1) { value = 1; } else if (value > 10) { value = 10; } input.successDc = value; } else if (identifier === "c") { // Used for assigning a character name i++; let value = argv[i]; if (value != undefined && value.trim().length != 0) { input.user = value.trim(); } } else if (identifier === "t") { // Used for assigning a rollname i++; let value = argv[i]; if (value != undefined && value.trim().length != 0) { input.rollname = value.trim(); } } else if (identifier === "q") { // The number of successes required (used for only certain rolls) let value = parseInt(entry.substring(1), 10); input.difficulty = value; } else if (input.type === "remorse") { log("remorse variable") // Used for remorse rolls let totalValue = parseInt(entry.substring(1), 10); let totalRemorse = updateMultiboxValue(totalValue); let missingRemorse = totalValue - totalRemorse; missingRemorse = updateMultiboxValue1(missingRemorse); input.willpower = missingRemorse / 16.0; } }
return input; }
// Used for multistate checkboxes function updateMultiboxValue(totalValue) { let value = totalValue; value = scaleMultiboxValue(value, 3616); value = scaleMultiboxValue(value, 241); value = scaleMultiboxValue(value, 16); return value; }
// Used for multistate checkboxes function updateMultiboxValue1(totalValue) { let value = totalValue; value = scaleMultiboxValue(value, 3616); value = scaleMultiboxValue(value, 241); return value; }
function scaleMultiboxValue(value, scaleNumber) { while (value > 0) { value -= scaleNumber; }
if (value < 0) { value += scaleNumber }
return value; }
// Decides how to distribute dice based on the type of roll function calculateRunScript(input) { if (input.type === "atr" || input.type === "skill") { return handleSkillRoll(input); } else if (input.type === "will") { return handleWillpowerRoll(input); } else if (input.type === "rouse") { return handleRouseRoll(input); } else if (input.type === "frenzy") { return handleFrenzyRoll(input); } else if (input.type === "remorse") { return handleRemorseRoll(input); } else if (input.type === "humanity") { return handleHumanityRoll(input); } else { return handleSimpleRoll(input); } }
// Get the standard DC function baseDc() { var dc = { // These DCs are set to be equal to or greater than their listed value critFail: 1, nil: 2, success: 6, critSuccess: 10 } return dc; }
// Calculates DC function calculateDc(run) { let dc; if (run[1].rouseStatRoll === true) { dc = { // These DCs are set to be equal to or greater than their listed value critFail: 1, nil: 2, success: 6, // All DCs must be set, setting to a number >10 will mean it is effectively ignored critSuccess: 42 } } else { dc = baseDc(); } return dc; }
on("chat:message", function (msg) { // returns the chat window command entered, all in lowercase. if (msg.type != 'api') { return; }
log("New roll"); log(msg);
if (_.has(msg, 'inlinerolls')) {
msg = performInlineRolls(msg);
}
log(msg);
var chatCommand = msg.content; vtmGlobal.reroll = chatCommand.replace(/\"/g, '"').replace(/\~/g, '~');
var argv = [].concat.apply([], chatCommand.split('~').map(function (v, i) { return i % 2 ? v : v.split(' ') })).filter(Boolean); log("Post Splitting"); log(argv);
try { if (argv[1] === "skill" || argv[1] === "atr" || argv[1] === "will" || argv[1] === "roll" || argv[1] === "rouse" || argv[1] === "frenzy" || argv[1] === "reroll" || argv[1] === "remorse" || argv[1] === "humanity") { let input = calculateVariables(argv, msg.who); let run = calculateRunScript(input); let dc = calculateDc(run); return processScriptTabs(run, msg.who, dc); } else if (argv[1] === "log" || argv[1] === "graphics" || argv[1] === "test" || argv[1] === "hero" || argv[1] === "lupine") { return processScriptTabs(argv, msg.who, baseDc()); } } catch (err) { sendChat("Error", "Invalid input" + err); return; } });