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

Sum damage rolls in a roll template

1629154068

Edited 1629155071
We are getting into some pretty complex attack results for some characters, I would like to add all the damage up instead of the player having to do so. My approach was to parse the chat output and look for a specific character (in this case † (ALT+0134) because it looks like a plus and it won't ever be used in chat)) Any number flagged with † should be summed and the total output to chat... this works, however I am getting the base dice roll and NOT the actual total for example if the players rolltemplate used " hit AC [[1d20]] for †[[1d6+10]] base damage plus †[[1d6]] fire " I am getting the results of the 1d6 dice rolls without the +10 So it appears to be processing the chat prior to the numbers being added up if that makes sense.... This is my script on("chat:message", function(msg) {   //grab any number preceded by a † (ALT+0134)   if(msg.content.indexOf("†") !== -1) {     var s = msg.content;         var matches = s.match(/(?<=(†|†\$\[\[))(\d+)/g);         var sum = 0;         for(var i = 0; i < matches.length; i++) {             sum += Number(matches[i]);         sendChat(msg.who, "matches= "+matches);         }     sendChat(msg.who, " total was : "+sum);   } }); The sendchat "matches=" was for troubleshooting only my output looks like matches= 1,5 matches= 1,5 total was : 6 as if the 1d8 was a 1 and the 1d6 fire was a 5, but the +10 isn't there It's probably something simple but I am not a coder by any stretch ... any help would be greatly appreciated!
1629163245
timmaugh
Pro
API Scripter
As far as what you have going, I'm not sure you're accessing the inline roll values correctly. Here is what a message object might look like, using your command line, above: {    content: {{hit AC $[[0]] for †$[[1]] base damage plus †$[[2]] fire}},    inlinerolls: [       {          expression: 1d20,          results: {             resultType: sum,             rolls: [                {                   dice: 1,                   results: [                      {                         v: 11                      }                   ],                   rollid: -MhGf7w1qxgAZp22rrOQ,                   sides: 20,                   type: R                }             ],             total: 11,             type: V          },          rollid: -MhGf7w1qxgAZp22rrOQ,          signature: 7794a863162f432e7eb2f6979a53bc0867568815a04358a06dc85e96db2f6ea4431db6c6a78413cbc7b48de42b18e3aa33ff36576246c683d8792e1fa8664182,          tdseed: 2996521147230069000       },       {          expression: 1d6+10,          results: {             resultType: sum,             rolls: [                {                   dice: 1,                   results: [                      {                         v: 4                      }                   ],                   rollid: -MhGf7w2x0H7ysOYaC3n,                   sides: 6,                   type: R                },                {                   expr: +10,                   type: M                }             ],             total: 14,             type: V          },          rollid: -MhGf7w2x0H7ysOYaC3n,          signature: 5b2d2c7f40aa8deba2022d131d6f520ce53169a5aaa4205ec92b4113f31f732040d7f8c0fe138a47d8ba6e63506bbf366b7d307563853da77ea78d052df08fd2,          tdseed: 6103776895173638000       },       {          expression: 1d6,          results: {             resultType: sum,             rolls: [                {                   dice: 1,                   results: [                      {                         v: 2                      }                   ],                   rollid: -MhGf7w2x0H7ysOYaC3o,                   sides: 6,                   type: R                }             ],             total: 2,             type: V          },          rollid: -MhGf7w2x0H7ysOYaC3o,          signature: 97331d6f92099de99395af1a64b115b197ca2f423c011a4a84a80934fb331f3df44eb4ec5bb423398b0fc2c0a6d713f8631fd6a069d41bc0522c7f7e492c3b8f,          tdseed: 475720406966539500       }    ],    playerid: -MYm6xDmt1Ze5LnVz59E,    rolltemplate: default,    type: general,    who: timmaugh (GM),    variables: {},    mules: [] } If you look at the content string, you don't have the value enclosed in the $[[..]], you have the roll index. Now look at the inlineroll array, and you can see how the various roll parts are nested. (BTW, I pulled this info using my ActivityTracker script, if you wanted to explore the message object more). In your for-loop, you have to use your index against the msg.inlinerolls array, and pull out the msg.inlinerolls[i].results.total  That should have your actual value in it, including the +10 you're looking for.
Thanks for the pointer, that seems to make things harder. I need to grab the inline roll only when preceded by † and for those inline rolls grab msg.inlinerolls[i].results.total, since I don't want all inline roll totals... Most of our roll template include hit rolls and damage rolls ... I will keep poking around.
1629169371

Edited 1629169516
So I think I need to take msg.content and parse for all inline rolls $[[  and build an array of only the ones that look like †$[[ So maybe I build an array with values 1,3,5 for the above Then I need to sum the msg.inlinerolls[1].results.total + msg.inlinerolls[3].results.total + msg.inlinerolls[5].results.total I think it's OK to assume all occurrences will be in an inline roll, I don't see much point to stick a random † someplace else in the message text other than an inline roll
1629175475

Edited 1629220402
timmaugh
Pro
API Scripter
Ah, right. I missed a step, there. EDIT: Actually, I missed a couple! My apologies. See this post , below, for working code. Your regex is doing the work of getting the rolls you need. You just need to get the capture groups from your regex. But your regex can be simpler. Let's try this: on("ready", () => {   on("chat:message", msg => {     if(msg.content.indexOf("†") === -1) return;     let matches = /†\$\[\[(\d+)/g.exec(msg.content);     if(!matches) return;     let sum = 0;     matches.forEach((m, roll) => {       sum += msg.inlinerolls[roll].results.total;     });     sendChat(msg.who, " total was : "+sum);   }); }); Your regex is returning ONLY the roll index markers in the command line that match your pattern (including the cross character). The 0th group of each match is the full string... †$[[0 ...and the 1st group of each match is your roll index (the '0', above). Even if the rolls you want are the 1st (or, the 0th), the 3rd, and the 15th, your regex has already isolated them for you. When you iterate over each match (forEach), you pass 2 arguments to the callback function: the whole match (the 0th group), and the isolated roll index (the 1st group). Extract the value and add it to your sum. If you want to see more regarding handling of inline rolls, I wrote a library script to help with parsing (called libInline ). Also, if you want, you can do what you're looking to do with metascripts. Not sure if you're treating this as a learning experience (if so, cool, hopefully the above gets you closer), or if you are just looking for the problem to be solved... but with ZeroFrame you can do something like: !&{template:default}{{name=ZeroFrame Proof of Concept}} {{To Hit=[[1d20]]}}{{Result=Hit AC $[[0]] for [[1d6+10]] base damage plus [[1d6]] fire.}}{{Total Damage=[\][\]$[[1]].value + $[[2]].value\]\]}}{&simple} The above produces: That reuses the rolls and totals them, with each still hover-able to get the roll information. If you can't see how to modify that example to your specific uses (and you want to go this direction) post back and I'll try to help. EDIT: Fixing for posterity; it shouldn't have been forEach but replace(), instead.
1629213612

Edited 1629213670
For a big complex roll template I think the metascript is going to be harder for a neophyte player to use and understand, if I say "just put † in front of your inline roll and it will add them up" I think it will see much wider use and thus value... many of my players are only minimally proficient with macros. That being said, the code sample you provided is behaving even more strangely, I get this output.... It seems to be grabbing the first 2 inline rolls, not the ones with † I think that's because it's may be getting a count of 3 matches but when it looks at those it's going to grab inline roll[0] and [1] (no idea why it's not grabbing a 3rd total) but I think it's has to do with not knowing to skip the appropriate inline totals Imagine how much easier this would be if there were some way to get the post-processed chat output :-)
I guess if I follow let matches = /†\$\[\[(\d+)/g.exec(msg.content); is going to get back 3 matches, the 21, 23, 21 damage above but this loop  matches.forEach((m, roll) => {       sum += msg.inlinerolls[roll].results.total;     }); is going to grab the first 3 inline rolls with no regard for which ones have † in front of them but then again it's only grabbing 2 so maybe I am way off base here on what I think it's doing
1629215378
timmaugh
Pro
API Scripter
Can you post the full chat message that you are sending to get that output? The rolls are numbered according to how they are encountered... from inner-most-left-most to outer-most-right-most. For you, based on the example, I don't think you have nested rolls, so it should just be left to right, 0 to N. Also, you *can* see the post-processed chat output (well, enough of it to piece together what's going on) with the ActivityTracker I linked to, above. Install it (save it and your sandbox starts up), then restart your sandbox, run !track to make sure it is armed and that it will be tracking chat messages, clear the log, then run the message that produces the above output. Finally, scroll back up to the config panel that opened when you ran !track, and click on the "REPORT" button. You will see all of the events it has tracked. Pick the one that corresponds to the chat message you sent (probably the 2nd to last, since the VERY last one will be your REPORT command), and click the OBJ button. That will show you the message object, including the content/command line, and the inline rolls. For your purposes, the only part you'll have to piece together is the inline rolls (they will show up as $[[0]], $[[1]], etc., and you'll just have to use that index in your inline roll array to determine the value that should be returning to the line in that case.
1629215796

Edited 1629220274
timmaugh
Pro
API Scripter
Unexreme said: I guess if I follow let matches = /†\$\[\[(\d+)/g.exec(msg.content); is going to get back 3 matches, the 21, 23, 21 damage above but this loop  matches.forEach((m, roll) => {       sum += msg.inlinerolls[roll].results.total;     }); is going to grab the first 3 inline rolls with no regard for which ones have † in front of them but then again it's only grabbing 2 so maybe I am way off base here on what I think it's doing Not quite... the roll argument passed into the forEach  replace() loop is the 1st capture group... which you have established as "all of the digits that follow †$[[" In other words, that roll IS the roll index from the inline roll array. If you take that regex statement to regex101.com, change the regex type to "ECMAScript (Javascript)", and then enter something in the text to test... like: This roll should be captured †$[[0]] but this one should not $[[1]]. And then this one should be captured †$[[2]]. You should see in the right panel that for each match, the Group 1 value is the roll index: That  forEach  replace() loop can pass as many arguments as there are groups in the regex match you are iterating over... so you could really think of it as: matches.forEach((fullmatch, group1, group2, group3, group4, group5,...) => { ... }); msg.content.replace(rx, ((fullmatch, group1, group2, group3, group4, group5,...) => { ... })); EDIT: fixing for posterity; shouldn't have been forEach but replace()
1629216156

Edited 1629220120
timmaugh
Pro
API Scripter
You can prove that it is getting the right rolls if you drop a log statement in that forEach  replace() loop... log(`Now retrieving roll #${roll}. The value is ${msg.inlinerolls[roll].results.total}`); Then check the log screen after you send a command through. EDIT: fixing for posterity; shouldn't have been forEach but replace()
let matches = /†\$\[\[(\d+)/g.exec(msg.content); is going to index the second inline roll in the chat as roll=0 I added to the foreach loop and I get this "Now retrieving roll #0. The value is 29" "Now retrieving roll #1. The value is 21" This makes sense because the regex is grabbing only the inline rolls with † to build my array while sum += msg.inlinerolls[roll].results.total; is using the index number relative all the inline rolls in the chat, not just the ones with † For example if I had 4 inline rolls and only the last one had a † when I ask for sum += msg.inlinerolls[0].results.total;  I will get the first inline roll, not the last one with the † I think the  first "matches =" needs to be all inline rolls $[[ with another filter inside the foreach loop that checks for the †$[[ Still testing....
Original roll template macro &{template:default} {{Attack Options=Deadly Aim}} {{name=Flurry of Bows}} {{AC[[1d20+17]] for = †[[1d8+20]] damage}} {{AC[[1d20+17]] for = †[[1d8+20]] damage}} {{AC[[1d20+12]] for = †[[1d8+20]] damage}}
1629219941
timmaugh
Pro
API Scripter
Ach! So, note to self: don't air code while sitting in a work meeting. =D I wasn't looking for the forEach function. I was looking for the replace() function. You're right, the second argument of the forEach callback is the index of the array. However, for the replace function, it is the groups from your match. You just have to remember to return the entire match to the line if you're not actually replacing it and it matters to the output. Let's try this instead: on("ready", () => { on("chat:message", msg => { if(msg.content.indexOf("†") === -1) return; let sum = 0 let rx = /†\$\[\[(\d+)/g; if(!rx.test(msg.content)) return; rx.lastIndex = 0; msg.content.replace(rx, ((m, roll) => { sum += msg.inlinerolls[roll].results.total; return m; })); sendChat(msg.who, " total was : "+sum); }); }); In this case, you don't actually *have* to return m in the replace() function, since the output to the chat has already happened (you are tracking the value for a new message). IOW, your replace isn't actually changing the output to the chat, so you don't have to return a value to the command line replacement operation. In cases like this, where I'm using the replace() function for something it wasn't *really* meant for, I just return the full match by default, just so I don't have to worry about the odd case down the line where I should. (I will edit the above posts to reflect this new -- and tested -- code.) =D
aha! that works and I think I follow the logic.... I was going down the foreach with an if(.match inside the foreach loop so I felt like I was getting closeish but I have nothing to do with code, I work in IT but write no code. Thanks a ton, I am thinking there could be a lot of uses for something like this in my own groups
Thanks a bunch! wish I could upvote in some manner :-)