mirror of
https://github.com/Nighthawk42/roll20-character-sheets.git
synced 2026-08-30 03:40:32 +00:00
873 lines
30 KiB
JavaScript
873 lines
30 KiB
JavaScript
// Vampire the Masquerade 5e Dice Mechanics by Momtahan K (Version 1.4).
|
|
//
|
|
// 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.4
|
|
// Enabled GM Whisper rolls
|
|
// 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: "",
|
|
whisper: ""
|
|
};
|
|
|
|
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 = vtmGlobal.whisper + "&{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 (identifier === "g") {
|
|
i++;
|
|
let value = argv[i];
|
|
if (value != undefined) {
|
|
vtmGlobal.whisper= value.trim();
|
|
}
|
|
} 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_orig) {
|
|
let msg=_.clone(msg_orig);
|
|
// 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;
|
|
}
|
|
});
|