Skip to content

Instantly share code, notes, and snippets.

@itspacrat
Forked from Parmeisan/Macro Parameters
Created January 18, 2021 18:26
Show Gist options
  • Save itspacrat/fc08f1be0bfde2d321d90a6588d20847 to your computer and use it in GitHub Desktop.
Save itspacrat/fc08f1be0bfde2d321d90a6588d20847 to your computer and use it in GitHub Desktop.
Roll20 - Macro Parameters
// 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