We use Cookies to help personalize and improve Roll20. For more information on our use of non-essential Cookies, visit our Privacy Policy here.
Accept
Advertisement Create a free account

Odd msg.content returned

1574695591

Edited 1574695904
DXWarlock
Pro
Sheet Author
API Scripter
It could be I am either missing or misunderstanding something. But when trying to capture what is sent to chat from an attack the API returns a result (that to me anyway), does not match what was sent to chat. Example: I use an attack from an NPC and log msg.content. This is the chat result of the attack: I assume the result of msg.content should be a directly pasteable code, that if I slapped it into the chatbox and hit enter would result in the same message being sent(Minus the roll results maybe). But it does not. This is what msg.content returns for me: " ||rowid=-LbGdkGgGhpkjuA8xiHd||0 {{rounded=rounded}} {{color=gold}} {{character_name=Sir Rip Studwell}} {{character_id=-LOYtydhMyPZl_Z2wYyH}} {{subtitle}} {{name=Club}} {{attack=$[[3]]}} {{damage=$[[6]]}} {{crit_confirm=$[[10]]}} {{crit_damage=$[[15]]}} {{type=Bludgeoning}} {{weapon_notes=}} 0 0 0 0 0 0 0 0 {{buff_note=}} {{condition_note=}} {{ranged_notes=}} {{header_image=}} {{attack_notes=}} {{vs=ac}} {{vsac=ac}} {{precision_dmg1=}} {{precision_dmg1_type=}} {{precision_dmg2=}} {{precision_dmg2_type=}} {{critical_dmg1=}} {{critical_dmg1_type=}} {{critical_dmg2=}} {{critical_dmg2_type=}} {{attack1name=}}" That clump of spaces(or its a tab maybe?), followed by ||rowid-...||0 seems odd to me. Everything after that is part of what I expect..but the content starts with something that can't be actually be part of the msg sent can it? That would just spit that out as plain text in chat. I'd think msg.content would start with  &{template:[something]} as that is what is actually being sent to chat. What I'm trying to do is capture the message as it was sent, to pull data I need from it, but I need it in exactly the same format as it was sent.
1574703322
Scott C.
Forum Champion
Sheet Author
API Scripter
Hey DX, at a guess, you have (or had at one time) the Pathfinder companion script installed and have the ammo tracking turned on. That ||rowid...|| Is inserted by pfc so it knows which ammo to detract from on an attack. The zero after it is an artifact from some of the default roll options on the community pf sheet. Also, note that the roll template declaration (e.g. &{template:pf_attack}) is not included in msg.content. It's stored in msg.rolltemplate instead as just the template name (e.g. pf_attack).
1574703706

Edited 1574703888
DXWarlock
Pro
Sheet Author
API Scripter
Ah that makes sense now, I was wondering if it was behind the scenes sheetworkers or something like that..but that above my pay grade so to speak to be able to figure that out..lol Is there a way to get the 'raw' message sent to the chat?  What I'm trying to do is a realllly long and round about way of removing roll formulas from the chat. Having a talk to one of my players, he's meta gaming by pointing at rolls to see enemy bonuses, etc and its influencing his ingame choices. And he agreed if I can hide them it will help him stop having the urge to. So instead of editing a 100 NPC's to add a 2nd layer of [[ ]] around every roll on every attack:  [[ [[1d6]] ]] for example .. I was going to set all the NPC's to whisper GM only. Then grab that whisper, 'sanitize' all the rolls so pointing at it shows like Rolling 5=5, and send that to chat.
1574704267
Scott C.
Forum Champion
Sheet Author
API Scripter
Oh, that's actually pretty easy. On mobile atm, but when I'm back at a comp, I'll post a snippet. As usual for API things, Aaron deserves most of the credit for it.
1574704718

Edited 1574704776
DXWarlock
Pro
Sheet Author
API Scripter
That would be amazing! If there is an easier way to scrub formulas from all rolls in chat than my Rube Goldberg solution I was trying..that would save me a LOT of work. I tried to do the converting 100 NPC attacks to be padded with extra [[ ]], but after about a dozen creatures in all with multiple attacks..I realized it was the dumbest way to try to fix this..lol. I'd have a full night of tediously editing 100's of sheets. As well as I would need to do that for every new creature or attack as I made them.
1574707508

Edited 1574708357
Scott C.
Forum Champion
Sheet Author
API Scripter
Ok, back at my computer. Here's a script I threw together using several functions I already had in various places. The extractRoll function is entirely stolen from Aaron's various scripts, as is my general script design. About half of the script below is just my error handling technique and basic script scaffold. The actual work is done in the handleInput and extractRoll functions. var hideRolls= hideRolls || (function(){let scriptStart = new Error;//Generates an error to localize the start of the script &nbsp; &nbsp; //converts the line number in the error to be line 1 &nbsp; &nbsp; scriptStart = scriptStart.stack.match(/apiscript\.js:(\d+)/)[1]*1; &nbsp; &nbsp; var version = 0.1, &nbsp; &nbsp; &nbsp; &nbsp; lastUpdate = 1574706278, &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; //Error reporting function &nbsp; &nbsp; sendError = function(err){ &nbsp; &nbsp; &nbsp; &nbsp; var stackMatch = err.stack.match(/apiscript\.js:\d+/g); &nbsp; &nbsp; &nbsp; &nbsp; _.each(stackMatch,(s)=&gt;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let sMatch = s.match(/\d+/)[0]*1; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; err.stack = err.stack.replace(new RegExp('apiscript\.js:'+sMatch),'apiscript.js:'+(sMatch-scriptStart+ 1)); &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; var stackToSend = err.stack ? (err.stack.match(/([^\n]+\n[^\n]+)/) ? err.stack.match(/([^\n]+\n[^\n]+)/)[1].replace(/\n/g,'&lt;br&gt;') : 'Unable to parse error') : 'Unable to parse error'; &nbsp; &nbsp; &nbsp; &nbsp; sendChat('','/w gm &lt;div style="border: 1px solid black; background-color: white; padding: 3px 3px;"&gt;'//overall div for nice formatting of control panel &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; +'&lt;div style="font-weight: bold; border-bottom: 1px solid black;font-size: 130%;"&gt;'//Control Panel Header div &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; +'Hide Rolls v'+version+'&lt;b&gt; Error Handling&lt;/b&gt;&lt;/div&gt;' &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; +'&lt;div style="border-top: 1px solid #000000; border-radius: .2em; background-color: white;"&gt;' &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; +'The following error occurred:&lt;br&gt;&lt;pre&gt;&lt;div style="color:red"&gt;&lt;b&gt;'+err.message+'&lt;br&gt;'+stackToSend+'&lt;/b&gt;&lt;/div&gt;&lt;/pre&gt;Please post this error report to the &lt;b&gt;&lt;u&gt;[Script forum thread](<a href="https://app.roll20.net/forum/post/7698809/script-door-knocker/?pageforid=7698809#post-7698809)&lt;/u&gt;&lt;/b" rel="nofollow">https://app.roll20.net/forum/post/7698809/script-door-knocker/?pageforid=7698809#post-7698809)&lt;/u&gt;&lt;/b</a>&gt;.' &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; +'&lt;/div&gt;' &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; +'&lt;/div&gt;',null,{noarchive:true}); &nbsp; &nbsp; }, &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; boot = function(){ &nbsp; &nbsp; &nbsp; &nbsp; checkInstall(); &nbsp; &nbsp; }, &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; /*Checks the API environment to make sure everything is prepped for the script*/ &nbsp; &nbsp; checkInstall = function() { &nbsp; &nbsp; &nbsp; &nbsp; log('=&gt; Hide Rolls v'+version+' &lt;=-&nbsp; ['+(new Date(lastUpdate*1000))+']'); &nbsp; &nbsp; &nbsp; &nbsp; if( ! _.has(state,'hide') || state.hide.version !== version) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; state.hide = state.hide || {}; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log(`==&gt; Updating Hide Rolls to v${version} &lt;`); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; state.hide.version = version; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; }, &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; handleInput = function(msg_orig) { &nbsp; &nbsp; &nbsp; &nbsp; try{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var msg = _.clone(msg_orig), &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; validTemplates = [//add the name of the roll templates you want the script to respond to here. &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'pf_attack', &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 'pf_spell', &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ]; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (!/^gm$/i.test(msg.target)||!new RegExp(`^${validTemplates.join('|')}$`).test(msg.rolltemplate)){//if the message isn't a whisper to the gm or doesn't have a valid roll template declaration, do nothing &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(_.has(msg,'inlinerolls')){//if there are inline rolls in the message, convert them to their raw numbers, and send the converted text to chat as a public message. &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; msg.content = extractRoll(msg); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendChat(msg.who,`&amp;{template:${msg.rolltemplate}} ${msg.content}`); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; }catch(err){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendError(err); &nbsp; &nbsp; &nbsp; &nbsp; } }, &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; extractRoll = function(msg){ &nbsp; &nbsp; &nbsp; &nbsp; return _.chain(msg.inlinerolls) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .reduce(function(m,v,k){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m['$[['+k+']]']=v.results.total || 0; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return m; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },{}) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .reduce(function(m,v,k){ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return m.replace(k,v);//converts [[1d20+5]] = 6 to be just 6 (normal text, no hover) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //return m.replace(k,`[[${v}]]`); //switch to this return value if you want the output to look like it would if you did [[6]] in the above example. Maintaining natural 1 and crit highlighting is a little more involved. &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },msg.content) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .value(); &nbsp; &nbsp; }, &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; RegisterEventHandlers = function() { &nbsp; &nbsp; &nbsp; &nbsp; on('chat:message',handleInput) &nbsp; &nbsp; }; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; return { &nbsp; &nbsp; &nbsp; &nbsp; Bootup: boot, &nbsp; &nbsp; &nbsp; RegisterEventHandlers: RegisterEventHandlers &nbsp; }; &nbsp; &nbsp;&nbsp; }()); on("ready",function(){ &nbsp; &nbsp; 'use strict'; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; hideRolls.Bootup(); &nbsp; &nbsp; hideRolls.RegisterEventHandlers(); });
1574707994

Edited 1574708313
DXWarlock
Pro
Sheet Author
API Scripter
Thanks man! You and a few other people like The Aaron never cease to amaze me..haha. I'll start tearing it down and throwing it's parts all over the room and see what I can figure it out, and hopefully remember how to put it back together :) Does it affect any inline rolls sent to chat? or do I need to call the parts somehow inside a roll? I haven't even really started to figure out what the parts do, so looking for what to expect so I know how to implement it going in. Edit: It seems I need "customUnderscore" also. its calling it for something. But it's not in the script itself anywhere.
1574708388
Scott C.
Forum Champion
Sheet Author
API Scripter
Heh, I just realized I left that call in there. It's not actually needed, and I actually just edited the code to take it out.
1574708586
Scott C.
Forum Champion
Sheet Author
API Scripter
DXWarlock said: Does it affect any inline rolls sent to chat? or do I need to call the parts somehow inside a roll? I haven't even really started to figure out what the parts do, so looking for what to expect so I know how to implement it going in. It will affect any and all inline rolls that are sent via a whisper to the gm in a message that has a valid roll template associated with it. What roll templates you want it to respond to can be set in the validTemplates variable in handleInput by just adding them to the array as has already been done for pf_attack and pf_spell. You don't need to do any API commands or edit the roll in any way (other than ensuring it's a whisper).
1574708812

Edited 1574708841
DXWarlock
Pro
Sheet Author
API Scripter
Ah I see! Did you have this script that did exactly what I was looking to try to do, already? I mean this does exactly what I was trying to do..whats the odds? :D
1574708979

Edited 1574709070
Scott C.
Forum Champion
Sheet Author
API Scripter
Heh, the basic inline roll extraction is something that is used pretty commonly, so I just pulled the extractRoll function out, and then wrote the handleInput function to suit real quick. Only about 10 lines of custom code. Edit: And all the other extraneous stuff is just my basic script template that I use as the start point for pretty much anything I'm coding for use on R20.
1574709178

Edited 1574709460
DXWarlock
Pro
Sheet Author
API Scripter
Well thanks a ton! I would have spent the rest of the week trying to cobble something together before Saturday to do this. If only there was a way to catch and clean up rolls before sent, without having to relay them back. Or an option in each games settings tab to hide roll formulas by default. I don't mind my players knowing the math by any means. Just not when one is using that knowledge out of game to try to meta game a strategy :P Your sucking the fun out of it for the others trying not to let that knowledge effect them.
1574709457

Edited 1574709485
Scott C.
Forum Champion
Sheet Author
API Scripter
Well, you could adapt the script above to only respond to messages sent as an api command. And, as part of that, you could have it edit all the macro text for all NPCs to replace the whisper attribute call with something like "!hide " so that they'd be setup to handle it. The problem you run into is maintaining the crit/fumble detection in that set up though. There are a few ways I could see to do it, but it's probably overkill for what you're wanting. This method also has the added bonus of still allowing you to see the full roll expression.
1574709682

Edited 1574709902
DXWarlock
Pro
Sheet Author
API Scripter
Its plenty for me to get started with. I still need to tweak it, as players whisper attacks to me..they liked the idea they cant see other players numbers, only the result we describe. But I leave NPC's visible so they know I'm not just 'saying' its hitting you every single time..So need to filter out players and change all NPC's to whisper too. And it seems it doesn't send crit confirms when one happens, got to figure out how to account for that. But it still saves me 2 days of my slooow work on it, yell at it for not working at all, then figure out how to make it do 'something'...lol
1574709973
Scott C.
Forum Champion
Sheet Author
API Scripter
ahh, damn, didn't think of the crit confirms. That's gonna be trickier. As for weeding out messages whispered to you by players, just change this line: if (!/^gm$/i.test(msg.target)||!new RegExp(`^${validTemplates.join('|')}$`).test(msg.rolltemplate)){ to this: if ( !playerIsGM(msg.playerid)|| !/^gm$/i.test(msg.target)||!new RegExp(`^${validTemplates.join('|')}$`).test(msg.rolltemplate)){
1574710077

Edited 1574710218
DXWarlock
Pro
Sheet Author
API Scripter
Ah that's way easier than the way I was going to do it. I was going to look up the character sheet assigned to who sent the roll, check the "isnpc" flag and if 1 it assumes its an NPC...
1574710324
Scott C.
Forum Champion
Sheet Author
API Scripter
As for the crit detection, Gonna have to think on that. It's doable, just gonna need some finagling of the extractRoll function.
1574710408

Edited 1574710448
DXWarlock
Pro
Sheet Author
API Scripter
I don't want you to be doing all the work. I'm going to try to figure it out also. You probably will WAY before me..but just pointing out I'm going to try (and probably give up after a few hours..but I tried..).
1574710683
Scott C.
Forum Champion
Sheet Author
API Scripter
Heh, sorry, I just finished a big project at work so I'm enjoying having free time to code scripts on R20 again. You should play around with the extractRoll function and look at that. The tricky part is going to be getting the public facing roll template to display the crit confirm.
1574711265

Edited 1574711311
DXWarlock
Pro
Sheet Author
API Scripter
Will do, I'll see what I can figure out. Aaron tried to explain the .reduce and .chain to me once..I got the most bare bones cave man understanding of what it does.
1574718056

Edited 1574718106
DXWarlock
Pro
Sheet Author
API Scripter
Yep, I was right, no idea how to do it despite my bests efforts for 2 hours. I think the roll logic on when crit confirms display based on the sheets roll templates along with the way the extractRoll works (And what it needs to make the template happy). Is beyond me..I manged to totally break the script before giving up and just putting the original version back in. :)
1574718393
Scott C.
Forum Champion
Sheet Author
API Scripter
Yep, you'd need to do something like the following: detect when the roll is a crit add something to make the replaced roll a crit for the template's detection optional bonus step, only do this when it's a d20 that is rolled
1574721500

Edited 1574721837
DXWarlock
Pro
Sheet Author
API Scripter
I think I'm on the right path Something like this..to see which inline rolls can crit: var array1 = msg.inlinerolls; var attacks = [] array1.forEach(function (element) { if (element.expression.includes("cs&gt;") == true) { attacks.push(element); } }); And this to see what the raw roll was and compare it to the crit: attacks.forEach(function (element) { var critSuccess = element.results.rolls[0].mods.customCrit[0].point; var attRaw = element.results.rolls[0].results[0]['v']; if (attRaw &gt;= critSuccess) { [something here] } }); (Only took me 3 hours to figure this much out!) But not sure what to actually do with it. Or what the template needs, and where, to make it actually work. :\ Then the highlighting red/green for 1 / 20 I am totally at a loss for. (I'm using the second option you had commented out for the black boxed numbers)
1574786321
Scott C.
Forum Champion
Sheet Author
API Scripter
Ok, I think I've got it. Try replacing the extractRoll function with this version: extractRoll = function(msg){ return _.chain(msg.inlinerolls)//indicate that we're going to start doing multiple loops through this iterable thing (in this case an array of inline rolls). The result of each successive function is passed as the argument to the next function .reduce(function(m,v,k){//Loop through the object passed from the chain. m is the memo and will hold the results of our changes. k is the index of the array, v is the value of the array at that index let critPoint = (v.results.rolls[0].mods &amp;&amp; v.results.rolls[0].mods.customCrit) ? v.results.rolls[0].mods.customCrit[0].point : v.results.rolls[0].sides,//set the crit point to which we are going to compare the result total. replacement; if(_.some(v.results.rolls[0].results,(result)=&gt;{return result.v &gt;= critPoint})){//check all of the raw die rolls for whether they are a critical; replacement = `${v.results.total || 0}+1d0cs&gt;0`;//if it is a critical, add a fake critical roll that won't affect the total. This will make this a "critical" for formatting and roll template detection }else{ replacement = v.results.total || 0;//if it isn't a critical, just use the total without any additions } m[`$[[${k}]]`]=replacement;//store the created new roll in the proper index. return m; },{}) .reduce(function(m,v,k){ return m.replace(k,`[[${v}]]`);//wrap the modified result in inline roll brackets and place it into the message at the proper place },msg.content) .value(); }, I've also put some comments in about what's going on. Let me know if that works, or if you have any questions.
1574788633

Edited 1574788759
DXWarlock
Pro
Sheet Author
API Scripter
Works prefect! Thank you, time to put on my sleuth&nbsp;hat and try to figure out how this voodoo happens.
1574790205
Scott C.
Forum Champion
Sheet Author
API Scripter
glad to hear. Hit me up if you've got any questions about it.
1574790736

Edited 1574791122
DXWarlock
Pro
Sheet Author
API Scripter
Only one I have so far is a curiosity more than a script question itself. I am perplexed how the second roll is not whispered.. Both look like this when I log the message content: First sent: /w gm ||rowid=-LuUU2C-1xU3kyQkcJOi||0 {{rounded=rounded}}.... One script sends: /w gm ||rowid=-LuUU2C-1xU3kyQkcJOi||0 {{rounded=rounded}}..... There is a space difference..but surely that wouldn't stop the second from whispering. Not that I want it to it's working great exactly how it should, just made me do that confused dog head tilt when I saw it.
1574791432

Edited 1574791504
DXWarlock
Pro
Sheet Author
API Scripter
OH I think I see why..the msg.rolltemplate that content does not show. Original message is: /w gm ${pf_attack} [..roll stuff..] The script sends: ${pf_attack} /w gm [..roll stuff..] And since /w gm doesn't fit in the roll template format it just doesn't show. Not a complaint of how you have it, just stumped me on HOW it was working as intended at first.
1574792210
Scott C.
Forum Champion
Sheet Author
API Scripter
hmm, interesting, the "/w gm" shouldn't be part of the msg.content at all, which is why I didn't handle it. At a guess there's a duplication in your macro maybe? so you wind up having something like "/w gm /w gm &amp;{template:pf_attack} ..." in your original message? Regardless, either the space or the template declaration will prevent the whisper from occurring. The space would cause the message sent to look like "/w gm whatever text" in chat (the /w gm would actually be displayed), while the roll template declaration hides it like you had surmised. Also, realized that I used the original msg detection instead of the revised one that will only pick up messages you whisper to yourself as gm. You'll just need to redo the replacement we had discussed before .
1574792620

Edited 1574792628
DXWarlock
Pro
Sheet Author
API Scripter
Already ahead of you, got that in already&nbsp; :) There might be another script that's putting in the extra /w gm somewhere. Ill poke and see if I can find it. Can't think of anything that would off the top of my head. It's working with it regardless so not a crucial. You saved me a ton of work over the course of 30 hours of smashing together a WAY less elegant script to sledgehammer a 'well..it technically works' solution..:D
1574793485
Scott C.
Forum Champion
Sheet Author
API Scripter
happy to help, and got me to actually look at the extractRoll function and know what it's doing rather than just copy/pasting it in to all my scripts and using it as is.
1574793547
DXWarlock
Pro
Sheet Author
API Scripter
Whats wrong with that? LOL 50% of my scripts are black magic boxes The Aaron gave me I have no idea how they do what they do...