Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

Cant get distance API to work

I stumbled across an API for measuring distances between 2 tokens, but cant get it to work <a href="https://gist.github.com/7H3LaughingMan/e5124cfa7c8" rel="nofollow">https://gist.github.com/7H3LaughingMan/e5124cfa7c8</a>... I just get distance of undefined when i use it with the suggested macro. !range @{target|1st Target|token_id} @{target|2nd Target|token_id}
1519334470
The Aaron
Pro
API Scripter
That will only measure the distance on hex grids. I've got a measure script for regular maps:&nbsp;&nbsp; <a href="https://github.com/shdwjk/Roll20API/blob/master/Me" rel="nofollow">https://github.com/shdwjk/Roll20API/blob/master/Me</a>... Commands are: !measure -- prints the distance between all selected tokens to chat. !wmeasure -- whsipers the distance between all selected tokens to the person running the command.
1519339583
keithcurtis
Forum Champion
Marketplace Creator
API Scripter
The Aaron said: That will only measure the distance on hex grids. I've got a measure script for regular maps:&nbsp;&nbsp; <a href="https://github.com/shdwjk/Roll20API/blob/master/Me" rel="nofollow">https://github.com/shdwjk/Roll20API/blob/master/Me</a>... Commands are: !measure -- prints the distance between all selected tokens to chat. !wmeasure -- whsipers the distance between all selected tokens to the person running the command. Added.
We use Hex Grids. Hero System, is Hex, rather than Squares.
1519482097
The Aaron
Pro
API Scripter
Hmmm. And the map page you’re on is set up as a hex grid (not just having a map image with a hex grid already superimposed in it?)?
1519486504
GiGs
Pro
Sheet Author
API Scripter
I tested this script on hex grids, and it worked fine. I admit, the way Aaron posted phrased his early comment ("That will only measure the distance on hex grids.&nbsp;I've got a measure script for regular maps"), I wasn't sure it would work for hexes either, but it does.&nbsp;
1519488869

Edited 1519564247
Sure im doing something daft. Works with HEX(V). Ive amended the script a bit for what I wanted.&nbsp; It now gives the range modifier for Hero system, when selecting 2 tokens. Only a minor tweak, but will save us having to keep looking the range mods up :-) // Computes the distances between 2 tokens by ID // Use the macro target picker to fill in the ID // Script command // !range @{target|1st Target|token_id} @{target|2nd Target|token_id}&nbsp; on("chat:message", function(msg) { &nbsp; &nbsp; if(msg.type == "api" && msg.content.indexOf("!range") != -1) { &nbsp; &nbsp; &nbsp; &nbsp; log(msg);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var n = msg.content.split(" ", 4) &nbsp; &nbsp; &nbsp; &nbsp; var cmd = n[0]; &nbsp; &nbsp; &nbsp; &nbsp; var id1 = n[1]; &nbsp; &nbsp; &nbsp; &nbsp; var id2 = n[2]; &nbsp; &nbsp; &nbsp; &nbsp; var token1 =&nbsp; getObj("graphic", id1);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var token2 =&nbsp; getObj("graphic", id2); &nbsp; &nbsp; &nbsp; &nbsp; if (token1 && token2) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var distance = tokenDistance(token1, token2); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /* sendChat("", "/desc Distance between Token1 & Token2 is " + distance); */ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendChat("", "/desc Range modifier is " +rangeMod(distance)); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rangeMod(distance) &nbsp; &nbsp; &nbsp; &nbsp; } else { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (!token1) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendChat("Range Finder","/w " + msg.who + " Could not locate first token!"); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (!token2) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sendChat("Range Finder","/w " + msg.who + " Could not locate second token!"); &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } }); function tokenDistance(token1, token2) { &nbsp; &nbsp; if (token1.get('pageid') != token2.get('pageid')) { &nbsp; &nbsp; &nbsp; &nbsp; log('Cannot measure distance between tokens on different pages'); &nbsp; &nbsp; &nbsp; &nbsp; return; &nbsp; &nbsp; } &nbsp; &nbsp; var distance; &nbsp; &nbsp; var page = getObj('page', token1.get('pageid')); &nbsp; &nbsp; var gridType = page.get('grid_type'); &nbsp; &nbsp; switch(gridType) { &nbsp; &nbsp; &nbsp; &nbsp; case 'hex': &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; distance = hexVDistance([token1.get("left"), token1.get("top")], [token2.get("left"), token2.get("top")]); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; case 'hexh': &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; distance = hexHDistance([token1.get("left"), token1.get("top")], [token2.get("left"), token2.get("top")]); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; } &nbsp; &nbsp; return distance; } function hexHDistance(unit1, unit2) { &nbsp; &nbsp; var q1, q2, r1, r2; &nbsp; &nbsp; q1 = Math.round((unit1[0] - 46.48512749037782) / 69.58512749037783); &nbsp; &nbsp; r1 = Math.round((unit1[1] - 39.8443949917523) / 39.8443949917523); &nbsp; &nbsp; r1 = Math.floor(r1 / 2); &nbsp; &nbsp; q2 = Math.round((unit2[0] - 46.48512749037782) / 69.58512749037783); &nbsp; &nbsp; r2 = Math.round((unit2[1] - 39.8443949917523) / 39.8443949917523); &nbsp; &nbsp; r2 = Math.floor(r2 / 2); &nbsp; &nbsp; return cubeDistance(oddQToCube(q1, r1), oddQToCube(q2, r2)); } function hexVDistance(unit1, unit2) { &nbsp; &nbsp; var q1, q2, r1, r2; &nbsp; &nbsp; q1 = Math.round((unit1[0] - 37.59928099223013) / 37.59928099223013); &nbsp; &nbsp; r1 = Math.round((unit1[1] - 43.86582782426834) / 66.96582782426833); &nbsp; &nbsp; q1 = Math.floor(q1 / 2); &nbsp; &nbsp; q2 = Math.round((unit2[0] - 37.59928099223013) / 37.59928099223013); &nbsp; &nbsp; r2 = Math.round((unit2[1] - 43.86582782426834) / 66.96582782426833); &nbsp; &nbsp; q2 = Math.floor(q2 / 2); &nbsp; &nbsp; return cubeDistance(oddRToCube(q1, r1), oddRToCube(q2, r2)); } function oddRToCube(q, r) { &nbsp; &nbsp; var x, y, z; &nbsp; &nbsp; x = q - (r - (r & 1)) / 2; &nbsp; &nbsp; z = r; &nbsp; &nbsp; y = -x - z; &nbsp; &nbsp; return [x, y, z]; } function oddQToCube(q, r) { &nbsp; &nbsp; var x, y, z; &nbsp; &nbsp; x = q; &nbsp; &nbsp; z = r - (q - (q & 1)) / 2; &nbsp; &nbsp; y = -x - z; &nbsp; &nbsp; return [x, y, z]; } function cubeDistance(cube1, cube2) { &nbsp; &nbsp; return Math.max(Math.abs(cube1[0] - cube2[0]), Math.abs(cube1[1] - cube2[1]), Math.abs(cube1[2] - cube2[2])); } function rangeMod(distR) { &nbsp; &nbsp; switch(true) { &nbsp; &nbsp; &nbsp; &nbsp; case (distR &lt; 5): &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return 0; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; case (distR &lt; 9): &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return -2; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; case (distR &lt; 17): &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return -4; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; case (distR &lt; 33): &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return -6; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; case (distR &lt; 65): &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return -8; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break; &nbsp; &nbsp; &nbsp; &nbsp; default: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; log('not a coded range'); &nbsp; &nbsp; } }
1519492162

Edited 1519596348
GiGs
Pro
Sheet Author
API Scripter
Does it not work for Hex(H) ? Now that you mention it, I only tried if for vertical hexes. I notice your split splits into 4 items, but you only actually use 3. Is this intentional? You can improve your rangeMod function (for some interpretations of 'improve'). Using a switch there is weird to me, I'd have used if then else, but it works so it's good :) function rangeMod(distR) { if(Math.round(distR) &lt; 5) { return 0; } else { let r = Math.round(distR); // round to a proper number of units - //not strictly necessary but handles when something looks like 4 hexes away //but is actually 4.4 units of distance and would otherwise be given the wrong range mod. // if this isn't an issue, replace each Math.round(distR) with just distR. let m = Math.ceil(Math.log(distR) / Math.log(2)) - 2; // this expression calculates the number of doublings of range above 4, so 5-8 = 1, 9-16 = 2, etc. return -2 * m; // convert to the actual modifier. // if you want to do the whole thing as a single line: //return -2* (Math.ceil(Math.log(Math.round(distR)) / Math.log(2))); } } Math.log(y) / Math.log(x);
1519501257
The Aaron
Pro
API Scripter
OH!&nbsp; I have this vague recollection that the name of the grid type for horizontal hexes changed at some point in the past.&nbsp; Change: case 'hexh': to case 'hexr': and it will probably start working.
1519537411
Loren the GM
Pro
Marketplace Creator
The Aaron said: That will only measure the distance on hex grids. I've got a measure script for regular maps:&nbsp;&nbsp; <a href="https://github.com/shdwjk/Roll20API/blob/master/Me" rel="nofollow">https://github.com/shdwjk/Roll20API/blob/master/Me</a>... Commands are: !measure -- prints the distance between all selected tokens to chat. !wmeasure -- whsipers the distance between all selected tokens to the person running the command. And yet another great script instantly added to my games. The Aaron, thank you!
GG I used Switch as I believe it is the more efficient code path. Once the code hits a match, it drops out of the switch. If you use a series of If / then / else then every line of code is parsed.
1519567887
The Aaron
Pro
API Scripter
I would guess that you lose that speed benefit when you don't have constant cases, but it's unlikely to matter either way with so few of them.&nbsp; You probably lose any benefit you might gain by calling it twice. =D I'd probably write it like this: let rangeMod = _.memoize((distR) =&gt; Math.ceil(-Math.pow( (distR-1) & 0xfffffff80, .5))); (That does mean you get a -0 for an answer, but that's still technically accurate. =D) Assuming it holds to the same pattern, that will work for pretty much any distance.&nbsp; ( rangeMod(123456) == -351 )&nbsp; If there's a maximum cap of -8, you could add a Math.max() call around it: let rangeMod = _.memoize((distR) =&gt; Math.max(-8,Math.ceil(-Math.pow( (distR-1) & 0xfffffff80, .5)))); Since it's a Pure Function (no side effects, a given input always results in the same output), you can wrap it in _.memoize() so that it caches the results of the call and becomes a lookup on subsequent calls with the same input.&nbsp; Your existing function is also a Pure Function, so you could get the same caching benefit from memoize'ing it.
1519568243
The Aaron
Pro
API Scripter
G G said: let m = Math.ceil(Math.log(distR) / Math.log(2)) - 2; Man, I KNEW I should have read your answer first!&nbsp; That's the math'ing I was trying to think of this morning!
GG, now read your post properly, so see what you mean. I used the Switch as my math was to poor to work out the formula for the ranges.&nbsp;
1519596288
GiGs
Pro
Sheet Author
API Scripter
A less math-intensive method of working out the distance mod also occurred to me just after posting: function rangeMod(distR) { let mod = 0; while (Math.round(distR) &gt; 4) { distR = distR/2; mod -= 2; } return mod; } You can remove the Math.round part if you don't mind treating 4.4 as above 4. For my Champions games, I'd treat that as equal to 4.