Last active
February 10, 2021 16:20
-
-
Save Parmeisan/9822139 to your computer and use it in GitHub Desktop.
Roll20 - Macro Parameters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Purpose: Allows you to automatically fill in macro variables, from chat or from another macro. | |
// Usage: #MacroName(param1,param2) | |
// Do NOT include a space after MacroName and before the (. If you do that, Roll20 intercepts it | |
// before sending it to the chat, and there's nothing I can do to help you. :) | |
var parm_macro_params = parm_macro_params || {}; | |
// Interrupts all chat messages to check for macros. | |
on("chat:message", function(msg) | |
{ | |
parm_macro_params.debug(msg); | |
parm_macro_params.replaceMacroParams(msg.content); | |
// Nothing need be done with the result, it was handled already. | |
}); | |
// Interrupts all chat messages to see if we've been waiting for them. | |
on("chat:message", function (msg) | |
{ | |
if (msg.who.indexOf("#") == 0) | |
{ | |
parm_macro_params.checkForWaiting(msg); | |
} | |
}); | |
// Namespace | |
parm_macro_params.debugging = false;//true;// | |
parm_macro_params.waiting_macro_call; | |
parm_macro_params.macro_results = []; | |
parm_macro_params.speak_as = "Params"; | |
// The meat of it all. This looks for things of the form #m(a), parses them, and handles them. | |
parm_macro_params.replaceMacroParams = function(t_in) | |
{ | |
var ns = parm_macro_params; | |
var t = ns.replaceAll(t_in, " ", ""); | |
// Ignore all text before and after the macros | |
ns.debug("ReplaceMacroParams: " + t); | |
var pos_macro = t.indexOf("#"); | |
while (pos_macro >= 0) | |
{ | |
ns.debug("Found macro at " + pos_macro); | |
// If a macro is entered and there is an open bracket *before* any spaces... | |
var pos_bracket = t.indexOf("(", pos_macro); | |
if (pos_bracket >= 0) | |
{ | |
var pos_space = t.indexOf(" ", pos_macro); | |
if (pos_space < 0) pos_space = t.length; | |
ns.debug(pos_bracket + " < " + pos_space) | |
if (pos_bracket < pos_space) | |
{ | |
// Then we need to start replacing parameters. | |
var macro_name = t.substring(pos_macro + 1, pos_bracket); | |
ns.debug("Macro name " + macro_name); | |
var macro_text = ns.getMacroText(macro_name); | |
var pos_comma = t.indexOf(",", pos_macro); | |
var pos_bracket2 = t.indexOf(")", pos_macro); | |
// Loop over parameters - everything found between ( and ) | |
var pos_curr = pos_bracket; | |
var pos_next = pos_comma; | |
ns.debug(pos_curr + " < " + pos_bracket2); | |
while (pos_curr < pos_bracket2) | |
{ | |
if (pos_next < 0 || pos_next > pos_bracket2) pos_next = pos_bracket2; | |
var param_value = t.substring(pos_curr + 1, pos_next); | |
ns.debug("Parameter value, orig: " + param_value); | |
for (var i = 0; i < ns.countInstances(param_value, "("); i += 1) | |
{ | |
pos_comma = t.indexOf(",", pos_bracket2 + 1); | |
pos_bracket2 = t.indexOf(")", pos_bracket2 + 1); | |
pos_next = pos_comma; | |
if (pos_next < 0 || pos_next > pos_bracket2) pos_next = pos_bracket2; | |
param_value = t.substring(pos_curr + 1, pos_next); | |
} | |
ns.debug("Parameter value, with brackets: " + param_value); | |
// Risky business, but what if the parameter is a macro? Time for recursion! | |
var param_result = param_value; | |
if (param_value.indexOf("#") >= 0) | |
{ | |
param_result = ns.replaceMacroParams(param_value); | |
ns.debug(param_result); | |
if (param_result.indexOf("!RESULT-") >= 0) | |
{ | |
// This will have to be handled later. Save this, and break out of everything. | |
ns.debug("Waiting replace: " + t); | |
ns.waiting_macro_call = t.replace(param_value, param_result); // macro_text | |
ns.debug(ns.waiting_macro_call); | |
return param_value; | |
} | |
} | |
ns.debug("Parameter value, post recursion " + param_value); | |
// Find the parameter name in the macro | |
ns.debug("macro: " + macro_text); | |
var param_pos_1 = macro_text.indexOf("?{"); | |
var param_pos_2 = macro_text.indexOf("}", param_pos_1); | |
ns.debug("param " + param_pos_1 + ", " + param_pos_2); | |
if (param_pos_1 >= 0 && param_pos_2 >= 0) | |
{ | |
var param_name = macro_text.substring(param_pos_1, param_pos_2 + 1); | |
ns.debug("Parameter name " + param_name); | |
// Replace it with the value we just found | |
macro_text = ns.replaceAll(macro_text, param_name, param_value); | |
} else {} // Just ignore extra parameters | |
// Now find the next positions | |
pos_curr = pos_next; | |
pos_next = t.indexOf(",", pos_curr + 1); | |
} | |
// Now we have a macro_name and a macro_text to replace it with. No result yet, though. | |
// We have to flag this whole thing to not actually be run until the result comes in. | |
// Send out the query of macro_text, then replace the first instance of the macro's name. | |
// NOTE: Changing to replaceAll should mean each macro, if given the same parameters, | |
// would run only once. I think most often you'd want it to run multiple times. | |
var macro_full = t.substring(pos_macro, pos_bracket2 + 1); | |
ns.debug("Full macro: " + macro_full); | |
var waiting_name = ns.getWaitingName(macro_name); | |
t = t.replace(macro_full, "!RESULT-" + waiting_name + "!"); | |
ns.debug(t); | |
ns.debug("#" + waiting_name + ": " + macro_text); | |
sendChat("#" + waiting_name, macro_text); | |
ns.macro_results[waiting_name] = "WAITING"; | |
} | |
} | |
pos_macro = t.indexOf("#", pos_macro + 1); | |
} | |
return t; | |
}; | |
// This will be triggered when a sendChat message comes through. | |
// Or technically if a user whose name starts with "#" talks, but I don't think it'll do much then. | |
parm_macro_params.checkForWaiting = function(msg) | |
{ | |
var ns = parm_macro_params; | |
var result_for = msg.who.substring(1); // Remove the # | |
ns.debug(result_for); | |
ns.macro_results = ns.macro_results.splice(result_for); | |
ns.debug(ns.waiting_macro_call); | |
if (!ns.isnull(ns.waiting_macro_call)) | |
{ | |
var inlineTotal = ns.getInlineTotal(msg, 1); | |
if (!ns.isnull(inlineTotal)) | |
{ | |
ns.waiting_macro_call = ns.waiting_macro_call.replace("!RESULT-" + result_for + "!", inlineTotal); | |
ns.debug("Check it: " + ns.waiting_macro_call); | |
if (!_.contains(ns.macro_results, "WAITING")) // It should be empty, really. | |
{ | |
// If we were waiting for something, then go back to that. | |
// However, and this is critical, flag that we're no longer waiting. | |
var macro_call = ns.waiting_macro_call; | |
ns.waiting_macro_call = ""; // YES, it needs to happen first. | |
ns.replaceMacroParams(macro_call); | |
} | |
} | |
} | |
}; | |
// There's totally a better way, but this gets the total from an inline roll. | |
// It assumes that it's the only one on the line, but my code doesn't use this | |
// unless it is anyway. (I was having trouble with the better way.) | |
parm_macro_params.getInlineTotal = function(msg, i) | |
{ | |
var ns = parm_macro_params; | |
var total = ""; | |
//debug("======>" + msg.inlinerolls[i]); | |
var roll = JSON.stringify(msg.inlinerolls); | |
if (!ns.isnull(roll)) | |
{ | |
var pos = roll.lastIndexOf("total"); | |
pos = roll.indexOf(":", pos); | |
var pos2 = roll.indexOf(",", pos); | |
total = roll.substring(pos + 1, pos2); | |
} | |
return total; | |
}; | |
// Get the text of a macro: | |
parm_macro_params.getMacroText = function(n) | |
{ | |
var ns = parm_macro_params; | |
var m = findObjs({ _name: n, _type: "macro" })[0]; | |
if (ns.isnull(m)) return ""; | |
else return m.get("action"); | |
}; | |
// The macro might be in there more than once. So just check if we're already waiting for it. | |
// This way we get !RESULTS-FIGHT! and !RESULTS-FIGHT-2! instead of overwriting the first. | |
parm_macro_params.getWaitingName = function(m) | |
{ | |
var ns = parm_macro_params; | |
var retVal = m; | |
var limit = 30; | |
if (ns.macro_results[m] == "WAITING") | |
{ | |
var count = 2; | |
// I've put a hard limit on how many to check for, because I don't want | |
// infinite loops and really, how many would any sane person ever need? | |
while (count < limit && ns.macro_results[m + "-" + count] == "WAITING") count++; | |
if (count >= limit) ns.error("Too many functions."); | |
retVal = m + "-" + count; | |
} | |
return retVal; | |
}; | |
// My function library | |
parm_macro_params.debug = function(s) { if (parm_macro_params.debugging) log(s); }; | |
parm_macro_params.error = function(s) { sendChat(parm_macro_params.speak_as, whisper + "Error: " + s); }; | |
parm_macro_params.info = function(s) { sendChat(parm_macro_params.speak_as, whisper + s); }; | |
parm_macro_params.nvl = function(o, v) { return parm_macro_params.isnull(o) ? v : o; }; | |
parm_macro_params.isnull = function(o) { return typeof o == 'undefined' || o == null || o.length == 0; }; | |
parm_macro_params.replaceAll = function(s, a, b) { return s.split(a).join(b); }; | |
parm_macro_params.countInstances = function(s, a) { return s.split(a).length - 1; }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment