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

New API Update (5/2) & (5/3): Create Objects!

There are some new changes to the API out today, the most important of which is the way that linked token bars + attributes are handled. From now on, if a token's bar is modified that is linked to an attribute, you will first hear the attribute events, then the graphic/token events in the API. In addition, if you set the value of attribute or the value of a bar in the API, and the bar/attribute is linked to anything, the API will automatically handle updating the linked property for you. Both of the values will be the same, but that way you can just work with the bar1_value property for a token without needing to worry about whether or not that bar is linked to an underlying attribute.  So, to recap: Attribute events will always precede bar events for linked bars. But if you are working with the bar values on a token, you can now safely listen to and modify only the bar values and the API will handle updating the linked attributes for you. Hopefully that makes things a little easier to deal with. In addition, there have been several new properties added to objects, including:   represents for the Graphic object, which is the ID of any linked Character (read-only) controlledby for the Graphic, Text, Path, Handout, and Character objects, which is now read+write, and is a comma-delimited list of the Player IDs which can control those objects inplayerjournals for the Character and Handouts objects, which controls which Players can see those objects in their Journal tab, also now read+write The documentation on the Wiki has been updated to reflect these changes. Please note that you need to reload your game if you're already in-game as well to make sure that you're using a game state compatible with the new changes. Also note that if you were reading any read-only attributes before that are now read+write (e.g. "_inplayerjournals" is now "inplayerjournals"), you need to update any event listeners and get calls to remove the underscore. (Note: As a rule of thumb, you can actually leave out the underscore even for read-only objects in get() calls and it will correctly return the value, so that might be best to do going forward as I change things up.)  A Quick Note on Read-Only Properties   Right now I'm basically taking a very conservative approach to opening up the ability to write to properties which require special handling. The eventual goal is that nearly every property will be read+write (except for ones like the "_id" and the "_characterid"), but I want to make sure that a) they are clearly documented and b) there are at least some safeguards in place to make sure that if you put an invalid value in there you don't ruin your whole Campaign. So bear with us while we get those rolled out.
1367512391
Alex L.
Pro
Sheet Author
I have noticed a big problem when you work with multiple interacting scripts, you have no way to force the load order it just loads from left to right but I cant work out how it picks the order so I can't trick it. If you could let me know how it works out the order for now then maybe add a button to change it in a latter version that would be a great help. Also as always thanks for all the hard work some great fixes/additions.
It should just be, as you've said, loading the scripts from the left-most to the right-most as they are shown in your Script Editor window. They are literally just concactenated together in-order; the different tabs are purely for ease of organization. At some point I'll probably add the ability to drag-and-drop the tabs so you can re-order them.
1367513176
Alex L.
Pro
Sheet Author
Yea I was having difficulty when adding new tab I couldn't guarantee were the new tab would end up in the order it wasn't always going to the end or the start, it did both. In the end I gave up and made a bunch 1-10 then put the code in and renamed them "#. classname" and that has fixed it for now.
1367513643
Alex L.
Pro
Sheet Author
On another note did you remove get and set as findObjs is returning objects that don't contain them.
My head is spinning with how fast you put in a fix on that bar/attribute thing, Riley.  That or because I stayed up all night playing Black Ops II. I'm not sure.  Off I go to modify my script! Thanks!
When you say "don't contain them", can you be more specific? Are you getting an error when you try to call those functions? Can you post a sample?
1367520424
Alex L.
Pro
Sheet Author
ok my test is i have a character called "player". code: on("ready", function() {   var obj = findObjs({ _type:"character", name:"player" });   log(obj);    log(("get" in obj)); }); log: Spinning up new sandbox... [{"name":"player","bio":"","gmnotes":"","archived":false,"inplayerjournals":"","controlledby":"","_id":"-ItaA5AgP7NWq3jjMjpy","_type":"character"}] false code: on("ready", function() {   var obj = findObjs({ _type:"character", name:"player" });   log(obj);    log(("get" in obj));   log(obj.get("name")); }); log: Spinning up new sandbox... TypeError: Object [object Object] has no method 'get'   at Sandbox.<anonymous> (evalmachine.<anonymous>:162:13)   at eval ( Made new campain just to check called "Get not in obj Test", put just a character called player in it then made a single script with the code above and I still get the same result, FYI this would have worked up until the last patch.
Yeah I must have broken something, sorry. I'll fix it next time I'm at a computer and able.
1367523027
Alex L.
Pro
Sheet Author
Ok no probs.
Oh I see what's going on. findObjs returns an array. So you should be doing something like: _.each(findObjs({ _type:"character", name:"player" }), function(obj) { log(obj.get("name")); }); If you know for a fact that it should only return one object, you can do: var obj = findObjs({ _type:"character", name:"player" })[0]; Although be sure to check then and make sure obj isn't undefined.
1367528052
Alex L.
Pro
Sheet Author
Yes your quite right I'm an idiot.
Alex L. said: Yes your quite right I'm an idiot. Haha, no you're not, we're just all still figuring this out, including me :P
on("change:token", function(obj) {     var target = findObjs({_type: "character", id: obj.get(represents)})[0]; }); Is this what I would use to get the character sheet (journal page) the graphic (token) is linked to?
1367587301
Alex L.
Pro
Sheet Author
You will want to do some checks before doing that to make sure obj.get(represents) gives you an id I also like to check that findObjs only returned one object in its array, although with id's this is almost impossible but its nice to check.
Actually there is the getObj() function which is what you should use if you know the ID of the object -- all IDs are primary/unique keys. So you would do: on("change:token", function(obj) {     var target = getObj("character", obj.get("represents")); }); The getObj() function is an order of magnitude faster than using findObjs (since it doesn't have to iterate through every object), so it's definitely best to use it whenever possible.
...And I just noticed apparently getObj() is not documented. Fixing that now. Sorry.
1367589063
Alex L.
Pro
Sheet Author
That would help Riley :P Can't really complain though API roll-out has been going smoothly so far.
It's documented on the wiki now :-)
1367589681
Alex L.
Pro
Sheet Author
I was wondering why there wasn't a get command for objects. But my point still stands never trust a find function return a single (or only) value, just as a matter of good practice if nothing else, its always when something that should be "impossible" happens that you have the hardest time debugging it.
Even when I try to find only the id on the player page, it's finding every token with that name. Hrm.
I just pushed out another new update to the API system. The main purpose of this update is to enable the creation of totally new objects via the API. To create an object, just use the createObj() function, which is documented here:&nbsp; <a href="https://wiki.roll20.net/API:Objects#Creating_Objects" rel="nofollow">https://wiki.roll20.net/API:Objects#Creating_Objects</a> Note that currently you can only create attributes, abilities, characters, and handouts. In addition, I've modified the findObjs() function to have some additional options, including case insensitivity in searching. More here:&nbsp; <a href="https://wiki.roll20.net/API:Objects#Finding.2FFiltering_Objects" rel="nofollow">https://wiki.roll20.net/API:Objects#Finding.2FFiltering_Objects</a> Let me know if you run into any trouble with those two new things or anything else. Thanks!
Riley, I may have found a small unintended side-effect of the Bar/Attribute change. &nbsp;If I create a monster with Hit Points, then give it a token that has a Hit Points bar linked to the attribute, then CLONE that monster 3 or 4 times, I wind up with 3 or 4 monsters that all share a single Hit Point pool. &nbsp;So if I damage Clone 1, Clone 2 also shows that damage. And this is occurring in production, not in dev. :/
Oh, interesting. So you're saying that it's duplicating the Character, but sharing the same Attribute, essentially? Like leaving the bar out of it, if you just clone a character and then change the Hit Points in Char 1, Char 2's Hit Points change as well? If so, yeah, then it's just a bug where the duplicating character thing isn't working correctly.
I heart this update.
Riley D. said: Oh, interesting. So you're saying that it's duplicating the Character, but sharing the same Attribute, essentially? Like leaving the bar out of it, if you just clone a character and then change the Hit Points in Char 1, Char 2's Hit Points change as well? If so, yeah, then it's just a bug where the duplicating character thing isn't working correctly. Ummmm. &nbsp;I haven't actually tested it, at that level. &nbsp;What I tested was the following: Create a new character. Assign a "Hit Points" attribute to that character, with a Current and Max of 30. Drag the character to the play area to create a token. Associate Bar 1 with the Hit Points attribute. Set this token as the "Default Token" for this character. Rename the token "Monster 1" Drag the character to the play area to create a second token; this one will already have Bar 1 set up for you. Rename the new token "Monster 2" Adjust the hit point bar for "Monster 1". Notice that "Monster 2" has had its hit point bar adjusted. Perhaps the solution is to not use attributes for monsters, at all. They really aren't necessary, I suppose. &nbsp;But it is a noteworthy issue which did not appear until the 5/2 update.
Okay, this is actually working as intended. When you link bars to attributes, you're linking them to the attributes for that Character. The whole idea is so that for example a player's token on Page B and a player's token on Page A will always have the same value for Bar 1 if it's linked to the Character's HP attribute. Make sense? If there are two different Characters they should have different values for HP, though, even if they were the same name. If you aren't wanting to share the HP value between multiple tokens, then you would not want to link it to a Character attribute. I think it already worked this way pre the 5/2 update...if it didn't, then that was a bug :-).
1367618781
Gauss
Forum Champion
It did work that way. What I have usually suggested is the following:&nbsp; 1) Link token to character sheet.&nbsp; 2) Link Bar to character sheet attribute (the stats are now set to the attribute) 3) Unlink bar from character sheet attribute (the stats remain, but they are not linked to the attribute) 4) Copy token as desired. With the new character sheet token slot you start off with the token linked, just unlink the hp bar when you create the token (step 3) I don't understand the API (at all) but perhaps there is a way for you to automate some of that via the API.&nbsp; - Gauss
Just set up the default token without linking the HP bar. The token will have the correct HP and it won't adjust all tokens of the same name as they would if the bar was linked.
Riley D. said: Actually there is the getObj() function which is what you should use if you know the ID of the object -- all IDs are primary/unique keys. So you would do: on("change:token", function(obj) { &nbsp; &nbsp; var target = getObj("character", obj.get("represents")); }); The getObj() function is an order of magnitude faster than using findObjs (since it doesn't have to iterate through every object), so it's definitely best to use it whenever possible. If I use this and then change a token not linked to a character in the journal... it causes a firebase.set error that error locks the api. Basically, all I want to do is check to see if a token is representing a page or not, so I can determine whether I'm going to alter bars or attributes.
1367683861
Alex L.
Pro
Sheet Author
its _represents
Actually, Riley said you can use a read only attribute without the underscore and it would still work. However, if he changes something from read only to read/write, than the underscore version of a script wouldn't work.
You can indeed use "represents" or "_represents", either will work for get() calls. In events you have to use "_represents". What's happening is findObj() is returned an undefined object, which you are then presumably trying to use in a set() call. You should be checking to make sure that represents isn't blank before you're looking for the Character object. E.g. if(obj.get("represents") == "") { //Doesn't represent anything, use bar values } else { //Is linked to a Character, you can use getObj("character"... } //OR Another way to do it is var target = getObj("character", obj.get("represents")); if(target == undefined) { //No linked character } else { //Linked character, use target here.... } However, as I've said in the update notes, if you are specifically just concerned with the bar values, you can just work with the bar values and the API will handle all the behind-the-scenes attribute-linking for you now. So if you get bar1_value and that's really "Strength" for the linked Character, it will be the correct value, and you can just set bar1_value and it will also set the attribute for you. So for that specific case, you don't really have to worry about whether the token is linked to anything or not. If you have any further info on what specifically you're wanting to do I can give you some further guidance on the best way to do it...
Riley D. said: You can indeed use "represents" or "_represents", either will work for get() calls. In events you have to use "_represents". What's happening is findObj() is returned an undefined object, which you are then presumably trying to use in a set() call. You should be checking to make sure that represents isn't blank before you're looking for the Character object. E.g. &lt;snip&gt; However, as I've said in the update notes, if you are specifically just concerned with the bar values, you can just work with the bar values and the API will handle all the behind-the-scenes attribute-linking for you now. So if you get bar1_value and that's really "Strength" for the linked Character, it will be the correct value, and you can just set bar1_value and it will also set the attribute for you. So for that specific case, you don't really have to worry about whether the token is linked to anything or not. If you have any further info on what specifically you're wanting to do I can give you some further guidance on the best way to do it... My ultimate goal is to give players the ability to adjust temp hp with api chat commands like !addthp target amount &nbsp;and it would adjust the attributes on characters and monsters, rather than a bar. That way we can use the bars for hit points, surges, and action points instead. It would also mean players wouldn't have to adjust two values either on the token or the character attributes or bars. They'd just enter the damage taken as usual and the script would adjust hit points and temp hp as needed automatically. So for example... Anna uses Sacred Flame and grants Bob five temporary hit points.&nbsp; She then enters !addthp Bob 5 and the script will check to see if Bob already has 5 or more temp hp. If he does, nothing happens. Otherwise it will replace whatever value it finds with 5. Bob then takes 14 damage. Instead of having to adjust his temp hp attribute or bubble and &nbsp;his regular hit point bubble, he just does -14 in his hit point bubble. The script then checks for a hit point change. If it find a negative hit point change, it removes an appropriate amount of temp hp and adds the difference to the hit point attribute. So in this example, Bob takes 14 damage. His hit points drop from 30 to 16 and then the script sees he has 5 temp hp and adjust his hit points up to 21 and then removes the 5 temp hp. If he had taken 4 or less damage, it would have added that back to his hit points and then subtracted the damage from his temp hp instead. I can do all the nitty gritty stuff. My stumbling block with javascript is the get/set commands and how they interact with tokens and characters. It's a different method than I'm used to, so it's taking time to learn. -edit- The reason I use a separate temp hp attribute instead of just adding it on top of existing hit points, is because it can be easy to forget that you had five temp hp at the end of an encounter and forget to remove them. If we get an event that fires when a token is removed from initiative, I could automatically script it to remove any temp hp at that point pretty easily.
Oh I see. Okay. Yeah in that case you'll want to use the target = getObj("character", obj.get("represents")); stuff then to get the chracter, then use something like: findObjs({_type: "attribute", name: "TempHP", _characterid: target.id})[0]; Which would get you the attribute named "TempHP" for the character linked to the token. You could then, if you wanted, just modify the "HP" attribute for the Character as well, isntead of messing with token bars -- if you modify the HP attribute and there are tokens that have that set to "Bar 1", their bars will get automatically adjusted. So that way you can just deal totally with Attributes and not worry about bars at all. Or you can work with bars too, your choice :-)
Ha! I see where I went wrong. I used id instead of target.id. The dot thing is throwing me for a loop, but I'm getting there. Thanks for pointing me in the right direction.