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 .
×

Repeating section help needed

I am new to this and just started playing with the custom character sheet editor a month ago. I am trying to update a field in a specific repeating section when either a text input or a checkbox input changes in that specific repeating section. I have narrowed the problem to the posted section because everything else works when I comment it out. These are the repeating row input elements. <fieldset class="repeating_meleeweapons">    <input Class="sheet-tiny" type="text" name="attr_mwreq_str" /> <input Class="sheet-short" type="checkbox" name="attr_mwadd_str" value="1" /> <input Class="sheet-tiny" type="text" name="attr_mwdamage" readonly/> strength is outside the repeating section. <label class='sheet-heavy'>Strength </label><input type="text" name='attr_strength' class='sheet-short'> This is the script that breaks. on("change:repeating_meleeweapons:mwadd_str change:repeating_meleeweapons:mwreq_str", function() { getAttrs(["strength","repeating_meleeweapons_mwadd_str","repeating_meleeweapons_mwreq_str"], function(values) { let str = parseInt(values.strength)||0; let min = parseInt(values.repeating_meleeweapons_mwreq_str)||0; let sck = parseInt(values.repeating_meleeweapons_mwadd_str)||0; if (min < str && sck = 1) { bonus = str - min; } else { bonus = 0; } setAttrs({ "repeating_meleeweapons_mwdamage": bonus }); }); }); Thanks
1604859700

Edited 1604861092
GiGs
Pro
Sheet Author
API Scripter
When you have an attribute outside the repeating section, you have to use the getSectionIDs function. The problem is the syntax you have there is designed for making changes to just one row. But when strength changes, you need to force a recalculation of all rows, since str is involved in all rows. This should do the trick. on("change:strength change:repeating_meleeweapons:mwadd_str change:repeating_meleeweapons:mwreq_str", function() {       getSectionIDs('repeating_meleeweapons', function(idarray) {         const fieldnames = {};         idarray.forEach(id => fieldnames.push(             `repeating_meleeweapons_${id}_mwadd_str`,             `repeating_meleeweapons_${id}_mwreq_str`         ));         getAttrs(["strength", ...fieldnames], function(values) {             const output = {};             const str = parseInt(values.strength)||0;             idarray.forEach(id => {                 let min = parseInt(values[`repeating_meleeweapons_${id}_mwreq_str`])||0;                 let sck = parseInt(values[`repeating_meleeweapons_${id}_mwadd_str`])||0;                 let bonus = (min < str && sck === 1) ? str - min : 0;                 output[`repeating_meleeweapons_${id}_mwdamage`] = bonus;                              });                                       setAttrs(output);         });     }); });
1604861290
GiGs
Pro
Sheet Author
API Scripter
By the way, this line may have also been causing you issues: if (min < str && sck = 1) { in javascript, a single = always means set , so that if statmenet was assigning a value of 1 to sck. To compare values you need to use == or === (preferably the later). The double equality compares values and tries to guess the type (e.g. string or integer), while triple === requires the type to be the same (if sck is an integer, then 1 is fine; if sck is a string, you need sck === '1' (comparing to string). Since you know the type (you used parseInt to make sure they are integers), the triple equality is the one to use.
Hello GiGs,  Your second post fixed the issue with the current script perfectly. My next step will be to implement updating of the entire section when strength changes or the sheet opens and you just gave me the answer for that as well. Thanks again for all the help.
1604864125
GiGs
Pro
Sheet Author
API Scripter
You're welcome!
Hello GiGs, One last question on this, hopefully, how would I specify multiple outputs, like assigning values to multiple attributes?
1604872814
GiGs
Pro
Sheet Author
API Scripter
you can just assign different values to output, like in this line: output[`repeating_meleeweapons_${id}_mwdamage`] = bonus; Just put the attribute name inside the [` `] part, and the value you want to assign after the =. That output is a special kind of variable, called a Javascript Object, and it can hold multiple attributes and their scores. If you want (this isnt necessary), you can see what's in it by adding code to your worker to log its values: console.log(output); or console.log(`Output is ${JSON.stringify(output)}`); This will then show up in the browser console, which you can find by typing F12 when youre viewing your campaign. That will open the developer tabs, and one of them is the console.
Hello GiGs, The script is not working again. I did make some changes and trying to update 2 fields in the repeating section when strength(outside repeating section) changes, and when the sheet is opened.   on("change:strength sheet:opened", function() { getSectionIDs('repeating_meleeweapons', function(idarray) { const fieldnames = {}; idarray.forEach(id => fieldnames.push( `repeating_meleeweapons_${id}_mwadd_str`, `repeating_meleeweapons_${id}_mwwounds`, `repeating_meleeweapons_${id}_mwreq_str` )); getAttrs(["strength","repeating_meleeweapons_mwadd_str","repeating_meleeweapons_mwreq_str","repeating_meleeweapons_mwwounds"], function(values) { const output = {}; const str = parseInt(values.strength)||0; idarray.forEach(id => { let min = parseInt(values[`repeating_meleeweapons_${id}_mwreq_str`])||0; let sck = parseInt(values[`repeating_meleeweapons_${id}_mwadd_str`])||0; let wnd = parseInt(values[`repeating_meleeweapons_${id}_mwwounds`])||0; let bonus = (min < str && sck === 1) ? str - min : 0; let modifier = (min > str) ? str - min : 0; output[`repeating_meleeweapons_${id}_mwdamage`] = (bonus * 2) + wnd [`repeating_meleeweapons_${id}_mwstr_mod`] = modifier; }); setAttrs(output); }); }); }); Not sure where I went wrong.
1604877526

Edited 1604878456
Oosh
Sheet Author
API Scripter
I'm a novice, but I'll try - I think you need to use your fieldnames object (which you put together in lines 3-7) in the getAttrs arguments, as GiGs has done above. So         getAttrs(["strength", ...fieldnames], function(values) { That object has all the actual row id's in the Attribute names, where "repeating_meleeweapons_mwreq_str" doesn't have a row id to look for. The second line where you're setting a value in your output array also looks a bit fishy. The second line will need the output object specified again, and either comma separated on the same line, or semi-colon the line above: output[`repeating_meleeweapons_${id}_mwdamage`] = (bonus * 2) + wnd; output[`repeating_meleeweapons_${id}_mwstr_mod`] = modifier; If you want to set them as an object without repeating the word 'output', I think you need something like this: output = { 'repeating_meleeweapons_${id}_mwdamage': (bonus * 2) + wnd, 'repeating_meleeweapons_${id}_mwstr_mod': modifier, }
Oosh proposed output[`repeating_meleeweapons_${id}_mwdamage`] = (bonus * 2) + wnd; output[`repeating_meleeweapons_${id}_mwstr_mod`] = modifier; I made this change and this change stopped the script from crashing, that is a good change. but the repeating sections do not update still when strength changes. I changed the names in the getAttrs to include the row IDs  _${id} but this did not fix the issue. Current version of the script still not updating but not crashing the script either. on("change:strength sheet:opened", function() { getSectionIDs('repeating_meleeweapons', function(idarray) { const fieldnames = {}; idarray.forEach(id => fieldnames.push( `repeating_meleeweapons_${id}_mwadd_str`, `repeating_meleeweapons_${id}_mwwounds`, `repeating_meleeweapons_${id}_mwreq_str` )); getAttrs(["strength","repeating_meleeweapons_${id}_mwadd_str","repeating_meleeweapons_${id}_mwreq_str","repeating_meleeweapons_${id}_mwwounds"], function(values) { const output = {}; const str = parseInt(values.strength)||0; idarray.forEach(id => { let min = parseInt(values[`repeating_meleeweapons_${id}_mwreq_str`])||0; let sck = parseInt(values[`repeating_meleeweapons_${id}_mwadd_str`])||0; let wnd = parseInt(values[`repeating_meleeweapons_${id}_mwwounds`])||0; let bonus = (min < str && sck === 1) ? str - min : 0; let modifier = (min > str) ? str - min : 0; output[`repeating_meleeweapons_${id}_mwdamage`] = (bonus * 2) + wnd; output[`repeating_meleeweapons_${id}_mwstr_mod`] = modifier; }); setAttrs(output); }); }); }); Any further help will be greatly appreciated.
1604887873
timmaugh
Roll20 Production Team
API Scripter
Are you actually getting the correct value for Strength? If you drop a log(values.strength) in your getAttrs() callback, do you get the value you would expect?
timmaugh wrote Are you actually getting the correct value for Strength? If you drop a log(values.strength) in your getAttrs() callback, do you get the value you would expect? I am very new at this and not familiar with how to do that. like this? getAttrs(["strength","repeating_meleeweapons_${id}_mwadd_str","repeating_meleeweapons_${id}_mwreq_str","repeating_meleeweapons_${id}_mwwounds"], function(values) { const output = {}; log(values.strength); const str = parseInt(values.strength)||0;
1604893609

Edited 1604898739
GiGs
Pro
Sheet Author
API Scripter
Oosh spotted two issues. Another is your on(change) line doesnt have the repeating section attributes any more, so the worker wont respond to changes in those. So try the code below: Note also that because you had the wrong stats listed in getAttrs (you need to use ...fieldnames), all of the repeating section attributes would have been treated as zero in the following calculations. They should work properly in the code below. on("change:strength change:repeating_meleeweapons:mwadd_str change:repeating_meleeweapons:mwreq_str change:repeating_meleeweapons:mwwounds sheet:opened", function() {       getSectionIDs('repeating_meleeweapons', function(idarray) {         const fieldnames = [];         idarray.forEach(id => fieldnames.push(             `repeating_meleeweapons_${id}_mwadd_str`,             `repeating_meleeweapons_${id}_mwwounds`,             `repeating_meleeweapons_${id}_mwreq_str`         ));         getAttrs(["strength", ...fieldnames], function(values) {             const output = {};             const str = parseInt(values.strength)||0;             idarray.forEach(id => {                 let min = parseInt(values[`repeating_meleeweapons_${id}_mwreq_str`])||0;                 let sck = parseInt(values[`repeating_meleeweapons_${id}_mwadd_str`])||0;                 let wnd = parseInt(values[`repeating_meleeweapons_${id}_mwwounds`])||0;                 let bonus = (min < str && sck === 1) ? str - min : 0;                 let modifier = (min > str) ? str - min : 0;                 output[`repeating_meleeweapons_${id}_mwdamage`] = (bonus * 2) + wnd;                 output[`repeating_meleeweapons_${id}_mwstr_mod`] = modifier;                          });             setAttrs(output);         });     }); });
1604894706

Edited 1604894929
Hello GiGs, Thanks again for assisting. I was trying to divide the update into three different scripts. Two that run if the repeating section is updated and one that runs if strength(outside the repeating section) changes and on sheet open. I commented out all 3 of the earlier ones I added and added yours. I tried it and it does NOT crash the script but it does not update anything. I checked the console and saw this error, though I do not understand it completely. TypeError: fieldnames.push is not a function     at eval (eval at messageHandler (sheetsandboxworker.js?1604893975088:698), <anonymous>:151:46)     at Array.forEach (<anonymous>)     at Object.eval [as -MIKX6-azxcs1ZRo38pO//repeating_meleeweapons_-mlcnkrx78u3z4mvp5ru//0.7673479271107955] (eval at messageHandler (sheetsandboxworker.js?1604893975088:698), <anonymous>:151:21)     at _fullfillAttrReq (sheetsandboxworker.js?1604893975088:673)     at messageHandler (sheetsandboxworker.js?1604893975088:708) Thanks again for the assistance.
1604898699

Edited 1604898768
GiGs
Pro
Sheet Author
API Scripter
Gah, thats my mistake. Change the const fieldnames line to an array, like so const fieldnames = []; I've corrected it in my last post.
1604898839

Edited 1604898865
Oosh
Sheet Author
API Scripter
edit - too slow, ill go back to sleep I haven't played in the sheet sandbox and so I'm not familiar with getSectionIDs and exactly how that works... but .push() is an array function AFAIK, so I would have thought the variable declaration needs to be:  const fieldnames = []; I could be wrong through. You might then want to push each id's worth of attributes as a single object into the array - not 100% sure on that though. They're probably fine as a single big array, but then again I don't know exactly how getAttrs works.
Thanks GiGs and Oosh, This works now as expected. It's great to have your help with these complex scripts. I could not have done it without you. Thanks again!
1604935140
GiGs
Pro
Sheet Author
API Scripter
Oosh said: I could be wrong through. You might then want to push each id's worth of attributes as a single object into the array - not 100% sure on that though. They're probably fine as a single big array, but then again I don't know exactly how getAttrs works. Just for information purposes - getAttrs accepts a single flat array, it can be as large as you want, but each item in the array is the name of an attribute on the character sheet. It then grabs all the values of those attributes from the character sheet, and stores the name and value in a javascript object variable with the name you supply in the getAttrs function (which is often values , so something like it). So we have this: getAttrs(["strength", ...fieldnames], function(values) { now the ... part is the split operator. It takes an array, and expands it into its constituent items - its a way to in this case take an array and expand it into another array (no nesting). Another way to write the above is getAttrs(["strength"].concat(fieldnames), function(values) { concat is a function which combines two arrays. Either way is fine, but the split operator is more flexible (handles more than 2 arrays better, for example). So after that line is run, you have a javascript object called values  with the names and scores of all supplied attributes (which is strength, and the listed attributes from every row of the repeating section, however many rows there are). So, working backwards, the getSectionIDs returns an array of row ids, one for each row of the repeating section. The forEach loop then loops through each row, gets the proper full name of each attribute, and pushes them into the fieldnames array. Then getAttrs uses it to get the values of all the row attributes, stored with their names. And the sheet worker than then use the idarray again (the one created earlier by getSectionIDs) to loop through the section, and update the needed attributes on each row of the section. Its a slightly convoluted structure, but one we are forced into by the networking constraints of getAttrs.
1604968212
Oosh
Sheet Author
API Scripter
Awesome, thanks GiGs!