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

[Utility] Functions to return an array of Tokens within a circle, arc, or linear path

1403473025

Edited 1403473250
As suggested by Aaron, I have moved some of my more math-heavy code out from my Spell functions into separate Utility functions, that return an array of Token graphic objects. The returned array is similar to that returned by findObjs(), except that it ONLY included those tokens that 1) are representing a defined Character, 2) lie within a specific kind of shape - either a complete circle, an arc of a circle, or a linear path, and 3) have a value in an attribute named Attitude that matches the string passed in at charType. (This allows for selection of 'PC's only, or 'ally', 'all', 'monster', etc.) &lt;script src=" <a href="https://gist.github.com/Aeristan/ac09b4dbfb65e5de4ad1.js&quot;&gt;&lt;/script" rel="nofollow">https://gist.github.com/Aeristan/ac09b4dbfb65e5de4ad1.js"&gt;&lt;/script</a> &gt; Those who have been looking at my first two spells have already seen the output of the Circle and Cone functions; the code has merely been generalized into a stand-alone utility. The third one, the linear path, was a little harder, and I had to look up at equation I needed at <a href="http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html" rel="nofollow">http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html</a> I don't have a fully coded spell that uses this yet, but here are a couple test cases to prove that it works: Does anyone have a cool graphic that I could use for a Lightning Bolt?
1403474906
Lithl
Pro
Sheet Author
API Scripter
Very nice. A few comments: Rather than Array.prototype.forEach, you have access to _.each. While _.each delegates the processing to Array.prototype.forEach if possible, not every browser has it (such as IE8). In that same vein, rather than collecting all of the tokens on the page with findObjs and then iterating over them with forEach or each, you could use filterObjs to do it all in one step. You should include a means to hit tokens which don't represent a character. Many GMs have NPCs which aren't represented by characters, and your targeting would ignore them. If you don't utilize the second point above, you can used Array.prototype.push to append things to an array, rather than i=arr.length;arr[i]=foo; (You can also initialize arrays with simply "[]" instead of "new Array()") An example of the second point (removing comments and debug): var result = filterObjs(function(obj) { if(obj.get('type') != 'graphic' || obj.get('subtype') != 'token' || obj.get('pageid') != pageID) return false; var tokenName = obj.get('name'); var representsID = obj.get('represents'); if (representsID == '' && representingCharactersOnly) return false; var correctDistance = false; var correctAngle = false; var correctWidth = false; var correctCharType = false; var d = distanceBetweenPoints(x, y, obj.get("left"), obj.get("top")); var thisCharType = 'monster'; var a = getAttrByName(representsID, 'Attitude'); if (a != null) { thisCharType = a; } if (d &lt;= distPixels) { correctDistance = true; } var point_dist = distanceOfPointFromLine(tok.get('left'), tok.get('top'), x, y, x2, y2); if (point_dist &lt;= (widthPixels / 2)) { correctWidth = true; } var point_angle = angleFromPointToPointInDegrees(x, y, tok.get('left'), tok.get('top')); if ((Math.abs(point_angle - direction) &lt; 90 ) || (Math.abs((point_angle - 360) - direction) &lt; 90 )) { correctAngle = true; } if (charType == thisCharType) { correctCharType = true; } else if (charType == 'all') { correctCharType = true; } else if (charType == 'NPC') { if ((thisCharType != 'PC') && (thisCharType != 'monster')) { correctCharType = true; } } else if (charType == 'ally') { if ((thisCharType == 'PC') || (thisCharType == 'helpful') || (thisCharType == 'friendly')) { correctCharType = true; } } else if (charType == 'enemy') { if ((thisCharType == 'unfriendly') || (thisCharType == 'hostile') || (thisCharType == 'monster')) { correctCharType = true; } } return (correctDistance == true) && (correctCharType == true) && (correctAngle == true) && (correctWidth == true); }); The first three lines of the callback are the fail-fast to make the filter equivalent to the findObjs call you would have made -- if the rest of the callback were replaced with "return true", the results set would be the same as your findObjs call. The rest is the content of your iteration over the findObjs result set, except for the bit at the end which added the iteration item to the result array. This version, instead, returns true if the token should be in the result set, or false if it should not. Your functions would then be in the format: function example(...) { // initialize additional variables var result = filterObjs(function(obj) {...}); return result; }
1403476080
The Aaron
Roll20 Production Team
API Scripter
Very cool. See if this works for you:
1403530369

Edited 1403530383
Rather than Array.prototype.forEach, you have access to _.each. While _.each delegates the processing to Array.prototype.forEach if possible, not every browser has it (such as IE8). Oh! OK I am looking at the Underscore documentation now... never saw that before, and there certainly is a lot there. I will have to wrap my mind around that after I get home this evening. Thank you! You should include a means to hit tokens which don't represent a character. Many GMs have NPCs which aren't represented by characters, and your targeting would ignore them. How, then, do we differentiate between a Token with no Character that still represents a monster, and some other kind of Token, such as a spell effect? If you don't utilize the second point above, you can used Array.prototype.push to append things to an array, rather than i=arr.length;arr[i]=foo; (You can also initialize arrays with simply "[]" instead of "new Array()") Oh, all right, cool! I tried using the .push method, but I didn't realize I had to include the .prototype call. Thanks for elaborating! (Also - since you brought up prototype - it is allowed to use .prototype to add properties and methods DIRECTLY to the Roll20 character and token objects, rather than using createObj to create an Attribute or Ability? It would sure save a lot of time, but I'm scared to try it lest it blow everything up...) Thanks so much for helping me optimize my code, Brian! You're a Gem!!!
1403536746
The Aaron
Roll20 Production Team
API Scripter
Ideally, spell effects would be {isdrawing: true} but you can't really count on that. Personally, all my monster tokens represent a monster character on which I have all the abilities. I can tell the difference between my tokens and my players by who can control the attached character. That said, you couldn't use that to distinguish between friend and foe. Probably the best option is to abstract the selection means, like filterObjs() does. Take a callback function that will answer the question "should this token be included?". Then provide your functions which use the attribute system you've designed. That way you can use your system, and others are free to do the same, or define their own functions that provide the right answer for their situation. I don't think you need the .prototype in that call to .push. I think Brian is just being specific about which push he's talking about. var foo=[]; foo.push(2); foo.push(10); log(foo); // should be [2,10] As for using .prototype to add functions to Roll20 objects: nope, you can't do that as far as I can tell. You can mess around with it, it shouldn't break anything for you, and if it does, you just need to restart your sandbox.
1403577326
Lithl
Pro
Sheet Author
API Scripter
Sarjenka A said: Rather than Array.prototype.forEach, you have access to _.each. While _.each delegates the processing to Array.prototype.forEach if possible, not every browser has it (such as IE8). Oh! OK I am looking at the Underscore documentation now... never saw that before, and there certainly is a lot there. I will have to wrap my mind around that after I get home this evening. Thank you! You should include a means to hit tokens which don't represent a character. Many GMs have NPCs which aren't represented by characters, and your targeting would ignore them. How, then, do we differentiate between a Token with no Character that still represents a monster, and some other kind of Token, such as a spell effect? If you don't utilize the second point above, you can used Array.prototype.push to append things to an array, rather than i=arr.length;arr[i]=foo; (You can also initialize arrays with simply "[]" instead of "new Array()") Oh, all right, cool! I tried using the .push method, but I didn't realize I had to include the .prototype call. Thanks for elaborating! (Also - since you brought up prototype - it is allowed to use .prototype to add properties and methods DIRECTLY to the Roll20 character and token objects, rather than using createObj to create an Attribute or Ability? It would sure save a lot of time, but I'm scared to try it lest it blow everything up...) Thanks so much for helping me optimize my code, Brian! You're a Gem!!! You shouldn't need to use .prototype to use push(), that's simply how the method is defined. As for adding onto Roll20 objects with prototype... it would probably not cause errors, but the prototyped function also won't work once the sandbox resets. It also would't replace attributes and abilities.
ANOTHER question: if you look at the output for my spell_Bless function, you'll notice that "Sarjenka" (the caster) is being Blessed TWICE. I've been over and over my code and as far as I can tell, the output is CORRECT - meaning, there really do exist two tokens named Sarjenka, with different IDs and (occasionally) different coordinates... but I can't find the other one. Has anyone else experienced this?
1403660909
Lithl
Pro
Sheet Author
API Scripter
Can you duplicate the problem with a new campaign?
Um... I'm not sure because, for some reason, when I copy the campaign, and then Cut and Paste the API sourcecode, it doesn't work in the copy, gives "unexpected token }" even though it works in the original. Hmm, give me some time to get to the bottom of it...
1403722472
The Aaron
Roll20 Production Team
API Scripter
Since the API scripts are effectively concatenated together, an error in one file can show up in another. I've seen "unexpected token }" as a result of this several times. You can use an external javascript editor with color highlighting (gist actually works pretty well for this) to spot some problems. Also, try double clicking the { or } around things and make sure it selects what you expect.
I got the API scripts in the copied campaign to work by giving the Scripts unique names that did not match the same script in the original campaign. Now I am seeing that the problem duplicates in the same way in each campaign. There are definitely multiple objects, of the same token, representing the same characters but with different IDs. There are also "extra" tokens that my functions are finding which have no names and represent no characters. I don't get it.
1403742298
The Aaron
Roll20 Production Team
API Scripter
If you have it in a test campaign, you can PM me the campaign link and GM me and I'll be happy to help you debug it. =D
WAIT! I think I know the answer already. The tokens for Sarjenka and Raven were ones in which I first began with one graphic image, and then decided needed to redo them and make a better graphic. When I do so, I deleted the old graphic file from my library. BUT I didn't delete the Tokens first! So now there are tokens hanging out which really are there, but their imgsrc links are invalid. However, the hex token for Jewel Twilight (the drow), I have not changed, and have been using the entire time. That must be why Jewel Twilight isn't getting duplicated! So I'll just write a loop that goes through and makes sure the image file links are correct, and see if they "pop" back onto the screen.
1403797221
The Aaron
Roll20 Production Team
API Scripter
You can probably still select them by dragging across the area with the mouse in a rectangle select box. I wonder if the dev team is aware of this particular issue. It would probably make sense to have the imgsrc change on tokens whose image src is no longer available.
YAY! I fixed it! I wasn't able to select them at all by dragging a selection pointer, I had to write a little utility for it, but now the "extra" tokens are visible. Cool.