Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

Blind Rolls

Hi, I developed the script a while ago and thought about refreshing it as it no longer seemed to work. It's working alright again, but something that I absolutely cannot do anymore is pull the attributes from a character to include the attribute in the hidden roll. Anyway, the script is here if it may be of use to you, and if anyone had an idea how to get the right attribute in the macro or the script, it would be great. Bonus point if someone knows how to pull the attribute from D&amp;D Beyond as I am using Beyond20 to make the link, although I understand that this is very probably not possible. The header contains all the description, hopefully it will be enough to understand. // Github:&nbsp; &nbsp;BlindRoll.js // By: &nbsp; &nbsp;Philippe K., Great Old One // Contact:&nbsp; <a href="https://app.roll20.net/users/321064" rel="nofollow">https://app.roll20.net/users/321064</a> // This script is for the DM to call for dices to be rolled "Blind" by the // players, i.e. they initate the roll but only the DM sees the result. // This is useful for "Detection" checks when you don't want the player to know // how well he rolled so that he cannot metagame what he saw. // It is also very useful for Stealth and again when you don't want the result // of a check to influence the characters' behaviour. // // It is very easy to use, just add the script in the API scripts of the // DM (only) in Roll20, for example under the name of "BlindRolls.js". // After that, the script is invoked by the DM using the following options: // - "!brr *" This will make a button appear in the chat box of every player, //&nbsp; &nbsp; requiring anyone in the party to roll a d20, only the first one rolled //&nbsp; &nbsp; will be sent back to the DM. Can be used for random encounters or just //&nbsp; &nbsp; globally to test the party's luck. // - "!brr [Character]" This will make a button appear in the chat box of only //&nbsp; &nbsp;that player, allowing him to send the roll only to the DM. This is all&nbsp; //&nbsp; &nbsp;purpose and useful if you want to leave the player if that was a skill //&nbsp; &nbsp;check, a saving throw, or just his luck. // - "!brr [Character] [skill]" This will make a button appear in the chat box //&nbsp; &nbsp;of only one player. The only difference with the previous option is that //&nbsp; &nbsp;the player will know what the roll is about. Note that the "skill" can be //&nbsp; &nbsp;anything, not necessarily an official/core skill, and no verification is //&nbsp; &nbsp;done about this. // - "!brr [Character] [skill] [value]" This will make a button appear in the //&nbsp; &nbsp;chat box of only one player, it will mention the bonus to the roll, show //&nbsp; &nbsp;it to the player and integrate that value in the roll sent back to the DM. //&nbsp; &nbsp;Note that, as above, no verification is made about the validity of the&nbsp; //&nbsp; &nbsp;skill or of the value. It is however possible to replace the value by an&nbsp; //&nbsp; &nbsp;attribute like "[[@{Arun Siktar|stealth_roll}]]" // // The script is best used from a macro, of course, where you can set up all the // rolls that the party and its members are likely to make, something like: // /w gm [Global](!brr *) // /w gm [Arun](!brr Arun) [Arun Perception](!brr Arun Perception 3) // /w gm [Chasse](!brr Chasse-le-singe) [Chasse Perception](!brr Chasse Perception 3) // /w gm [Eldoran](!brr Eldoran) [Eldoran Perception](!brr Eldoran Perception 3) // /w gm [Hasira](!brr Hasira,) [Hasira Perception](!brr Hasira, Perception 3) // /w gm [Joyver](!brr Joyver) [Joyver Perception](!brr Joyver Perception 3) // /w gm [Xykis](!brr Xykis) [Xykis Perception](!brr Xykis Perception 3) // /w gm [Shallan](!brr Shallan) [Shallan Stealth](!brr Shallan Stealth 3) // Install that macro in the Macro Bar, and when clicked, it will bring up a // series of buttons that you can just click to ask a Blind Roll from one of // your PCs. var BlindRoll = BlindRoll || (function() { 'use strict'; var version = '0.0.1', lastUpdate = 150995981, roller, rollN = 0, skill = "", bonus = 0, checkInstall = function() { log('-=&gt; BlindRoll v' + version + ' &lt;=-&nbsp; [' + (new Date(lastUpdate * 1000)) + ']'); }, handleInput = function(msg_orig) { var msg = _.clone(msg_orig); if (msg.type !== "api") { return; } // brr - Blind Roll Request, sent by the GM, Format is !brr (Target|*) [Skill [Bonus]] if (msg.content.indexOf("!brr ") !== -1) { &nbsp; &nbsp; log ("") &nbsp; &nbsp; log (msg.content) var params = msg.content.replace("!brr ", "").split(" ", 3) roller = params[0]; log ("Roller:|"+roller+"|") rollN = Date.now(); var chatCommand = (roller == '*' ? "/em" : "/w " + roller); switch (params.length) { case 3: skill = params[1]; bonus = params[2]; sendChat("GM", chatCommand + " [Blind " + skill + " Roll - Modifier " + (bonus &lt; 0 ? '' : '+') + bonus + " ](!bra " + rollN + ") ", null, { noarchive: true }); sendChat("Blind Roller", "/w GM Blind " + skill&nbsp; + " (" + (bonus &lt; 0 ? '' : '+') + bonus + ")"+ " Roll requested from " + roller + "."); break; case 2: skill = params[1]; bonus = 0; sendChat("GM", chatCommand + " [Blind " + skill + " Roll](!bra " + rollN + ") ", null, { noarchive: true }); sendChat("Blind Roller", "/w GM Blind " + skill + " Roll requested from " + roller); break; default: skill = ""; bonus = 0; sendChat("GM", chatCommand + " [" + (roller == '*' ? "Party " : "") + "Blind Roll](!bra " + rollN + ") ", null, { noarchive: true }); sendChat("Blind Roller", "/w GM Blind d20 Roll requested from " + (roller == '*' ? "the Party" : roller) + "."); } } // bra - Blind Roll Answer, send back by the button in the chat of the target if (msg.content.indexOf("!bra ") !== -1) { if (rollN == 0 || msg.content.replace("!bra ", "") != rollN) { sendChat("GM", "/w " + (roller == '*' ? msg.who : "\"" + roller + "\"") + " Only one roll, please..."); } else { sendChat("GM", "/w " + (roller == '*' ? msg.who : roller) + " Thanks for the " + (skill == '' ? '' : skill + ' ') + "roll!"); sendChat((roller == '*' ? msg.who : roller), "/w GM " + (roller == '*' ? "Party " : "") + "Blind " + (skill == '' ? '' : skill + ' ') + "Roll [[d20" + (bonus &lt; 0 ? bonus : '') + (bonus &gt; 0 ? '+' + bonus : '') + "]]"); rollN = 0; } } }, registerEventHandlers = function() { on('chat:message', handleInput); }; return { CheckInstall: checkInstall, RegisterEventHandlers: registerEventHandlers }; }()); on('ready', function() { 'use strict'; BlindRoll.CheckInstall(); BlindRoll.RegisterEventHandlers(); });
1600742597

Edited 1600742684
timmaugh
Pro
API Scripter
This is an interesting script, but why do you say that you can't pull in the attribute from a character anymore? If you drop in something into the "bra" side of the call that can parse into "go get the attribute I'm about to name", you can use the speaker of the "bra" side to find the character id and the unique reference to the attribute to find the data you're looking for as the script is generating the roll to send back to the GM. I can provide an example of that if it doesn't make sense. Another suggestion would be to allow the GM to choose to hide the type of roll the player is about to make. Drop the real attribute name into the state variable under the property "state.brr.hidden" *. Then, when "bra" goes to resolve the attribute, if it sees "hidden" it knows to look in the state for the name of the actual attribute to resolve for each individual character. * - This might need more structure if there are potentially multiple blind rolls going on at the same time (a perception roll by this character and a stealth roll by that character). To accommodate that situation you might need a salt. A counter would work... maintained and incremented in the state variable. state.brr.hidden-1 state.brr.hidden-2 state.brr.hidden-3 When you get to some maximum number of double-blind rolls, start over at 1. Also, the way the rollN variable is attached (scoped to the blindroll object), it looks like if the GM drops a blind roll out for everyone, only the first person to respond will actually matter and everyone else will get the Dkembe Mutombo finger (figuratively). Parties do not roll twice in the House of Mutombo. Two potential problems with that. First... what if the GM wanted multiple people to be able to respond? You want to know who noticed the shimmering, slimy mirrorling inching across the floor on its malformed knuckles, eyes glassy black and its back bent from the pits, whispering that nonsense rhyme from the nightmare you only half remember with a voice like a woodwind's broken reed, something that might once have been musical but sings now only in the coarse hiss of a cadaver dragged through sand. Everyone has to roll perception... not just the first person to hit the button the script puts into chat. If the GM's answer is to send multiple individual perception rolls (one to each member of the party), then that gets to the second potential problem... I think that all of those different roll buttons will still be checking the same rollN value... meaning that the GM would have to wait until the first person resolved their blind roll before the GM could issue another one. You might look at ways to let the GM limit the number of rolls s/he receives. Set the flag one way, and only the first person to respond counts (as it seems to be written, now). Set the flag the opposite, and each member of the party can send a blind roll response, but only once each. This would require managing a set of rollN keys like what I suggested for salting the hidden variable, above. state.brr.rolls[rollN] state.brr.rolls[rollN][character.id] In that case, "bra" would have to check the state to see if, for this rollN object, the character had already responded: let rollmsg = Object.prototype.hasOwnProperty.call(state.brr.rolls[rollN], character.id) ? "Only one roll allowed." : "Thank you for your roll. Don't sweat it too much. I'm sure everything will be mostly ok. Mostly."; sendChat(//...); state.brr.rolls[rollN][character.id] = true; So... two suggestions... let the GM choose to obfuscate the attribute of the roll, and then him/her ask for returns from multiple people instead of only the first.
1600778672
David M.
Pro
API Scripter
HAHA thank you for that! Parties do not roll twice in the House of Mutombo.
Thank for the advice. Although I've been programming for a long time, I'm really not a master in that kind of language and environment, so... :) Yes, I would appreciate advice about pulling out a certain attribute from whoever is replying. I've tried to include some macro language in the !bra line, but it does not seem to work. As for the other advice, they are all really good, but will complexify the script to a point where I am not sure I'll be able to debug it and in particular test it. Even for this, I need to use another account on another browser, for example, so I will start being a bit stuck if I had multiplayer capacity. The reason for the way the party roll is configured like this is because we have this habit of saying "Someone roll a d20 for the party", and having only the first result come in makes things simple. But I agree that asking a perception roll for all the party makes sense. As for the other suggestion to use counters in case there are multiple requests, it's not bad either, but I kept things simple to be at my level of programming, and in any case if I send multiple buttons to click to a player, there is also a chance that it will scroll up and be forgotten, so playwise it is easier to serialise the rolls as well. But it could be a nice addition if I could make it work. Finally, about requesting a roll without showing the player what he is rolling for, it's a good idea as well, but again more programming hidden in the code, so overall, it starts to make things really complex. Finally, the meme is so appropriate, I had a laugh, thanks again. :)
1600984148

Edited 1601229905
timmaugh
Pro
API Scripter
Sorry for the delay in answering. Someday I will tell the tale of, "The Great Standing Desk Debacle," but it is not this day. This day, we code. So, you're not going to get the character attributes through macro language unless your code knows how (and for whom) to get the thing you're looking for. There are a couple of ways to go about this that I can think of off the top of my head. The first involves changing both your command line and the script, while the second only involves changing your script. ( EDIT: they both might require changing your command line to accommodate characters with spaces, but the second option *can* be implemented as is if you only want to support mono-nomic characters) First Option - Parse the Command Line for the Attribute Name Big picture, this one works because you provide the name of the attribute you want to grab, and the script gets that attribute for the speaker at the time that the player clicks on the button. For instance, you provide, "Smackability," and when the !bra side of the code processes, it checks for the speaker (to get the character id), then looks for the value of that character's "Smackability" attribute. It adds it into the roll appropriately before sending the whisper to the GM. To make this work... you need to carve out a place to supply the attribute name in the command line. Since that means that your command lines are going to change, anyway, I would take the opportunity to suggest you alter the parsing of your command line so that you don't have to split on spaces. That's going to present a problem if your target character has a space in their name. I would suggest you split on something that won't otherwise appear in your command line, like: REGEX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;==&gt;&nbsp;&nbsp;&nbsp;&nbsp;EXPLANATION ==================================================================== /\s+--/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; ==&gt;&nbsp; &nbsp; 1 or more spaces + double hyphen /\s+!!/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; ==&gt;&nbsp; &nbsp; 1 or more spaces + double exclamation point /\s+^^/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; ==&gt;&nbsp; &nbsp; 1 or more spaces + double up-carets) There's a whole lot more I could say on the best practices of parsing a command line (also known as: opinion), but I will refrain for now. Let's just assume you are able to provide the attribute name in the command line and properly parse it into a variable called 'attr.' Then, your !bra side of the code would need to retrieve that info: let character = findObjs({ type: 'character' }).filter(c =&gt; c.get('name') === msg.who)[0]; if (!character) { &nbsp; &nbsp; sendChat('Crabby Pants', 'Try rolling as your character next time.'); &nbsp;&nbsp;&nbsp;&nbsp;return; } let targetAttr = getAttrByName(attr, character.id); if(!targetAttr) { &nbsp; &nbsp; sendChat('Crabby Pants', `/w GM Yeah, that attribute name you provided? ${attr}? About that...`); &nbsp; &nbsp; return; } // ... use targetAttr to construct the roll to present to the GM (air-coded, so testing may be in order) Obviously, that would need to be conditioned on the attr variable actually being filled from the command line (if you don't supply it, it means other things). Second Option - Send Individual Buttons Pre-Filled with the Attribute Call This one would work because you would be providing the character(s) you want to send the button to, but you would pre-build the button to reference some attribute of that character. For instance, asking for the Smackability attribute for the character "Limor the Bulbous" would populate the command line supplied to the button sent to Limor's controlling player to have within it: @{Limor the Bulbous|Smackability} Then you can trust Roll20 parsing to do the rest and retrieve that value at the appropriate time. (Now that I say that, certain encoding of HTML characters -- and testing -- might be required to make sure Roll20 parsers wouldn't eat the above attribute call before you want it to.) To make this one work... you would need to alter your command line to pass in the attribute you want to use. You also need to alter it to allow a character like Limor the Bulbous to pass your parsing requirements (with spaces in his name). You might even want to allow for an option of "selected" to be passed into that argument (the same way you use an asterisk), and you could then collect and parse the selected property of the message object at the time the !brr call is made. This way you could sub-divide the party if they were in 2 locations. Grab 3 tokens and send your buttons only to the selected. If you pass '*', you would gather all of the characters into an array. But the next change is that you're not going to /em anymore. You're going to build individual buttons for everyone. So, whether you have an array of all characters, an array of selected tokens (which you would filter for those representing characters and then map into those represented characters), or you have a single character... you then iterate over those characters to construct your button to send their way, and you sub in their name and the attribute as necessary. Getting all of the characters is what we did, above, except without the filter: let characters = []; // condition checking would go here to make sure we want all characters - * characters = findObjs({ type: 'character' }); If you wanted to get them from the selected property of the message, that would look something like: // condition checking would go here to make sure we want 'selected' characters... characters = msg.selected &nbsp; &nbsp; .map(t =&gt; getObj('graphic', t._id)) &nbsp; &nbsp; .filter(t =&gt; t.get('represents')) &nbsp; &nbsp; .map(t =&gt; findObjs({ type: 'character', id: t.get('represents') })[0]); If you only had one and you had parsed it into a variable named charName, push it into the array: characters.push(findObs({ type: 'character' }).filter(c =&gt; c.get('name') === charName)[0]); Once you're there, just iterate over the characters array and produce your output of sendChat calls, each with character-specific requests for attributes. Make sense?
Sorry, been on other projects for a while, thanks for the long and detailed answer. I'll probably need a bit of time to understand and process it, but it seems really interesting. Thanks again!