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

[5e Shaped Sheet] Adding equipment through the API

I have a script I'm working on where I would like to insert an equipment item into my player's 5e Shaped character sheets. I can't seem to find any reference for doing this, but I know it's there somewhere. Can anyone point me in the right direction? Thanks!
1538357412
The Aaron
Pro
API Scripter
The equipment is stored in a repeating group.  To programmatically add to a repeating group you must create each of the attributes that are in the row.  For the shaped sheet, there are quite a few attributes to deal with.  You'll have to dig into the sheet to find which ones to create. The basic idea given a repeating group called "things" which has attributes in each row named "name", "description", "weight", and "quantity" is that you would: Generate a unique row id (see below) Create new attributes for "repeating_things_ROWID_name", "repeating_things_ROWID_description", etc To create a row id, you can use these functions: var generateUUID = (function() { "use strict"; var a = 0, b = []; return function() { var c = (new Date()).getTime() + 0, d = c === a; a = c; for (var e = new Array(8), f = 7; 0 <= f; f--) { e[f] = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz".charAt(c % 64); c = Math.floor(c / 64); } c = e.join(""); if (d) { for (f = 11; 0 <= f && 63 === b[f]; f--) { b[f] = 0; } b[f]++; } else { for (f = 0; 12 > f; f++) { b[f] = Math.floor(64 * Math.random()); } } for (f = 0; 12 > f; f++){ c += "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz".charAt(b[f]); } return c; }; }()), generateRowID = function () { "use strict"; return generateUUID().replace(/_/g, "Z"); }; Those are the actual functions Roll20 uses, just extracted for use on the API side of things. Good luck and come back with more questions if you have them!
Thanks, The Aaron! Somehow I knew I could count on you. :D I'll likely be back for more help.
1538374694

Edited 1538374721
Jakob
Sheet Author
API Scripter
Repeating rows aren't really intuitive to use from the API, all our knowledge is from some trial & error as to how they are handled internally. I could tell some stories about how I had to figure out everything by myself when writing ChatSetAttr . Luckily, creating a new row is rather straightforward, as opposed to retrieving existing rows in the correct order...
Take a look at Beyond Importer.  It seems to have this up and running.
Jakob said: Repeating rows aren't really intuitive to use from the API, all our knowledge is from some trial & error as to how they are handled internally. I could tell some stories about how I had to figure out everything by myself when writing ChatSetAttr . Luckily, creating a new row is rather straightforward, as opposed to retrieving existing rows in the correct order... Luckily, I'm just trying to add a single specialty item to the equipment list that will get its ID stored elsewhere for later updating. Nothing more dynamic than that.
Michael G. said: Take a look at Beyond Importer.  It seems to have this up and running. I will! Thanks for the suggestion.
1538857725

Edited 1538857764
Ok, so creating the row ID is obvious, as are are the fields needed for an equipment item. However, I cannot for the life of me figure out how to actually add the equipment item to the character sheet. I tried looking at ChatSetAttr, but it seems incapable of referencing those items. This screenshot is me trying to use "!modattr --sel --nocreate --repeating_equipment_-LElOoIwHv1gZYWxrK5L_name|Explorer Bag" to change the name of an equipment item. You can see the item name really exists, but ChatSetAttr doesn't recognize it. That left me looking at the 5e Shaped Sheet itself, in particular the addCommonEquipmentItems function. Here's the relevant section of code from that function:       commonEquipment.forEach(function (equipment) {         var repString = 'repeating_equipment_' + generateRowID();         Object.keys(equipment).forEach(function (field) {           data[repString + '_' + field] = equipment[field];         });       }); I know my scripting knowledge mostly predates a lot of the object oriented functionality in use today (damn, that makes me feel old), so bear with me. The 4th line of that section above is updating a "data" object but there seems to be nowhere in the code at all that defines the "data" variable. Almost every function in the Shaped Sheet's javascript references "data," either passing it to functions or modifying it but never defining it. How is this possible? And since I need to access it as well, how do I do so?
1538862285
The Aaron
Pro
API Scripter
if you look up in the code a bit, you can see that it is a variable passed into the function: const addCommonEquipmentItems = () => { getSetItems('addCommonEquipmentItems', { process: ( data ) => { const commonEquipment = [ { The shaped sheet is very complex, and gets constructed out of many libraries of operations.  getSetItems() in the above, calls getSetData(), passing it a collectionArray which is [] (empty array) as the object passed to getSetItems() doesn't define a property for collectionArrayRepeatingSections.  getSetData() passes that empty array to getAttrs(), and the result of that call is then passed to the process function defined above. The practical upshot of all this is that data is an empty object created in a higher scope and passed down to the process function for collection of changes.  Since objects are passed by reference in javascript, the changes made to data in the process() function are visible in the object in the calling scope, which is then passed to setData(), which does some optimizations around not setting anything that hasn't changed, eventually calling setAttrs() to push the changes up to Roll20.  After that completes, it calls the processCallback() defined in the above object as a sibling of the process() function, which just calls updateEquipment() to make sure all the weights and such are updated correctly. Hope that helps!
1538890109

Edited 1538901061
Jakob
Sheet Author
API Scripter
Just a note about the ChatSetAttr part: if you take a look at the error message, you can see that it complains about the attribute not being number-valued - this is because you're using !modattr instead of !setattr (this may be slightly confusingly named; modattr is not used to change the values of existing attributes in general, only to modify existing numerical  attributes by some other number). ChatSetAttr can create new rows if you use -create for the row ID instead of a proper ID.
The Aaron said: if you look up in the code a bit, you can see that it is a variable passed into the function: const addCommonEquipmentItems = () => { getSetItems('addCommonEquipmentItems', { process: ( data ) => { const commonEquipment = [ { The shaped sheet is very complex, and gets constructed out of many libraries of operations.  getSetItems() in the above, calls getSetData(), passing it a collectionArray which is [] (empty array) as the object passed to getSetItems() doesn't define a property for collectionArrayRepeatingSections.  getSetData() passes that empty array to getAttrs(), and the result of that call is then passed to the process function defined above. The practical upshot of all this is that data is an empty object created in a higher scope and passed down to the process function for collection of changes.  Since objects are passed by reference in javascript, the changes made to data in the process() function are visible in the object in the calling scope, which is then passed to setData(), which does some optimizations around not setting anything that hasn't changed, eventually calling setAttrs() to push the changes up to Roll20.  After that completes, it calls the processCallback() defined in the above object as a sibling of the process() function, which just calls updateEquipment() to make sure all the weights and such are updated correctly. Hope that helps! Yes, I followed it all up to where the data object is first passed in. I just don't understand enough to get that object myself in order to make the changes/updates I need to do. I know I can't use "data" without a reference, I just don't know how to make that reference. How do I do this pseudo code: var myDataObj = selectedCharacter.shapedCharacterSheetObj; myDataObj.repeating_equipment[new] = myNewEquipmentItem; As a side note (because that's where this script is going), is there a way to call that updateEquipment function from within my code without writing my own? I feel like I've hit the ceiling of what I'm capable of with this thing, but I really want this to work!
Jakob said: Just a note about the ChatSetAttr part: if you take a look at the error message, you can see that it complains about the attribute not being number-valued - this is because you're using !modattr instead of !setattr (this may be slightly confusingly named; modattr is not used to change the values of existing attributes in general, only to modify existing numerical  attributes by some other number). ChatSetAttr can create new rows if you use -create for the row ID instead of a proper ID. Ok, that makes sense. So I guess I should take a look at how you did it in ChatSetAttr. Is there anywhere I should look specifically? Something I should keep in mind? That's reference to the reply to The Aaron above.
1538937284

Edited 1538937371
Jakob
Sheet Author
API Scripter
Ben L. said: Jakob said: Just a note about the ChatSetAttr part: if you take a look at the error message, you can see that it complains about the attribute not being number-valued - this is because you're using !modattr instead of !setattr (this may be slightly confusingly named; modattr is not used to change the values of existing attributes in general, only to modify existing numerical  attributes by some other number). ChatSetAttr can create new rows if you use -create for the row ID instead of a proper ID. Ok, that makes sense. So I guess I should take a look at how you did it in ChatSetAttr. Is there anywhere I should look specifically? Something I should keep in mind? That's reference to the reply to The Aaron above. You could  look into the getCharRepeatingAttributes function, but it's easily the most complex function in the code because I was significantly worse at writing code back when I wrote it. It's also unnecessarily complicated for what you want to do, since it has to take care of a lot of different cases for repeating attributes. Your best bet would be to copy the model that the Shaped sheet uses (but no, you cannot call updateEquipment from your code): const addItemForCharacter = function (charid) { // define item data const myItem = { name: "foo", weight: "bar", // more stuff }; // make new row from item data const data = {}; const repString = `repeating_equipment_${generateRowID()})`; Object.keys(myItem).forEach(field => { data[`${repString}_${field}`] = myItem[field]; }); // set attributes setAttrs(charid, data); };
Jakob said: Ben L. said: Jakob said: Just a note about the ChatSetAttr part: if you take a look at the error message, you can see that it complains about the attribute not being number-valued - this is because you're using !modattr instead of !setattr (this may be slightly confusingly named; modattr is not used to change the values of existing attributes in general, only to modify existing numerical  attributes by some other number). ChatSetAttr can create new rows if you use -create for the row ID instead of a proper ID. Ok, that makes sense. So I guess I should take a look at how you did it in ChatSetAttr. Is there anywhere I should look specifically? Something I should keep in mind? That's reference to the reply to The Aaron above. ... Your best bet would be to copy the model that the Shaped sheet uses (but no, you cannot call updateEquipment from your code): const addItemForCharacter = function (charid) { // define item data const myItem = { name: "foo", weight: "bar", // more stuff }; // make new row from item data const data = {}; const repString = `repeating_equipment_${generateRowID()})`; Object.keys(myItem).forEach(field => { data[`${repString}_${field}`] = myItem[field]; }); // set attributes setAttrs(charid, data); }; I had all of that except for the setAttrs call and the empty object. I didn't think it was going to be that simple. So now my script seems to be creating everything I'm asking it to, but at the end it is giving me this error in the console: {"who":"error","type":"error","content":"Unable to find a player or character with name: "} I assume it's from the sheet itself but I could be wrong. It's not killing the sandbox, and all the other scripts (including my own) are working fine. Is it something I should be concerned about?
1539079778
The Aaron
Pro
API Scripter
Where does that error show up?
The Aaron said: Where does that error show up? That error is in the API Output Console right after the log output about creating the new repeating items.
One more hurdle and hopefully the last: The Shaped sheet has a "per use" weight that is calculated by the sheet, which is then used in calculating total weight of all items carried. I am updating the weight of my equipment item like I want, but until I can get that per use field recalculated it won't affect encumbrance the way it's intended. I obviously can't call the sheet's updateEquipment function, but is there another way to trigger it? 
1539221511
The Aaron
Pro
API Scripter
If you use .setWithWorker(), it should cause whatever sheetworker adjusts things to take action as if a human made your change.  Try using that for your final set (or all your sets if you're doing the whole row at a time).  Do you want to share your code so far, maybe we can make suggestions?
The Aaron said: If you use .setWithWorker(), it should cause whatever sheetworker adjusts things to take action as if a human made your change.  Try using that for your final set (or all your sets if you're doing the whole row at a time).  Do you want to share your code so far, maybe we can make suggestions? I'll try the .setWithWorker() instead of a straight .set() and see what happens. Thanks for the suggestion! I have the script in github right now, but have not committed any changes to the new branch. I'll definitely post when I get back to my real computer.
I tried .setWithWorker and it doesn't trigger the necessary calculation. Bummer :( Here is a link to the current repository:&nbsp; <a href="https://github.com/blawson69/PurseStrings" rel="nofollow">https://github.com/blawson69/PurseStrings</a> There are two issues since I've added the equipment item, including the "per use" calculation failure. And, of course, the "unable to find player or character" issue in the console which may or may not be related to the other two. If you'd like to take a look, I can send you a private message to discuss the issues instead of getting into it here. Any help would be appreciated. Thanks!
1539339953
The Aaron
Pro
API Scripter
Laying them out here might get better responses than I could provide, but you can PM me if you’d rather.&nbsp;
The Aaron said: Laying them out here might get better responses than I could provide, but you can PM me if you’d rather.&nbsp; Either way works. Since everyone has access to the code I may as well do it here.
The following is on the&nbsp; 3.0 branch of PurseStrings : Issue 1: I cannot get the sheet to calculate the "per use" weight after updating the CoinPurse item weight, which is the only way an item contributes to encumbrance on the sheet. I'm using the suggested .setWithWorker() which does not accomplish this any more than .set() did. Issue 2: Chat feedback has somehow been affected by the new functionality. The showPurse function works, but the dialogs (using showDialog) that are supposed to be sent to chat from the commandSetup, commandAdd, and commandSubt functions do not. These three are the only functions that call the new&nbsp;updateCoinWeight function (indirectly through the changePurse function), so I assume there's a connection. I just don't know what. Issue 3: The API Output console throws a warning every time the updateCoinWeight function is called: {"who":"error","type":"error","content":"Unable to find a player or character with name: "} The function works properly, updating the weight based on the new coin count as expected. This warning was thrown when I was using .set() instead of .setWithWorker() as well. I have no way of knowing why this is happening, but it may be affecting the showDialog in issue 2.
1539365818

Edited 1539365833
Ben L. said: ... Issue 2: Chat feedback has somehow been affected by the new functionality. The showPurse function works, but the dialogs (using showDialog) that are supposed to be sent to chat from the commandSetup, commandAdd, and commandSubt functions do not. These three are the only functions that call the new&nbsp;updateCoinWeight function (indirectly through the changePurse function), so I assume there's a connection. I just don't know what. Correction: the commandSetup function does not generate chat feedback even when no coins are passed in, so it is not the changePurse call that is causing it to fail. The mystery deepens. :(
Hello. I've been using some of the ideas in here for a script for the DCC sheet, which includes adding weapons to a character. However, the weapons have a checkbox for a certain effect, and I'm not entirely sure how to check the box via the api, or if I can. Anyone have an idea? I should specify, if I manually check the box, it does not create an attribute entry on the attributes and abilities tab. image: Gist, lines 971 and 366:&nbsp; <a href="https://gist.github.com/Bastlifa/80f98f727ff08dca5b3423776e08f0f9" rel="nofollow">https://gist.github.com/Bastlifa/80f98f727ff08dca5b3423776e08f0f9</a> Some further clarifications: The sheet has other checkboxes, but when checked, they create an attribute on the attributes and abilities tab. My method of checking those boxes is to create that attribute with the appropriate current value. I've tried doing that for "attr_zeroWeaponLuckyRoll" and "zeroWeaponLuckyRoll", and setting the value as "@{luckStartingMod}" (no quotes around any of those), but it doesn't check the box.
1539646986

Edited 1539647053
GiGs
Pro
Sheet Author
API Scripter
That gist link leads to a 404. Make sure the checkbox has an attribute (name="attr_something"), add a value="1" parameter, and then in sheet workers, you can set its value to 1 and it will be checked. Setting to 0 will uncheck it.
Apparently their crummy spam filter caught it by mistake. Why it would even look for a private gist is beyond me. After asking why I was flagged, they, ironically, sent me 11 of the same message explaining the mistake within a span of 8 minutes. Hopefully the link works now. Anyways, I'm a bit unclear on how to add a 1 parameter, and what is meant by this. The checkbox does have an attribute, shown in the picture (or seems to, "attr_zeroWeaponLuckyRoll"), but it will not create that attribute on the attributes and abilities tab when checked, and manually entering that ability there, along with any of "checked", "true", "1", "@{luckStartingMod}" in the current value does not check the box.
1539652331
GiGs
Pro
Sheet Author
API Scripter
Your script is a bit huge for me to check through, though I my comment was a reference to the character sheet code: I now see that your screenshot above is readable if I click on it, but pasting it as text would have been much more helpful. What I was suggesting was changing the "value=@{luckStartingmod}" part of your checkbox to "value=1" and using it as an on/off switch. This is the best way to use checkboxes. It should then appear in the attributes tab, but it doesnt matter if it doesn't.
Yeah, the image was really meant for clarification, the first paragraph was really where the question was: wondering how to check a box via an api script. To be clear, this isn't my sheet. I'm just writing a script for a game which uses the sheet.
1539653956
GiGs
Pro
Sheet Author
API Scripter
Ordinarily using a script to set a checkbox's value to match whatever is in the value= &nbsp;statement should check that box. Maybe the fact that that sheet uses&nbsp; "value=@{luckStartingmod}" &nbsp;is messing it up. Maybe you need to get the current value of @{luckStartingmod} in your string, and set the checkbox to that value (the number).
It's sorted. For some reason, aLuckyRoll wasn't passing through. A scope issue, I assume (I passed it to a function which modified it, but didn't return it, thinking that it would be modified in the function it was declared in). Interestingly, the value for ranged weapons is slightly different in capitalization: @{luckstartingMod} vs @{luckStartingMod} for melee.
1539675137
GiGs
Pro
Sheet Author
API Scripter
capitalisation doesnt seem to matter in ordinary attribute calls, but might be in sheet workers/scripts, so its best to be consistent.
1539771745
The Aaron
Pro
API Scripter
Repeating row attributes don’t show up in the Attributes and abilities tab. JavaScript is pass by value, but the value of some types is a reference (objects and arrays mainly), which makes them appear to pass by reference as changes to the underlying values (properties, array contents, etc) persist.&nbsp; It’s probably better for the DCC issue to have its own thread.&nbsp; Ben, I’ll try to look into this issue over the weekend when I have a larger block of time.&nbsp;