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

Need some help "match" keeps returning property of undefined

1558036238
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
All, I am looking for an assist on this. I am almost there but cannot seem to get past working with arrays of varying lengths and thus get a match undefined property error. on("chat:message", function (msg) { //This allows players to enter !sr <number> to roll a number of d6 dice with a target of 4. if (msg.type == "api" && msg.content.indexOf("!test ") !== -1) { var rawDamage = msg.content.replace("!test ", ""); rawDamage = rawDamage.toLowerCase(); //Process rawDamage split into arrays. // 3d+1 cut = [3d+1,cut] var arrDamage = rawDamage.split(" "); var s = rawDamage.split(" ").length; arrDamage.length = s; var damageRoll = arrDamage[0]; var damageType = arrDamage[1]; var damageMod1 = arrDamage[2]; var damageMod2 = arrDamage[3]; if (damageType == null && damageType == '') { damageType = ""; } else { damageType = damageType.toString(); } if (s >= 3 && damageMod1.match("d") == "d") { damageMod1 = damageMod1.replace("[", ""); damageMod1 = damageMod1.replace("]", ""); damageMod1 = damageMod1.replace("d", "d6"); damageMod1 = "[[" + damageMod1 + "]] Frag damage"; damageMod1 = damageMod1.toString(); } else { damageMod1 = damageMod1.toString(); } if (s = 4 && damageMod2.match("d") == "d") { damageMod2 = damageMod2.replace("[", ""); damageMod2 = damageMod2.replace("]", ""); damageMod2 = damageMod2.replace("d", "d6"); damageMod2 = "[[" + damageMod2 + "]] Frag damage"; damageMod2 = damageMod2.toString(); } else { damageMod2 = damageMod2.toString(); } // Function to check for "d6" if else replace "d" with "d6" & replace "x" with "*" if (damageRoll.includes("d6")) { damageRoll = "[[" + damageRoll + "]]"; } else { damageRoll = damageRoll.replace("d", "d6"); damageRoll = "[[" + damageRoll + "]]"; } if (damageRoll.includes("x")) { damageRoll = damageRoll.replace("x", "*"); damageRoll = "[[" + damageRoll + "]]"; } if (damageRoll.includes("¥")) { damageRoll = damageRoll.replace("¥", "*"); damageRoll = "[[" + damageRoll + "]]"; } damageRoll = damageRoll.toString(); sendChat(msg.who, damageRoll + " " + damageType + " " + damageMod1 + " " + damageMod2); } }); Any advice or straight up fixing it is much appreciated. Thanks in advance.
1558041601

Edited 1558041686
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Well, a little more detail on what the problem you are experiencing is and what the aim of the script is would help. I see the following problems with a quick skim through: if (damageType == null && damageType == '') { This will NEVER trigger as damageType cannot be both null and an empty string. Additionally, I would highly recommend switching to using the type specific comparators (i.e. === & !== ) instead of the non type specific comparators (i.e. == & != ). The non type specific comparators can cause some odd behaviors when comparing certain constructs and values. if (s >= 3 && damageMod1.match("d") == "d") { and if (s = 4 && damageMod2.match("d") == "d") { .match  1) requires regexp and the spec doesn't mention an ability to translate strings to regexp and 2) returns an array of all the matches found in the string, so doing 'dddd'.match(/d/) would return ['d','d','d','d'] . Whereas the expression 'd'.match(/d/)  would simply return ['d'] . I think what you're trying to do is just test if the letter d is present in damageMod1/2. If this is what you are aiming for I'd recommend .test() , which is used like so: if (s >= 3 && /d/.test(damageMod1) ) { But, I can give better advice if you can give some more details on your error and what your goals for the script are. EDIT: Stackoverflow discussion with some good explanations for why you should use type specific comparators.
To TL;DR the stackoverflow discussion, the fact that == and != are not transitive should be reason enough to avoid them.
1558045157
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
The intent of the script is to take any of the following and make it a roll: 3d cr, 6d cr ex, 4d cr ex [2d], 4d(10) cr ex [2d], 6d burn ex, or any combination thereof. The roll format almost always begins with #dice and a damage type. The rest sometimes is addtional damage effects or modifiers. Bracketed damage is fragmentation damage.
1558046435
GiGs
Pro
Sheet Author
API Scripter
This looks like a script to convert GURPS damages into roll20 rolls. Is that right? Is there a reason you have kept the gurps damage strings (6d. ex, etc)? Where are these damage strings coming from? If they are on a character sheet you are better off converting them to roll20 format as part of the character sheet (a column for damage, another dor type, another for armour divider, etc.).
1558046579
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
simply to allow players to copy/paste. It will speed transferring characters from sheets to roll20. and bottomline I wanted to try to do this.
1558056334
The Aaron
Roll20 Production Team
API Scripter
Actually, you can give .match() a string, but the return is an array, so damageMod1.match("d") would return an array if it matches (truthy) or null if it doesn't (falsey).  You can just take the == "d" off and it should do what you intend as far as a match.  Using a regex and .test() is a nicer solution. I like to tell people you should always use === and !== until you have enough experience to know when you'd use == and !=, which is never. =D
1558059636
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
I figured out part of my problem is that at the end where I am sending to chat. It is looking for a value for damageMod1 and damageMod2 and throwing an undfined error when I am processing 3d cr as an example. So I need to find a way to build the entire thing then send it as a single value to chat.
1558059747

Edited 1558059776
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
on("chat:message", function (msg) { //This allows players to enter !sr <number> to roll a number of d6 dice with a target of 4. if (msg.type == "api" && msg.content.indexOf("!test ") !== -1) { var rawDamage = msg.content.replace("!test ", ""); rawDamage = rawDamage.toLowerCase(); //Process rawDamage split into arrays. // 3d+1 cut = [3d+1,cut] var arrDamage = rawDamage.split(" "); var s = rawDamage.split(" ").length; arrDamage.length = s; var damageRoll = arrDamage[0]; var damageType = arrDamage[1]; // Function to check for "d6" if else replace "d" with "d6" & replace "x" with "*" if (damageRoll.includes("d6")) { damageRoll = "[[" + damageRoll + "]]"; } else { damageRoll = damageRoll.replace("d", "d6"); damageRoll = "[[" + damageRoll + "]]"; } if (damageRoll.includes("x")) { damageRoll = damageRoll.replace("x", "*"); damageRoll = "[[" + damageRoll + "]]"; } if (damageRoll.includes("¥")) { damageRoll = damageRoll.replace("¥", "*"); damageRoll = "[[" + damageRoll + "]]"; } damageRoll = damageRoll.toString(); damageType = damageType.toString(); sendChat(msg.who, damageRoll + " " + damageType); } }); The above works when there are only two arrays from the split.
1558060043
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
So my thinking then is something like this var finalRoll = damageRoll + " " + damageType + " " + damageMod1 + " " damageMod2; sendChat(msg.who, finalRoll);
1558060833

Edited 1558064691
The Aaron
Roll20 Production Team
API Scripter
Give this a try: on("chat:message", function (msg) { if (msg.type == "api" && msg.content.indexOf("!test ") !== -1) { let rawDamage = msg.content.replace(/^!test\s+/i, "").toLowerCase(); let arrDamage = rawDamage.split(/\s+/); let damageRoll = (arrDamage[0]||'').replace(/d(?:[^6x¥]|^)/,'d6').replace(/[x¥]/,'*'); let damageType = (arrDamage[1]||''); let damageMod1 = (arrDamage[2]||'').replace(/d(?:[^6x¥]|^)/,'d6').replace(/[x¥]/,'*'); let damageMod2 = (arrDamage[3]||'').replace(/d(?:[^6x¥]|^)/,'d6').replace(/[x¥]/,'*'); sendChat(msg.who, `[[${damageRoll}]] ${damageType}${damageMod1 ? ` [[${damageMod1}]] Frag damage` : ''}${damageMod2 ? ` [[${damageMod2}]] Frag damage` : ''}` ); } }); You don't need to set the length on the array, it's an intrinsic property. I'm not sure what you mean about it only working on two arrays. Ok, adjusted.
1558061294

Edited 1558061344
GiGs
Pro
Sheet Author
API Scripter
Edit:  ninja'd :) What is the purpose of this line? arrDamage.length = s; It seems to be unnecessary, and you dont actually use s anywhere else as far as can see. Can you give two or three complete and different examples of the ways you are calling this script, complete with the !test at the start. I'm guessing they look something like: !test  3d+1 cut It's hard to judge what advice to give without knowing what input you are sending it. Also you should remove that comment at the start, it is misleading. I'm referring to this: //This allows players to enter !sr <number> to roll a number of d6 dice with a target of 4.
1558061316
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
Right because some damage rolls are as follows: 5d¥2(0.5) pi++ 2d [2d] cr ex 2d  cr ex  [2d] so I need to be able to pass those other values to be processed in other parts of my api.
1558061460
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
The original intent of the var s to pass the value arrDamage,length to the if else statements however that didn't seem to work
1558061632

Edited 1558061746
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
Edit: ninja'd ^3 Remember the rawDamage may be split into an array length of 2 to 4 usually.
1558062012

Edited 1558062073
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
@The Aaron This is just a test that I will rip the guts out of and plug into my GURPS roll engine. Which does roll comparisons, finds wound modifiers, wound notes, rolls crits on the correct table based on hit location. The ex damage and the frag damage will be sent to another part of the API to produce incremented damage based on distance from center of blast. Which all of it will get sent back to the chat in roll templates. Whew! And I owe it all to you because you gave me some hints that helped me get started.
1558062788

Edited 1558062823
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
Aaron your solution isn't replacing the x or  ¥ thus 3dx3 cr is getting sent to chat as 3d63 cr
1558064698
The Aaron
Roll20 Production Team
API Scripter
Adjusted above.
1558098935
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
I have tried your solution, Aaron. This roll breaks it: 1d+1 [1d] cr ex throwing this error SyntaxError: Expected "(", ".", "[", "abs(", "ceil(", "d", "floor(", "round(", "t", "{", [ |\t], [+|\-] or [0-9] but "c" found. undefined and this roll breaks it as well: 6d¥7(10) cr ex  Could not determine result type of: [{"type":"M","expr":6},{"type":"C","text":"d*7(10)"}] undefined what about pattern matching or splitting subStrings? Armor divisors "n" are enclosed with "(n)" Frag damage "n" dice is enclosed with "[nd]"
1558111238
GiGs
Pro
Sheet Author
API Scripter
The problem is, Aaron cant build a script that will work because you havent told him all the variations of input it will receive. This is why I asked you above to post some complete examples of input. What you need to do is post every single kind of variation  of input that you might use. 
1558113218
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
Yes, true. And that could be tedious. however; there are some basic patterns that all GURPS damage rolls follow. Armor divisors are always enclosed in parenthesis. Frag damage is always enclosed in brackets. Damage rolls are always followed by a damage type and damage modifier. ex. cr - crushing  followed by ex - explosion. Armed with that knowledge one could parse an entry as complicated as the following:  1d-2 [1d(0.2)] burn ex
1558129000
Gen Kitty
Forum Champion
SᵃᵛᵃǤᵉ said: Yes, true. And that could be tedious. Also tedious is trying to build a script to satisfy uncommunicated desires.  Without a complete design spec, the programmer is doomed to failure.
1558129396
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
So we pour derision on me...that's rich. Like I had written earlier... the basic format begins with a number"d" which is usually followed by the damage type however there are exceptions. But, I'll keep working on it myself. Thank you so much...Roll20 community...thanks
1558129961

Edited 1558130480
I don't think it's derision to ask for more information about what you want help on.  Clearly people can't help you effectively as they could because there are design parameters that are not stated fully (namely input variations).  People are trying to help but, you need to help them help you.  I would just provide them with that information - in the form of canonical examples.
1558131319
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
Right and I have explained the roll structure. and here are examples: 4d cr 3d cut 1d cor 4d(10) cr 4d(10) cr ex 4d+1 [2d] cr ex 1d-2 [1d(0.2)] burn ex 4dx2(10) cr ex 5dx2 cr ex 6dx3 [6d+2] cr ex 5dx2(0.5) pi++ 6dx3 pi++ 8dx2(2) pi inc 5d pi+ 6dx11(10) cr ex 3d burn 2d [1d(0.2)] burn ex 1d+1 [1d-1 cr] cr ex 1d imp 8d cr ex [3d] 6dx4 burn ex 4d(10) cr ex[2d] The following are the damage modifiers aff burn cor cr cor cut fat imp inc pi- pi pi+ pi++ rad sur Any value inside parenthesis is an Armor divider Any value inside brackets is fragmentation damage. I think that about covers it.
1558299122

Edited 1558299175
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
So I have a pattern matching function working pretty good. It pulls out bracket parenthesis enclosed arguments. on("chat:message", function (msg) { //This allows players to enter !sr <number> to roll a number of d6 dice with a target of 4. if (msg.type == "api" && msg.content.indexOf("!frag ") !== -1) { let rawDamage = msg.content.replace(/^!frag\s+/i, "").toLowerCase().toString(); // Find Frag damage var regex1 = RegExp(/\[(.*?)\]/g); var arrayFrag; arrayFrag = regex1.exec(rawDamage) log(`Found ${arrayFrag[0]} frag damage`); let fragDamage = (arrayFrag[0]||'').replace('d','d6').replace(/[x¥]/,"*"); //Find the Armor Divisor var regex2 = RegExp(/\((.*?)\)/g); var arrayArmor; arrayArmor = regex2.exec(arrayFrag[0]) log(`Found ${arrayArmor[0]} armor divisor`); armorDiv = arrayArmor[0]; fragDamage = "[" + fragDamage + "] frag damage"; sendChat(msg.who, `${fragDamage}` + " " + `${armorDiv}` ) } });
1558302383

Edited 1558304697
The Aaron
Roll20 Production Team
API Scripter
So, for the sample above, here's a decoding: { "4d cr": { "roll": "4d6", "damageTypes": [ "cr" ] }, "3d cut": { "roll": "3d6", "damageTypes": [ "cut" ] }, "1d cor": { "roll": "1d6", "damageTypes": [ "cor" ] }, "4d(10) cr": { "armorDiv": "10", "roll": "4d6", "damageTypes": [ "cr" ] }, "4d(10) cr ex": { "armorDiv": "10", "roll": "4d6", "damageTypes": [ "cr", "ex" ] }, "4d+1 [2d] cr ex": { "roll": "4d6+1", "frag": { "roll": "2d6" }, "damageTypes": [ "cr", "ex" ] }, "1d-2 [1d(0.2)] burn ex": { "roll": "1d6-2", "frag": { "armorDiv": "0.2", "roll": "1d6" }, "damageTypes": [ "burn", "ex" ] }, "4dx2(10) cr ex": { "armorDiv": "10", "roll": "4d6*2", "damageTypes": [ "cr", "ex" ] }, "5dx2 cr ex": { "roll": "5d6*2", "damageTypes": [ "cr", "ex" ] }, "6dx3 [6d+2] cr ex": { "roll": "6d6*3", "frag": { "roll": "6d6+2" }, "damageTypes": [ "cr", "ex" ] }, "5dx2(0.5) pi++": { "armorDiv": "0.5", "roll": "5d6*2", "damageTypes": [ "pi++" ] }, "6dx3 pi++": { "roll": "6d6*3", "damageTypes": [ "pi++" ] }, "8dx2(2) pi inc": { "armorDiv": "2", "roll": "8d6*2", "damageTypes": [ "pi", "inc" ] }, "5d pi+": { "roll": "5d6", "damageTypes": [ "pi+" ] }, "6dx11(10) cr ex": { "armorDiv": "10", "roll": "6d6*11", "damageTypes": [ "cr", "ex" ] }, "3d burn": { "roll": "3d6", "damageTypes": [ "burn" ] }, "2d [1d(0.2)] burn ex": { "roll": "2d6", "frag": { "armorDiv": "0.2", "roll": "1d6" }, "damageTypes": [ "burn", "ex" ] }, "1d+1 [1d-1 cr] cr ex": { "roll": "1d6+1", "frag": { "roll": "1d6-1", "damageTypes": [ "cr" ] }, "damageTypes": [ "cr", "ex" ] }, "1d imp": { "roll": "1d6", "damageTypes": [ "imp" ] }, "8d cr ex [3d]": { "roll": "8d6", "damageTypes": [ "cr", "ex" ], "frag": { "roll": "3d6" } }, "6dx4 burn ex": { "roll": "6d6*4", "damageTypes": [ "burn", "ex" ] }, "4d(10) cr ex[2d]": { "armorDiv": "10", "roll": "4d6", "damageTypes": [ "cr" ], "frag": { "roll": "2d6" } } } Using this function: const getRollData = (text) => { const damageMods = ['aff','burn','cor','cr','cor','cut','ex','fat','imp','inc','pi-','pi','pi+','pi++','rad','sur']; const regex = { roll: /\d+d/, frag: /\[([^\]]*)\]/, armorDiv: /\(((?:0\.)?\d+)\)/ }; let p = []; let b = false; let accu=''; text.split('').forEach(c=>{ if(/\[/.test(c)){ b=true; } if(/\]/.test(c)){ b=false; } if(!b && /\s/.test(c)){ if(accu.length){ p.push(accu); } accu=''; } else { accu+=c; } }); p.push(accu); let roll = {}; p.forEach(t=>{ if(regex.frag.test(t)){ let m = t.match(regex.frag); roll.frag = getRollData(m[1]); } else if(regex.roll.test(t)){ let m = t.match(regex.armorDiv); if(m){ roll.armorDiv = m[1]; t = t.replace(regex.armorDiv,''); } t = t.replace(/d/,'d6'); t = t.replace(/[x¥]/g,'*'); roll.roll = t; } else { if(damageMods.includes(t)){ roll.damageTypes = [...(roll.damageTypes||[]),t]; } } }); return roll; }; Edit: recursive descent parser to handle spaces in frag syntax.
1558306590

Edited 1558306633
GiGs
Pro
Sheet Author
API Scripter
With my incomplete knowledge of GURPS and the big sample I've posted above, here are the rules that a damage string might follow. The first part is the damage string. Damage Dice: can be any number of dice (1d, 2d, 7d, 20d). The dice can have +1, +2, -1, or -2. so 1d+2 is valid, as is 3d-1. Very large numbers of dice sometimes have a multiple: 7dx2, 5dx3, 2dx10. Any multiple is possible. These rolls will never have a + or -. so 1d+2x5 will never happen. The following extra bits may be added to the string. Any number of them, or none, may be present, but they will always be in the listed order Armour divider, always in parathesese: (10), (0.5). There is never a space, so 3d(2), 1d+2(5), are valid, but 3d (2) is not. Shrapnel, in square brackets. There's always a space here. Shrapnel is a full damage string, following all previous rules: Examples: 3d [1d+2], 2d(5) [1d-1(2)], 2dx5 [2d] Finally you have damage types, which describe the kind of damage that is inflicted. I'm not sure what they all are, or if they follow any ordering rules.  aff = ?? burn = burning damage cor ? corrosive? cr = crushing cut = cutting fat = fatigue? imp = impaling inc ??? pi- ??? pi ??? pi+ ??? pi++ ??? rad = radiation sur ??? Cut, cr, and imp are the common ones, and will not occur together on the same attack. I notice this listing above didn't explain the last couple of examples 8d cr ex [3d] 6dx4 burn ex 4d(10) cr ex[2d] There's no mention of ex, but I;m guessing its a separate damage type for when explosion damage is not fragmentation and needs its own damage type. This is all pretty complicate to parse! But you need at least this kind of detail to be able to start.
1558306960

Edited 1558309056
The Aaron
Roll20 Production Team
API Scripter
GiGs, I think I've got the parsing for all of that except the ex[2d] one, I missed that.  I probably need more info on how to parse it. Here's a script that makes use of the function above to do some output of the data: on('ready',()=>{ const getRollData = (text) => { const regex = { roll: /^\d+d/, frag: /^\[([^\]]*)\]/, armorDiv: /\(((?:0\.)?\d+)\)/, mod: /(aff|burn|cor|cr|cor|cut|ex|fat|imp|inc|pi-|pi|pi\+|pi\+\+|rad|sur)(?:\[([^\]]+)\])?/ }; let p = []; let b = false; let accu=''; text.split('').forEach(c=>{ if(/\[/.test(c)){ b=true; } if(/\]/.test(c)){ b=false; } if(!b && /\s/.test(c)){ if(accu.length){ p.push(accu); } accu=''; } else { accu+=c; } }); p.push(accu); let roll = {}; p.forEach(t=>{ if(regex.frag.test(t)){ let m = t.match(regex.frag); roll.frag = getRollData(m[1]); } else if(regex.roll.test(t)){ let m = t.match(regex.armorDiv); if(m){ roll.armorDiv = m[1]; t = t.replace(regex.armorDiv,''); } t = t.replace(/d/,'d6'); t = t.replace(/[x¥]/g,'*'); roll.roll = t; } else { let m = t.match(regex.mod); if(m) { let r={}; if(m[2]){ r=getRollData(m[2]); } roll.damageTypes = [...(roll.damageTypes||[]),`${m[1]}${r.roll?`[[[${r.roll}]]]`:''}`]; } } }); return roll; }; const fmtRoll = (rd) => `${rd.roll ? `[[${rd.roll}]] ` :''}${rd.armorDiv ? `Armor Div: ${rd.armorDiv} ` : ''}${rd.damageTypes ? `Damage Mods: ${rd.damageTypes.join(' ')} `:''}${rd.frag ? `Frag Damage: ${fmtRoll(rd.frag)}` : ''}`; on('chat:message', (msg)=>{ if('api'===msg.type && /^!frag\b/i.test(msg.content)){ let rd = getRollData(msg.content.replace(/^!frag\s+/i,'')); sendChat(msg.who, fmtRoll(rd)); } }); }); Example: !frag 1d+1 [1d-1 cr] cr ex Yields:
1558307587
GiGs
Pro
Sheet Author
API Scripter
Nice work. I didnt read the previous post properly.
1558309039

Edited 1558309075
The Aaron
Roll20 Production Team
API Scripter
Yeah, once I had the samples, the only hard part was not splitting on spaces inside a frag specifier.  That ex[2d] is a bit of a problem.  I adjusted the above to just keep it with the damage mod: so it's really just a matter of understanding what it should do, maybe just roll the damage... Edited the above to do that.
1558309621
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
Aaron, thanks for the follow through! on the ex[2d] I think that's a book error. So we won't see it. GiGs, to your ??s ex is explosive damage. That was another thing I was going to work on later since it's damage lessens farther away from the blast. Frag damage is always cutting damage. So I'll probably go back and add that in somehow. aff = affliction most likely won't see that since it is a resisted roll versus an attribute. pi- = small piercing pi = piercing pi+ = large piercing pi++ = huge piercing inc = incendiary also rare no real game effect except for starting fires then you get burning... sur = surge, rare as well; secondary damage.
1558311816
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
Now I am going to try to get this jammed into my G.U.R.P.S. (Grand Unified Roll Parsing Software) 
1558312606
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
The Aaron said: Yeah, once I had the samples, the only hard part was not splitting on spaces inside a frag specifier.  That ex[2d] is a bit of a problem.  I adjusted the above to just keep it with the damage mod: so it's really just a matter of understanding what it should do, maybe just roll the damage... Edited the above to do that. The split before frag damage with no space is a error that I copied. Sorry about that. Not trying to play "Stump the Chump".
1558313071
The Aaron
Roll20 Production Team
API Scripter
No big deal, it will still handle the valid format. =D
1558454611
SᵃᵛᵃǤᵉ
Sheet Author
API Scripter
So I am still working on plugging this into my API. When I ran the large piercing pi+ and huge piercing pi++ the output dropped the +'s. Also I need the mods to also be this variable "dtype". Still trying but could use some hints.