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

findObjs() and Bar Values

Noticed something and not sure if it's a bug, or just a quirk i need to capture. When entering a value for a token bar value (such as bar1_value) through the UI, it gets saved as a string. However, when set via code, it can be saved as a numeric value. This is causing problems with findObjs(). If the value is entered in the UI form, findObjs() needs to be set to search as a string ie. bar1_value: "0" . If I set the value in code to the number 0, I have to use findObjs with bar1_value: 0 . This means two different search techniques depending if the value was entered in code or through the UI. Now that I know this is occurring, I know I need to go in and do some cleanup with a on("graphic:change") event to convert any entry in these boxes to their numeric equivalent (at least the ones I plan to do findObjs() against), or I need to save my values in code as strings, which in turn means potentially having to convert back to numeric equivalents if I need to do any calculations. Guess in my thought process, a "bar1_value" field gives the impression that it stores a numeric value (as does "bar1_max" or "aura1_radius"). Seems that the conversion to numeric should occur at the UI form entry if the main purpose of this field is to be numeric. Would save further conversions and/or event captures. Or am I way off base? Is there a reason of which I'm not aware that the values for bars and auras are saved as strings when entered in the UI forms? Verification I had set up this bit of code to search for a specific token. I enter a value of 0 into the UI for bar1_value, then run my command !Test. It searches for that token by ID and by bar1_value where bar1_value is numeric. Then it changes the value of bar1_value in code to 0, and searches again using the same criteria. The first search returns no matches, and the second return the expected count of 1. Here's also the log output... i excluded irrelevant fields to save space. {"_id":"-JFT0t6PRjz2fFn14Sq7", ... ,"bar1_value":0, ... } {"_id":"-JFT0t6PRjz2fFn14Sq7", ... ,"bar1_value":"1", ... } {"_id":"-JFT0t6PRjz2fFn14Sq7", ... ,"bar1_value":"0", ... } "- found 0" // searched against a numeric 0 "- found 1" // searched against a numeric 0 after .set statement {"_id":"-JFT0t6PRjz2fFn14Sq7", ... ,"bar1_value":0, ... } And the test code... The the results are the same if I start with a numeric value of 0. The first findObjs() returns no objects, then .set to "0", and the second findObjs() returns the expected obj. on("chat:message", function (msg) { if (msg.type != "api" || msg.content != "!Test") return; //set variables var thisID = "-JFT0t6PRjz2fFn14Sq7"; var thisObj = getObj("graphic", thisID); var found = []; //search as is found = findObjs({ _type: "graphic", _id: thisID, bar1_value: 0, }); log("- found " + found.length); //set test values to numeric thisObj.set({ bar1_value: 0, }); //search after numeric set found = findObjs({ _type: "graphic", _id: thisID, bar1_value: 0, }); log("- found " + found.length); });
Making a mountain out of a digital molehill, I'm sure. In order to avoid the conflict with findObjs() against a bar value entered numeric in code vs in the UI form, I'm having to force those entries as numeric on the on("change:graphic") event. Tryin' to do this in a single set statement, i use the following... I have to check for non-numeric values or script will crash. obj.set({ bar1_value: (isNaN(obj.get("bar1_value"))) ? obj.get("bar1_value") : parseInt(obj.get("bar1_value")), bar1_max: (isNaN(obj.get("bar1_max"))) ? obj.get("bar1_max") : parseInt(obj.get("bar1_max")), bar2_value: (isNaN(obj.get("bar2_value"))) ? obj.get("bar2_value") : parseInt(obj.get("bar2_value")), bar2_max: (isNaN(obj.get("bar2_max"))) ? obj.get("bar2_max") : parseInt(obj.get("bar2_max")), bar3_value: (isNaN(obj.get("bar3_value"))) ? obj.get("bar3_value") : parseInt(obj.get("bar3_value")), bar3_max: (isNaN(obj.get("bar3_max"))) ? obj.get("bar3_max") : parseInt(obj.get("bar3_max")), aura1_radius: (isNaN(obj.get("aura1_radius"))) ? obj.get("aura1_radius") : parseInt(obj.get("aura1_radius")), aura2_radius: (isNaN(obj.get("aura2_radius"))) ? obj.get("aura2_radius") : parseInt(obj.get("aura2_radius")), });
1392462987
Alex L.
Pro
Sheet Author
Ok first off can I just check that you are never running: found = findObjs({ _type: "graphic", _id: thisID, bar1_value: 0, }); In a real script (ie your not searching for a value that you could just get). If you are then the correct way to do it would be: var thisObj = getObj("graphic", thisID); var value = thisObj.get("bar1_value"); if(value == 0) { ....... } ....... And here is the function I use to ensure that any input I get that could have been entered by a player is an int: fastInt = function(i) { if(!isNaN(i)) { i = Math.round(i); } else { i = 0; } return i; } I use Math.round rather than parseInt out of habbit as it is faster in most browser based JavaScript interpretors and i always return a int.
1392472871

Edited 1392473236
Matt
Pro
I appreciate the reply. My understanding is that while getObj() would be better in my example, it only returns a single item. My example, I forced it to search for a single item to show that the same item is returned when searching as a integer, but not as a string. But in an actual script, if I'm looking for all graphics on a certain page where bar1_value = #, then getObj() would not provide the results I need. An object where a value of 0 is entered for bar1_value from the UI form is not returned in the search results from findObjs({bar1_value: 0}) because 0 is being treated as the string "0". To resolve the issue, I either have to convert the value to an integer as it's entered, or I need to use filterObjs() to convert in the callback function. This is the part that causes me confusion. A field that generally used for numbers isn't being treated as numeric. In the end, I'm need to convert at the time of entry, or at the time of searching to return a number. Wasn't aware that Math.round() was faster than parseInt(), so that's good info to know. However, if I assume that any NaN is equal to 0, that too would throw off my results when I search for 0. I'm not wanting to change the value of bar1_value in the event it should be used for a non-numeric entry, but I do want to make sure that numeric entries are treated as numbers.
1392499531
Lithl
Pro
Sheet Author
API Scripter
JavaScript is a weak-typed language. Strings, numbers, booleans, and Woobergs all change into each other whenever the engine decides it's appropriate. Normally, it's not much of an issue. if (myVar == 0) will coerce myVar to a number and check for equality. If they type maters, there's a solution for that, too: if (myVar === 0) will only evaluate to true if myVar is a number and it's 0. The real problem you've got is the interaction with findObjs , which I suspect uses the latter equality operator when performing its search. Instead of supplying an object with values to match (exactly), however, you can supply a function to findObjs and be a little heuristic in your hunt. var found = findObjs(function(obj) { if (obj.get('type') == 'graphic' && obj.id = thisID && obj.get('bar1_value') == 0) { return true; } else { return false; } }); Of course, you can get more complicated than that; I'm simply using your example.
This has been more of a point to ponder than a search for a solution, though I do appreciate the feedback. The searching makes more sense now if using === instead of == . (still relatively new to the language) I'll settle for the reason why a field primarily used for numeric values is saved as a string is, well, that's what it does. Knowing this is the behavior, I can adjust with what I know and the info posted here. Thanks all.
1392539566
Lithl
Pro
Sheet Author
API Scripter
Matt said: I'll settle for the reason why a field primarily used for numeric values is saved as a string is, well, that's what it does. Knowing this is the behavior, I can adjust with what I know and the info posted here. Thanks all. FWIW, when the user actually enters a value into the box in the UI, JavaScript will by default treat it as a string (you'd have to get coercion going on to turn it into a number, or parse it explicitly with parseInt ), so it'll save as a string without any other effort applied. And, I've seen people talking about storing non-numeric data in their bar values, so "fixing" that would be a breaking change for some. (Also, above post was made & deleted by me because Drew didn't log out when he got off my computer and I wasn't paying attention.)
Thanks for the info. Defaults to string by design. And I wasn't looking to change the field to a numeric, although i wouldn't complain if the value were converted at the point of saving from the UI. I do understand that this field can be used for non-numeric values. Between the == vs === and the design of Javascript itself defaulting to strings, I'm content. :) Thanks to all for tolerating such inquiries.
1392594868
Lithl
Pro
Sheet Author
API Scripter
It's not that "JavaScript defaults to string," but rather "<input type='text' .../>" is read as a string.
1392618302
Alex L.
Pro
Sheet Author
Brian said: It's not that "JavaScript defaults to string," but rather "<input type='text' .../>" is read as a string. Yes all user input will always be a string no matter what you do, the reason my code sets NaN to 0 is because in my code having a NaN would break everything and there is no reason to have a NaN. Can I ask, just out of curiosity but why do you need to search for values of bars?
Sure. I've written a script that manages doors for the players' use. <a href="https://app.roll20.net/forum/post/636123/#post-636" rel="nofollow">https://app.roll20.net/forum/post/636123/#post-636</a>... In the script, I use the _value, _max and _radius fields to hold status information about the door. Bar1 identifies if the door is locked, for example. I've not had any issues with this approach because everything was known when the script ran, meaning I knew what door was being opened/unlocked and didn't have to search. The problem arose when I added in secret doors, which uses aura1_radius. When a player moves a token, I check to see if they moved within range of a secret door. If they do, checks are made, and the door is revealed on success. Because of this, I need to be able to identify doors that have a specific value in aura1_radius, therefore I use the following search: //find all secret doors on the same page as the token var secretSwitches = findObjs({ _type: "graphic", _pageid: pc.OnPage(), aura1_radius: statusDoors.doorTypeHidden, }); It was this search that made me realize the value going into the UI was the string "1", and my search value was the numeric 1. From that, I began to ponder why a field labeled 'radius', which gives the impression it's primary purpose would be to store a numeric value, would not attempt to save the value as a numeric if possible instead of defaulting to a string. Even if "&lt;input type='text' .../&gt;" is read as a string, was curious why any conversion wasn't done before saving the value. Not saying to only save numeric values, as I understand some use these fields to save NaN values, but for those values than can be converted - why aren't they? Maybe I'm wrong (very possible), but seems if it were converted at the UI, other conversions, explicit or implied, would be minimized. May be my lack of knowledge on how much control the UI has between the entry of the field, and the save method. And at this point, it's more of a general inquiry than looking for a solution. My code functions, and I now know to be aware of === situation when searching. All is well with the world. :)
1392671421
Alex L.
Pro
Sheet Author
Matt said: Sure. I've written a script that manages doors for the players' use. <a href="https://app.roll20.net/forum/post/636123/#post-636" rel="nofollow">https://app.roll20.net/forum/post/636123/#post-636</a>... ..... Ok in that case use the custum search Brian posted. it should fix all your problems.
1392703796
Lithl
Pro
Sheet Author
API Scripter
Matt said: I began to ponder why a field labeled 'radius', which gives the impression it's primary purpose would be to store a numeric value, would not attempt to save the value as a numeric if possible instead of defaulting to a string. Even if "&lt;input type='text' .../&gt;" is read as a string, was curious why any conversion wasn't done before saving the value. Not saying to only save numeric values, as I understand some use these fields to save NaN values, but for those values than can be converted - why aren't they? Maybe I'm wrong (very possible), but seems if it were converted at the UI, other conversions, explicit or implied, would be minimized. May be my lack of knowledge on how much control the UI has between the entry of the field, and the save method. And at this point, it's more of a general inquiry than looking for a solution. My code functions, and I now know to be aware of === situation when searching. All is well with the world. :) That's a question only Riley can answer. But I suspect it's because he didn't bother to do it, and there was no pressing need for it.