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

Why can't I get obj.get to work anymore?

1384678800

Edited 1384678929
I'm still relatively new to this API and JavaScript. But I had this functionality working up until earlier today. Now, I can't get properties from objects. I removed all my scripts, and started fresh. on("chat:message", function(msg) { if (msg.type == "api" && msg.content.indexOf("!InfoDump") !== -1 && msg.who.indexOf("(GM)") !== -1) { log("InfoDump: " + msg); _.each(msg.selected, function(obj) { log(obj); }); } }); Selecting some objects in the UI and running the script, I get these expected results. Restarting sandbox due to script changes... Spinning up new sandbox... "InfoDump: [object Object]" {"_id":"-J8_2kYzjoXSHg6cp_ja","_type":"graphic"} {"_id":"-J8_2nj1UGOVgqANMPDz","_type":"graphic"} {"_id":"-J8_2o2kIcsMMJpP_X5k","_type":"graphic"} {"_id":"-J8_2ojNFSM0aipVy0jB","_type":"graphic"} {"_id":"-J8_2pDo0i3oZoE3IZs4","_type":"graphic"} However, if I modify the script by adding a single line. on("chat:message", function(msg) { if (msg.type == "api" && msg.content.indexOf("!InfoDump") !== -1 && msg.who.indexOf("(GM)") !== -1) { log("InfoDump: " + msg); _.each(msg.selected, function(obj) { log(obj); var selObjType = obj.get("_type"); }); } }); I get the following. I did not change which objects were selected. Restarting sandbox due to script changes... Spinning up new sandbox... TypeError: Object [object Object] has no method 'get' at evalmachine.<anonymous>:137:34 at Array.forEach (native) at Function._.each._.forEach (/home/symbly/www/d20-api-server/sandcastle/node_modules/underscore/underscore.js:78:11) at Sandbox.<anonymous> (evalmachine.<anonymous>:134:11) at eval (
1384682769
Lithl
Pro
Sheet Author
API Scripter
msg.selected is not an array of Roll20 objects, but rather an array of objects which each contain only the properties _id and _type. You need to call getObj(obj._type, obj._id) in order to get a reference to the Roll20 object that's selected.
1384725876

Edited 1384726701
Ok, great! Thanks for the information! I got it to work now. But for some reason, even though it seems like I was only selecting tokens, some of them were text objects, so even with getObj, I was still getting errors when I used obj.get because the property I was looking for doesn't exist on non-graphic objects, and I don't care about non-tokens for this purpose. So, now I know I need to filter the list coming in from msg.selected - only processing the token subtype of graphic objects. Here's my resultant code. It seems quite weighty and cumbersome, but it works. Is there a shorter way? on("chat:message", function(msg) { log("InfoDump-Pre:"); if (msg.type == "api" && msg.content.indexOf("!InfoDump") !== -1 && msg.who.indexOf("(GM)") !== -1) { log("InfoDump: " + msg); _.each(msg.selected, function(objInfo) { var obj = getObj(objInfo._type, objInfo._id); if( obj.get("_type") == "graphic" ){ if( obj.get("_subtype") == "token" ){ // Do stuff here } } }); } });
1384728570
Lithl
Pro
Sheet Author
API Scripter
A graphic is only going to be a token or a drawing (drawings may be tokens which you have set as a drawing through the context menu or API, cards on the table, or dice pulled from the chat to the table -- Path objects are not drawings, even though they're made with the drawing tools in the UI), so if there aren't ever any drawings you could omit the check on subtype and only check type. You could omit the cal to get("type") if you use getObj("graphic", objInfo._id) , although you'd still have to check whether obj was undefined. Finally, you could use var obj = findObjs({ _id: objInfo._id, _type: "graphic", _subtype: "token" })[0]; if obj is not undefined, then you're guaranteed that it's a token. Example: _.each(msg.selected, function(objInfo) { var obj = findObjs({ _id: objInfo._id, _type: 'graphic', _subtype: 'token' })[0]; if(obj) { // Do stuff here } });
findObjs loops through every object in the campaign. That is a really heavy thing to nest just to validate instead of using an if when you already have the object.
Brian said: A graphic is only going to be a token or a drawing... For now... I prefer to future-proof whenever possible. It's only one extra line in this case, so I'm ok with it. John M. said: findObjs loops through every object in the campaign. Hmmm, I just had an idea to use this in a "find tokens within X units" script. Pass in a token object and an integer, get back an array of token objects that are within the distance provided. Even better, an array of 2-tuples containing token/distance pairs. Thank you both for your input!
filterObjs may be the better choice for your area search. You're able to pass a function to it that gets applied to every object, so you can do your distance check and return true for objects within range. You'd probably need a second loop over your returned collection to give the distances since, iirc, filterObj will only return the object array based on the truthiness of the passed function's retun value.
1384781485

Edited 1384781535
Next I need to figure out how to properly create and return a two-dimensional array containing a Roll20 object and number (likely an int) pairs.
This is the code I am using that finds tokens, just tokens, and nothing but tokens. It ignores drawings, cards, and things on the map, dynamic lighting, & map layers. var currentPageTokens = findObjs({ _pageid: Campaign().get("playerpageid"), _type: "graphic", _subtype: "token", isdrawing: false, layer: "objects" });