OK, it only took a total of 9 lines of code. And a few moments of me being an idiot. Here is the new version that will work as a Plugger plug-in to return a value from the script. I will leave it to Aaron whether or not he wants to incorporate this into his regular upkeep/re-release of the script. Note, to make this useful to you as a meta-return (getting information that you can use in another script), the script can only return 1 data point at a time. In effect, it returns the distance between the first 2 things it sees between the set of tokens selected and the token IDs included on the command line. If you have 2 things selected and 3 tokens in the command line, it will return only a single value -- not rows and rows. That means make sure you have explicit references to your tokens to make sure you're getting the correct data in return. To use this in its most basic form, you'd need Plugger, too. Then, to build/use your Plugger line... 1) wrap everything in {&eval} and {&/eval} tags 2) drop the exclamation point before measure 3) enclose all of measure's arguments in parentheses For example, with 2 tokens selected, you would only need: {&eval}measure(){&/eval} With one token selected you would have to explicitly reference one: {&eval}measure(@{mook|token_id}){&/eval} If you want to do targeting, you lose your selected tokens (a limitation of Roll20, not metascripts), so you either have to target start & finish tokens, or include your selected token's id in the command: {&eval}measure(@{selected|token_id} @{target|Target 1|token_id}){&/eval} All of those are the same requirements of the script as originally existed; I just wanted to show how to translate them into the Plugger syntax. 4) put that statement in the macro command line for your OTHER script. The command line must start with an exclamation point to trigger the API. Make sure the Plugger statement is in the spot where the distance value should be returned. For instance: !AttackThat --range|{&eval}measure(@{selected|token_id} @{target|Target 1|token_id}){&/eval} By the time the AttackThat script sees the command line, Plugger will have returned a value, so the line will now read: !AttackThat --range|22 Other Metascript Interactions Simple Message with ZeroFrame Once you have the output of the distance, you can drop a normal chat message out with ZeroFrame, allowing you to build a template output: !&{template:default}{{name=Distance from @{selected|token_name} to 2 Targets}}{{@{target|Target 1|token_name}={&eval}measure(@{selected|token_id} @{target|Target 1|token_id}){&/eval} }}{{@{target|Target 2|token_name}={&eval}measure(@{selected|token_id} @{target|Target 2|token_id}){&/eval} }}{&simple} Yes, this begins to just return the same chat output of the script originally, but you can combine it with the methods, below, to do more with the returned numbers because you have them "in context" at the moment you're building your output. Deferred Inline Roll with ZeroFrame If you want the value returned to be used in an inline roll, you have to "hide" the inline roll. Roll20 will try to parse the inline roll the first time it sees it, but that's before the API (Plugger) has had a chance to return the value you need. To defer a roll, use ZeroFrame and include backslashes (and brackets) to denote "cycles" for deferring a roll. An inline roll deferred one pass (to allow Plugger to return a value) would look like: [\][\] ...roll equation here... \]\] In practice, this could be a to-hit number: [\][\]{1d20+@{selected|AttackSkill}}>{&eval}measure(@{selected|token_id} @{target|Target 1|token_id}){&/eval} \]\] ...or some other math operation. Code // Github: <a href="https://github.com/shdwjk/Roll20API/blob/master/Measure/Measure.js" rel="nofollow">https://github.com/shdwjk/Roll20API/blob/master/Measure/Measure.js</a>
// By: The Aaron, Arcane Scriptomancer
// Contact: <a href="https://app.roll20.net/users/104025/the-aaron" rel="nofollow">https://app.roll20.net/users/104025/the-aaron</a>
var Measure = Measure || (function () {
'use strict';
var version = '0.3.3',
lastUpdate = 1530335089,
checkInstall = function () {
log('-=> Measure v' + version + ' <=- [' + (new Date(lastUpdate * 1000)) + ']');
},
handleInput = function (msg) {
var args,
pageid,
page,
measurements,
whisper = false,
who;
if (msg.type !== "api") {
return;
}
args = msg.content.split(/\s+/);
switch (args.shift()) {
case '!wmeasure':
whisper = true;
who = (getObj('player', msg.playerid) || { get: () => 'API' }).get('_displayname');
// break; // Intentional fall through
case '!measure':
measurements = _.chain(_.union(args, _.pluck(msg.selected, '_id')))
.uniq()
.map(function (t) {
return getObj('graphic', t);
})
.reject(_.isUndefined)
.map(function (t) {
pageid = t.get('pageid');
return {
name: t.get('name') || "Token @ " + Math.round(t.get('left') / 70) + ',' + Math.round(t.get('top') / 70),
x: t.get('left'),
y: t.get('top')
};
})
.reduce(function (m, t, k, l) {
_.each(_.rest(l, k + 1), function (t2) {
m.push({
name1: t.name,
name2: t2.name,
distance: (Math.sqrt(Math.pow((t.x - t2.x), 2) + Math.pow((t.y - t2.y), 2)) / 70)
});
});
return m;
}, [])
.value()
;
page = getObj('page', pageid);
if (page) {
if (msg.eval) {
return measurements.length ? Math.round(page.get('scale_number') * measurements[0].distance, 2) : 0;
}
_.chain(measurements)
.reduce(function (m, e) {
var d = Math.round(page.get('scale_number') * e.distance, 2);
m.push("<li>" + e.name1 + " to " + e.name2 + ": <b>" + d + " " + page.get('scale_units') + "</b></li>");
return m;
}, [])
.join('')
.tap(function (o) {
sendChat('Measure', (whisper ? '/w "' + who + '"' : '/direct') + ' <div><b>Measurements:</b><ul>' + o + '</ul></div>');
});
}
break;
}
},
measure = function (m) { return handleInput(m); },
registerEventHandlers = function () {
on('chat:message', handleInput);
try {
Plugger.RegisterRule(measure);
} catch (error) {
log(error);
}
};
return {
CheckInstall: checkInstall,
RegisterEventHandlers: registerEventHandlers,
};
}());
on('ready', function () {
'use strict';
Measure.CheckInstall();
Measure.RegisterEventHandlers();
});