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

Repeating Sections

Ok, I'm stumped.  Probably because I haven't coded in 10 years and I don't get all the javascript shorthand like arrow notation :-P I have a repeating section of weapons.  I want to spin through the list and see if the weapon uses a certain skill.  If so, then I'm going to change a different value on that weapon.  Here's what I'm doing:         getSectionIDs("weapons", function (idarray) {             for (var i=0; i<idarray.length; i++) {                 getAttrs(["repeating_weapons_" + idarray[i] + "_weapon_melee_skill"], function(repeatingvalues) {                     console.log(repeatingvalues);                     console.log(repeatingvalues("repeating_weapons_" + idarray[i] + "_weapon_melee_skill"));                 });             }         }); I get that getAttrs is async and i is out of range before the callback happens so the second console.log is undefined.  repeatingvalues has what I need.  I just have no idea how to reference it inside the getAttrs callback.   Here is the result of the first console.log: {repeating_weapons_-mubbnijfifbgj-fiqii_weapon_melee_skill: "1H Edged"} How in the world do I get that  -mubbnijfifbgj-fiqii  so i can reference the field I want to update? Many thanks in advanced.
1614918844

Edited 1614919841
GiGs
Pro
Sheet Author
API Scripter
There's not enough information in your post to give you the code you need. But in principle, you dont want to have getAttrs (or setAttrs) inside a loop. The way to do this is (pseudo code): getSectionIDs     have a loop to create an array of the attributes      getAttrs([the array you just created], vales => {         create a variable to hold the stats you want to update         loop to go through idarray again             all your work is done inside this loop, saving any changed attributes and values to the variable just created         loop ends         setAttrs(save the variable you created above) So yes, you essentially do a loop through idarray twice: once before getAttrs, to build the names of all the attributes in the repeating section, so getAttrs can retrieve them, then loop again inside getAttrs to do the work you need, so the context isnt lost. Here's partial code for what that would look like: getSectionIDs ( 'repeating_weapons' ,  idarray   =>  {      const   fieldnames  = [];      idarray . forEach ( id   =>  {          fieldnames . push ( 'repeating_weapons_'  +  id  +  '_weapon_melee_skill' );     });      // at this point fieldnames includes the full name of weapon_melee_skill for all rows of the repeating section      getAttrs ( fieldnames ,  values   =>  {          //create an empty object variable into which you'll save any changed values          const   output  = {};          // the idarray array still exists, so you can use it to loop through the repeating section rows again          idarray . forEach ( id   =>  {              // as you loop through through the rows, get the value of the weapon_skill              const   skill  =  values [ 'repeating_weapons_'  +  id  +  '_weapon_melee_skill' ];              // do whatever you need here.         });          // once you have done all the work you need to do, save any changed attributes to the sheet.          setAttrs ( output );     }); });
Makes sense.  Will give it a shot.  
1614920441
timmaugh
Pro
API Scripter
Hey, Cary... the quick/dirty answer is you apply a regex like this: let fullrx = /^repeating_([^_]*?)_([^_]*?)_(.+)$/; let parts = fullrx.exec(repeatingvalues); log(parts[1]);    // outputs weapons log(parts[2]);    // outputs sectionid log(parts[3]);    // outputs suffix The longer answer is... I'm not sure why you're going that far to get the info you want... This will get you the list of repeating element ids for a given section on a given character: let [section, character_id] = ['weapons', '-M2nd0vns0zf-sa-1']; // could be passed into a function or retrieved dynamically let sectionrx = new RegExp(`repeating_${section}_([^_]+)_.*$`); let elemIDs = [...new Set(findObjs({ type: 'attribute', characterid: character_id })     .filter(a => sectionrx.test(a.get('name')))     .map(a => sectionrx.exec(a.get('name'))[1]))]; Then you can keep connecting array functions to the end. The next one would be a map, to reconstruct the full name using the suffix  you want and return an object bearing that name... or to return both things you're looking for (then we'll filter): elemIDs.map(e => {     return {         melee: findObjs({ type: 'attribute', characterid: character_id, name: `repeating_weapons_${e}_weapon_melee_skill`})[0],         secondary: findObjs({ type: 'attribute', characterid: character_id, name: `repeating_weapons_${e}_weapon_secondary_attr`})[0]     };)     .filter(a => a.melee && a.secondary && a.melee.get('current') === 'skill you are looking for')     .forEach(a => {         // set the values you need to set on a.secondary     }); You don't have to go asynchronous to get what you're looking for.
1614921801

Edited 1614921847
Ok, I have everything working.  console log shows everything good.  I have this code at the bottom         var attrs = {};         attrs["repeating_weapons_" + id + "_weapon_melee_bonus"] = mbonus;         attrs["repeating_weapons_" + id + "_weapon_ranged_bonus"] = rbonus;         console.log(attrs);         setAttrs({             attrs         }); When I show attrs, it looks like I want: {repeating_weapons_-mubbnijfifbgj-fiqii_weapon_melee_bonus: 72, repeating_weapons_-mubbnijfifbgj-fiqii_weapon_ranged_bonus: 11} But when I call setAttrs, it doesn't update my repeating section.  I've double checked all my variable names.  What am I missing?  Is my attrs variable formatted correctly for setAttrs?
1614921951

Edited 1614922103
Grr... brackets..  Sometimes just typing the question helps you see it.  :-) For those that might look this up in the future. setAttrs({             attrs         }); Should have been: setAttrs(attrs); Since attrs was already an object.  
1614922330

Edited 1614922475
GiGs
Pro
Sheet Author
API Scripter
timmaugh said: Hey, Cary... the quick/dirty answer is you apply a regex like this: let fullrx = /^repeating_([^_]*?)_([^_]*?)_(.+)$/; let parts = fullrx.exec(repeatingvalues); log(parts[1]);    // outputs weapons log(parts[2]);    // outputs sectionid log(parts[3]);    // outputs suffix The longer answer is... I'm not sure why you're going that far to get the info you want... This will get you the list of repeating element ids for a given section on a given character: let [section, character_id] = ['weapons', '-M2nd0vns0zf-sa-1']; // could be passed into a function or retrieved dynamically let sectionrx = new RegExp(`repeating_${section}_([^_]+)_.*$`); let elemIDs = [...new Set(findObjs({ type: 'attribute', characterid: character_id })     .filter(a => sectionrx.test(a.get('name')))     .map(a => sectionrx.exec(a.get('name'))[1]))]; Then you can keep connecting array functions to the end. The next one would be a map, to reconstruct the full name using the suffix  you want and return an object bearing that name... or to return both things you're looking for (then we'll filter): elemIDs.map(e => {     return {         melee: findObjs({ type: 'attribute', characterid: character_id, name: `repeating_weapons_${e}_weapon_melee_skill`})[0],         secondary: findObjs({ type: 'attribute', characterid: character_id, name: `repeating_weapons_${e}_weapon_secondary_attr`})[0]     };)     .filter(a => a.melee && a.secondary && a.melee.get('current') === 'skill you are looking for')     .forEach(a => {         // set the values you need to set on a.secondary     }); You don't have to go asynchronous to get what you're looking for. I don't believe the code you're suggesting above will work in a sheet worker. The API and sheet workers are different environments. for example, findObjs is an API function - you cant use it in a sheet worker. If you're getting a value from the character sheet in a sheet worker, you have to use getAttrs, and so you always have to go asynchronous. That said, this question is in the API forum, so if Cary is actually using the API your answer is a much  better one than mine. It's just the first post started with getSectionIDs, and referred to getAttrs, so I assumed it was a sheet worker question.
No APIs.  I'm working on a community sheet.
1614923014

Edited 1614923508
Speaking of...  Can you send something to the chat besides a roll from a sheetworker?  I've got a worker to generate stats randomly (my randomInteger post in another thread).  Instead of randomly populating the character sheet, I was just going to output them to the chat window.   I know I could just show them in a field on the sheet, but I wanted to broadcast it so everybody would see what they rolled.  I made a rolltemplate but can't figure out how to call it from the sheet.  Can you combine a sheetworker and a rollbutton?  Just thinking out loud. And the reason I want to do it from a sheetworker is because of some of the formulas that can be applied to the rolls (e.g. roll 9, keep 6).  I have a roll button now that kind of works, but it only shows the result of the /r 9d100k6.  I want to show the individual rolls that are kept.
1614923367
timmaugh
Pro
API Scripter
GiGs said: I don't believe the code you're suggesting above will work in a sheet worker. The API and sheet workers are different environments. for example, findObjs is an API function - you cant use it in a sheet worker. If you're getting a value from the character sheet in a sheet worker, you have to use getAttrs, and so you always have to go asynchronous. That said, this question is in the API forum, so if Cary is actually using the API your answer is a much  better one than mine. It's just the first post started with getSectionIDs, and referred to getAttrs, so I assumed it was a sheet worker question. A ha. Yes, I missed that. Thanks.
1614923948
GiGs
Pro
Sheet Author
API Scripter
Cary said: Speaking of...  Can you send something to the chat besides a roll from a sheetworker?  I've got a worker to generate stats randomly (my randomInteger post in another thread).  Instead of randomly populating the character sheet, I was just going to output them to the chat window.   I know I could just show them in a field on the sheet, but I wanted to broadcast it so everybody would see what they rolled.  I made a rolltemplate but can't figure out how to call it from the sheet.  Can you combine a sheetworker and a rollbutton?  Just thinking out loud. And the reason I want to do it from a sheetworker is because of some of the formulas that can be applied to the rolls (e.g. roll 9, keep 6).  I have a roll button now that kind of works, but it only shows the result of the /r 9d100k6.  I want to show the individual rolls that are kept. Unfortunately you cant combine sheet workers and printing to chat directly. You can have an action button that triggers a sheet worker, and updates in attribute. And then a roll button can print that contents of that attribute to chat. So this is a two step process. There's no way to combine them as one action without using an API script.