Hey, complete beginner here. I'm attempting to remove rolls and modifiers from monster attacks, when hovering and I've got something that works ish . My current problem is getting crits to work. Would love any feedback, I've never really written purely functional code before, so this all is pretty new for me. Also this is for Pathfinder 1E, no idea how different the roll templates are for other games/editions. My current thought on crits is to fake a roll so like a 1d1cs1 and then I can later sub out that one. Edit: My approach was to whisper rolls to the GM. Then take that message and replace the references to the inlinerolls with [[total]] and then resend the message back out to the players. Edit 2: Using 1d1cs1+total-1 works! I updated the code below to use that. Edit 3: Updated code and fixed skill check and saving throw displays. Edit 4: Changed 1d1cs1 on crits to 1d0cs0 which preserves the crit threat of green (rather than blue which 1d1cs1 was producing). //Generic util to grab and return the string between start and end (omitted) function parseString ( message , start , end ) { var name = "" ; var indexStart = message . indexOf ( start ); if ( indexStart != - 1 ) { indexStart += start . length ; var endIndex = message . indexOf ( end , indexStart ); name = message . substring ( indexStart , endIndex ); } return name ; } //Grabs the attack name off the string. function parseAttackName ( message ) { const start = "{{name=" ; const end = "}}" ; return parseString ( message , start , end ); } //Grabs the character name off the string. function parseCharacterName ( message ) { const start = "{{charname=" ; const end = "}}" ; return parseString ( message , start , end ); } //Grabs the crit start off the roll string. function parseCritStart ( message ) { const start = " \" point \" :" ; const end = "}" ; return parseInt ( parseString ( message , start , end )); } //Grabs any raw roll off the string. function parseAttackRawRoll ( message ) { const start = "results \" :[{ \" v \" :" ; const end = "}" ; return parseInt ( parseString ( message , start , end )); } //Grab the total off the string. function parseTotal ( message ) { const start = " \" total \" :" ; const end = "," ; return parseString ( message , start , end ); } //Grabs the header of this component up until the inlineroll reference function parseHeader ( message ) { const start = "{" ; const end = "$" ; var name = "" ; var indexStart = message . indexOf ( start ); if ( indexStart != - 1 ) { indexStart += start . length ; var endIndex = message . indexOf ( end , indexStart ); name = message . substring ( indexStart - 1 , endIndex ); } return name ; } //Break the message up into {{}}s chunks. function splitMessageIntoComponents ( message ) { var array = new Array (); const start = "{{" ; const end = "}}" ; var curIndex = 0 ; while ( curIndex < message . length ) { var indexStart = message . indexOf ( start , curIndex ); if ( indexStart != - 1 ) { var endIndex = message . indexOf ( end , indexStart ); endIndex += end . length ; while (( endIndex ) < message . length && message [ endIndex ] == '}' ) { endIndex ++ ; } array . push ( message . substring ( indexStart , endIndex )); curIndex = endIndex ; } else { curIndex = message . length ; } } return array ; } //Checks if this string is referencing inlinerolls. function shouldUseInline ( message ) { return containsString ( message , "$[[" ); } function containsString ( message , check ) { return message . indexOf ( check ) != - 1 ; } function isAttack ( message ) { const roll = "roll" ; var rollIdx = message . indexOf ( roll ); attack = rollIdx != - 1 ; if ( attack ) { rollIdx += roll . length ; attack = ( message [ rollIdx ] == '=' ) || ( message [ rollIdx + 1 ] == '=' ); } return attack ; } function isCritThreat ( aCritStart , aRoll ) { return aRoll >= aCritStart ; } //Raised whenever a chat message is sent. on ( 'chat:message' , function ( msg ) { //If we've whispered rolls to the GM, go ahead and send it out to chat stripped of modifiers if ( msg . type == "whisper" && msg . target == "gm" && msg . inlinerolls ) { //Get an array of all {{}}'s var args = splitMessageIntoComponents ( msg . content ); //Start building the message to send. var chatMsg = "&{template:npc} " ; var inlineIndex = 0 ; for ( var i = 0 ; i < args . length ; ++ i ) { //Current arg references the inline rolls. if ( shouldUseInline ( args [ i ])) { //We want to grab the total off to squelch the roll modifiers. var jsonString = JSON . stringify ( msg . inlinerolls [ inlineIndex ]); var total = parseTotal ( jsonString ); var append = "" ; if ( total != "" ) { var isCrit = ( isAttack ( args [ i ]) && isCritThreat ( parseCritStart ( jsonString ), parseAttackRawRoll ( jsonString ))); if ( ! isCrit ) { //Rebuild the component with only the total. append = parseHeader ( args [ i ]) + "[[" + total + "]]}}" ; } else { append = parseHeader ( args [ i ]) + "[[1d0cs0+" + total + "]]}}" ; } } inlineIndex ++ ; } else { //No args, just pass this back out append = args [ i ]; } chatMsg += append ; } //Grab the character who whispered var characterName = parseCharacterName ( msg . content ); var characters = findObjs ({ _type: "character" , name: characterName }); //Send message out as them (NOTE: Ignore whispertype.) var char = characters [ 0 ]; sendChat ( "character|" + char . get ( '_id' ), chatMsg ); } });