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

Sheetworker calculations within repeating section lines

1583006875
Kraynic
Pro
Sheet Author
The skills I am working with are within a percentage based system.  What I was hoping to do was hide (accessible with a checkbox) several of the skill related fields and have a "readonly" skill rating field that would display total skill rating after all the math was done.  At the moment (there may be other things added later), the process is like this: Skill Rating = Skill Base + Skill Bonus + ((Skill Level - 1) * Skill Progression) Skill Base is the percentage the skill starts out as at level 1.  Skill Progression is how many percent you gain each level you advance past 1. Current html for the section: <fieldset class="repeating_skillocc"> <div class='skill-grid-container' > <button type="roll" name='roll_occskill' value="&{template:custom} {{color=green}} {{title=Character Name}} {{subtitle=@{skill_name}}} {{Check=[[d100cs<1cf>100]]}} {{Skill=@{skill_rating}}} {{desc=@{skill_reference}}}"></button> <input class='occ-field' type="text" value="" name="attr_skill_name" placeholder="Skill name"/> <input class='occ-field' type="number" title='Total Skill Percentage Rating' value="" name="attr_skill_rating" readonly/> <h6>%</h6> <input class="occ-field" type="number" title="Skill Bonus" value="0" name="attr_skillbonus"> <input type='checkbox' title='Show Bonus and Notes Fields' name='attr_occextras' value="1"> <input class='occextras-toggle' type='hidden' name='attr_occextras'> <div class='occextras-hide'> <div class="occextras-container"> <h5>Skill Lvl</h5> <h5>Skill Base</h5> <h5>Skill Prog</h5> <h5>Notes</h5> <input class="occ-field" type="number" title="Skill Base" value="0" name="attr_skill_base"> <input class="occ-field" type="number" title="Skill Progression" value="0" name="attr_skill_prog"> <input class='occ-field' type="number" title='Skill Level' value="1" name="attr_skill_level" /> <textarea class="skill-notes" type="text" value="" name="attr_skill_reference" ></textarea> </div> </div> </div> </fieldset> My attempt at a sheetworker to accomplish this: on("change:repeating_skill_base change:repeating_skill_level change:repeating_skill_prog change:repeating_skillbonus", function () { getAttrs(['repeating_skill_base','repeating_skill_level','repeating_skill_prog'], function (values) { const repeating_skill_base = parseInt(values['repeating_skill_base'])||0; const repeating_skill_level = parseInt(values['repeating_skill_level'])||0; const repeating_skill_prog = parseInt(values['repeating_skill_prog'])||0; const repeating_skillbonus = parseInt(values['repeating_skillbonus'])||0; const repeating_skill_rating = Math.round(repeating_skill_base + repeating_skillbonus + ((repeating_skill_level -1) * repeating_skill_prog)); setAttrs({ repeating_skill_rating: repeating_skill_rating }); }); }); Since I am script illiterate and this is my attempt to cobble together a couple scripts from my existing sheet, I'm not surprised my frankenstein doesn't walk...  I first tried this without the "repeating_" on it, and that didn't work either.  I also got thinking maybe it needs pointed at the correct repeating section, but I'm not sure how to do that. So, where am I going wrong on this?  Also, is this going to slow down the sheet once someone has 20-30 skills, or will it only recalculate the actual section that changed? I could just leave the total box off and have the macro in the roll button calculate and display the total skill rating if this could cause issues down the line for characters with a lot of skills.
1583007992

Edited 1583008034
GiGs
Pro
Sheet Author
API Scripter
The first thing i notice is the attribute names in your sheet workers aren't complete. The repeating section name is repeating_skillocc, and attributes are of the form skill_base, skill_level, etc. Repeating section names on the change line and in the rest of the sheet worker use two different syntaxes. In both cases, you need to link the repeating section name and the attribute name. To detect for changes you link them with a colon, like so: change:repeating_skillocc:skill_base while to use them in the rest (getAttrs, values. etc), you link them with an underscore, like so: getAttrs(['repeating_skillocc_skill_base', and  values['repeating_skillocc_skill_base'] Hope that helps!
1583008478

Edited 1583008682
vÍnce
Pro
Sheet Author
I'm in the same boat as you when it comes to programing with js/sheetworker...Just my untrained observation; I think you might be missing "repeating_skillbonus" from getAttrs. And should the attribute be "repeating_skillbonus" or "repeating_skill_skillbonus"?  or rather "repeating_skillocc_skillbonus"  Maybe just a typo.
1583008601
Andreas J.
Forum Champion
Sheet Author
Translator
Yeah, manipulating repeating attributes are handled differently. I haven't really looked into it that much, but I know that this stuff is documented on the main sheetworker wiki page . You'll essentially use getSectionIDs to use get their IDs and then save, save it in an array, and then look through the array while performing your main thing. RepeatSum handles repeating section in a way that's less obvious, but pretty sure it's basically still the same logic.
1583009551
GiGs
Pro
Sheet Author
API Scripter
Well spotted on that missing attribute from getAttrs, Vince. Andreas, you shouldnt need to use getSectionIDs for a simple function like this. If you change an item in a row, and the change only affects other items in the same row, you can use the syntax I described above, and bypass the need for getSectionIDs. Here's a corrected sheet worker on("change:repeating_skillocc:skill_base change:repeating_skillocc:skill_level change:repeating_skillocc:skill_prog change:repeating_skillocc:skillbonus", function () {     getAttrs(['repeating_skillocc_skill_base', 'repeating_skillocc_skill_level', 'repeating_skillocc_skill_prog', 'repeating_skillocc_skillbonus'], function (values) {         const base = parseInt(values['repeating_skillocc_skill_base']) || 0;         const level = parseInt(values['repeating_skillocc_skill_level']) || 0;         const prog = parseInt(values['repeating_skillocc_skill_prog']) || 0;         const bonus = parseInt(values['repeating_skillocc_skillbonus']) || 0;         const rating = Math.round(base + bonus + ((level - 1) * prog));         setAttrs({             repeating_skillocc_skill_rating: rating         });     }); }); Note you can name the variables anything. You dont have to use the really long name that the attributes actually have - I used the short segment from the end so you can recognise which attribute it is, but its short enough to type without much pain. As a side note, I'm very bothered by the fact that you've used "skillbonus" instead of "skill_bonus" wheneverything else uses that underscore format. I'd recommend changing that attribute, for consistency, which would make the sheet worker on("change:repeating_skillocc:skill_base change:repeating_skillocc:skill_level change:repeating_skillocc:skill_prog change:repeating_skillocc:skill_bonus", function () {     getAttrs(['repeating_skillocc_skill_base', 'repeating_skillocc_skill_level', 'repeating_skillocc_skill_prog', 'repeating_skillocc_skill_bonus'], function (values) {         const base = parseInt(values['repeating_skillocc_skill_base']) || 0;         const level = parseInt(values['repeating_skillocc_skill_level']) || 0;         const prog = parseInt(values['repeating_skillocc_skill_prog']) || 0;         const bonus = parseInt(values['repeating_skillocc_skill_bonus']) || 0;         const rating = Math.round(base + bonus + ((level - 1) * prog));         setAttrs({             repeating_skillocc_skill_rating: rating         });     }); }); This isn't just a minor nitpick: consistency helps automation. As you get into more advanced scripting techniques, using a consistent format can help make things easier. It doesnt make a difference right now, in this sheet worker - but it might later on in the sheet, and it'll be harder to correct then.
1583010052

Edited 1583011324
Kraynic
Pro
Sheet Author
GiGs said: Well spotted on that missing attribute from getAttrs, Vince. As a side note, I'm very bothered by the fact that you've used "skillbonus" instead of "skill_bonus" when everything else uses that underscore format. I'd recommend changing that attribute, for consistency. (bunches of stuff snipped out) Yeah, I spotted that missing attribute while going through and correcting things after reading the first reply here.  As I was doing that, I wondered if someone would call me out on that bonus attribute...  I'll be a good boy and go make it match the others.  :p Edit:  Managed to really confuse myself before realizing that my labels were out of order with the input fields...  It is all working well now.  Thanks!
1583011417
GiGs
Pro
Sheet Author
API Scripter
so they are :) Great!
1583096808
Kraynic
Pro
Sheet Author
Ok, I have another question about this sheetworker.  I added in another value for it to grab.  There is a bonus from the IQ stat that can apply towards skills.  On my fantasy sheet, I just had the roll button subtract that from the percentile roll.  I thought I would add "iq_bonus" to this script and have it add into the visible rating on this sheet: on("change:repeating_skillocc:skill_base change:repeating_skillocc:skill_level change:repeating_skillocc:skill_prog change:repeating_skillocc:skill_bonus change:iq_bonus", function () { getAttrs(['repeating_skillocc_skill_base', 'repeating_skillocc_skill_level', 'repeating_skillocc_skill_prog', 'repeating_skillocc_skill_bonus','iq_bonus'], function (values) { const base = parseInt(values['repeating_skillocc_skill_base']) || 0; const level = parseInt(values['repeating_skillocc_skill_level']) || 0; const prog = parseInt(values['repeating_skillocc_skill_prog']) || 0; const bonus = parseInt(values['repeating_skillocc_skill_bonus']) || 0; const iq_bonus = parseInt(values['iq_bonus']) || 0; const rating = Math.round(base + iq_bonus + bonus + ((level - 1) * prog)); setAttrs({ repeating_skillocc_skill_rating: rating }); }); }); This works as far as the math goes, but it doesn't respond to changes in iq_bonus.  I have to change one of the regular inputs, and then it will recalculate and include the iq_bonus.   That bonus is provided by another sheetworker that fills it in (in a readonly field) if the IQ stat is high enough to give a bonus, and adjusts the bonus as needed if IQ changes.  Does the "on change" not get triggered if the change is made by another sheetworker? 
1583097558
GiGs
Pro
Sheet Author
API Scripter
To understand why this doesnt work, you need to understand something about repeating section attributes. Remember that the repeating section attributes are not complete attributes: An attribute in a repeating section has an actual name that looks something like this: repeating_skillocc_-6548GHL123-skill_base When the sheet is built, roll20 takes the different parts - the repeating section name, each row id, and the name you have given to each individual part of the repeating section, and puts them together to make the real  attribute name. When you use this syntax change:repeating_skillocc:skill_base roll20 monitors that repeating section, and when skill_base changes, it detects which row  that change happened on, uses that row id, to build the complete actual  attribute name. Now, when you put in an attribute outside of the section, and that changes, roll20 looks at this syntax, and sees it's an incomplete attribute name, and looks for the row id. But since the change didnt happen within the repeating section, there is no row id, so it cant construct that attribute name, and the sheet worker fails. Using this syntax can only   used to monitor changes within the repeating section itself - its designed for changes that happen one row at a time . If you want to have it respond to changes outside the repeating section, then its likely to affect multiple rows at once , and for that, you need to use the more complex getSectionIDs approach.  I posted an explanation how to do this over here  (my last big post there is an explanation with fairly easy-to-follow code, I hope).
1583098093
Kraynic
Pro
Sheet Author
Ok, I'll take a look.  If nothing else, I can go back to the method from my other sheet.
1583098281
GiGs
Pro
Sheet Author
API Scripter
You'll find it's not much more complicated, once you have a process to follow, you just have to put in a getSectionIDS call before the getAttrs, and build up a set of all the attribute names, then inside the getAttrs, loop through the rows again to do the changes you need to (adding the IQ difference to each row).
1583098379
GiGs
Pro
Sheet Author
API Scripter
I'm curious how you did it on the other sheet. As far as i know, there's no way to avoid using getSectionIDs for this.
1583099390

Edited 1583099756
Kraynic
Pro
Sheet Author
Skills in the old fantasy rule set from palladium went off a progression chart that didn't just go up a set amount each time you leveled, and different skills started and progressed at different rates.  Usually gaining relatively quickly at low level and slowing as you increased.  The occupation of your character gives one time bonuses to certain skills, as well as family/social background.  Basically, there was no calculation of skill rating on the other sheet, and it was just entered manually at character creation and edited at level up.  On that sheet, the iq_bonus attribute was subtracted from the d100 roll that was compared to the skill rating instead of adding the bonus to each individual skill rating. Basically, I avoided the whole issue on that sheet. 
1583100305

Edited 1583100332
GiGs
Pro
Sheet Author
API Scripter
Here's your sheet worker converted to use getSectionIDs. If you compare them, you can see its basically the same function, with a couple of loops to handle the repeating section part. It could be written more elegantly, but I wanted to stay close to the previous function, you could compare them and see the differences. Also reading my post in the other thread is still a good idea. on("change:repeating_skillocc:skill_base change:repeating_skillocc:skill_level change:repeating_skillocc:skill_prog change:repeating_skillocc:skill_bonus change:iq_bonus", function () {     getSectionIDs(`repeating_skillocc`, idArray => {         const fieldnames = [];         idArray.forEach(id => fieldnames.push(             `repeating_skillocc_${id}_skill_base`,             `repeating_skillocc_${id}_skill_level`,             `repeating_skillocc_${id}_skill_prog`,             `repeating_skillocc_${id}_skill_bonus`         ));         getAttrs([...fieldnames, 'iq_bonus'], function (values) {             const output = {};             const iq_bonus = parseInt(values['iq_bonus']) || 0;             idArray.forEach(id => {                 const base = parseInt(values[`repeating_skillocc_${id}_skill_base`]) || 0;                 const level = parseInt(values[`repeating_skillocc_${id}_skill_level`]) || 0;                 const prog = parseInt(values[`repeating_skillocc_${id}_skill_prog`]) || 0;                 const bonus = parseInt(values[`repeating_skillocc_${id}_skill_bonus`]) || 0;                                  const rating = Math.round(base + iq_bonus + bonus + ((level - 1) * prog));                 output[`repeating_skillocc_${id}_skill_rating`] = rating;             });             setAttrs(output);         });     }); });
1583100511
Kraynic
Pro
Sheet Author
I will.  I know there was some repeating section sum scripting on the other sheet for inventory, and I'll have to be looking all that over anyway, because I need to create a worksheet/calculator for "Bio-E" animal mutation points.  There will be a few repeating sections in there as well.
1583107456

Edited 1583210552
Caden
Forum Champion
Sheet Author
API Scripter
Compendium Curator
I was working on a similar section. You can try this. You only need to use getIDSection if you're pulling information from other repeating rows. Which maybe that's what you want to do. I'll admit I didn't read the whole post soooo my solution may be missing key info. const parseIntegers = numbers => {     for (let [key, value] of Object.entries(numbers)) {         numbers[key] = parseInt(value) || 0;     }     return numbers } const getRepeatingSection = triggerName => {     const split = triggerName.split('_');     return `${split[0]}_${split[1]}_${split[2]}` } on("change:repeating_skillocc:skill_base change:repeating_skillocc:skill_level change:repeating_skillocc:skill_prog change:repeating_skillocc:skill_bonus", function (eventinfo) {     const section = getRepeatingSection(eventinfo.triggerName);          getAttrs([`${section}_skill_base`, `${section}_skill_level`, `${section}_skill_prog`, `${section}_skill_bonus`], function (values) {         const numbers = parseIntegers(values);         const base = numbers[`${section}_skill_base`];         const level = numbers[`${section}_skill_level`];         const prog = numbers[`${section}_skill_prog`];         const bonus = numbers[`${section}_skill_bonus`];         setAttrs({             [`${section}_skill_rating`]: Math.round(base + bonus + ((level - 1) * prog))         });     }); });