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

Assitance with Sheetworkers:

I need help with sheetworkers in adding together two key values for a repeating ability below is the relevant code: <div class="sheet-mainstat sheet-block"> <h3>Prime Stat <select name="attr_mainstat" id="attr_mainstat" > <option value="@{mig}">MIGHT</option> <option value="@{agi}">AGILITY</option> <option value="@{rea}">REASON</option> <option value="@{int}">INTUITION</option> <option value="@{pre}">PRESENCE</option> </select></h3> </div> <div class="sheet-skillstat sheet-block"> <h3>Skill Stat <select name="attr_skillstat" id="attr_skillstat"> <option value="@{mig}">MIGHT</option> <option value="@{agi}">AGILITY</option> <option value="@{rea}">REASON</option> <option value="@{int}">INTUITION</option> <option value="@{pre}">PRESENCE</option> </select></h3> </div> <div class="sheet-characteristics sheet-block"> <h3>Characteristics</h3> <label>Might <button id="attrright" name="roll_might" value="/r 2d10+@{mig}" type="roll"></button><input type="number" name="attr_mig" id="attrright" value></label> <label>Agility <button id="attrright" name="roll_agility" value="/r 2d10+@{agi}" type="roll"></button><input type="number" name="attr_agi" id="attrright"></label> <label>Reason <button id="attrright" name="roll_reason" value="/r 2d10+@{rea}" type="roll"></button><input type="number" name="attr_rea" id="attrright"></label> <label>Intuition <button id="attrright" name="roll_intuition" value="/r 2d10+@{int}" type="roll"></button><input type="number" name="attr_int" id="attrright"></label> <label>Presence <button id="attrright" name="roll_presence" value="/r 2d10+@{pre}" type="roll"></button><input type="number" name="attr_pre" id="attrright"></label> </div> <div class="sheet-abils sheet-block"> <h3>Abilities</h3> <fieldset class="repeating_attack" style="display: none;"> <div class="attack"> <input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked"><span>y</span> <div class="options"> <div class="option-primary"><span></span> <p>Ability Name: <input type="text" name="attr_atkname"></p> <p>Characteristic: <select name="attr_abilstat" id="attr_abilstat"> <option value="@{mig}" selected="selected" >MIGHT</option> <option value="@{agi}">AGILITY</option> <option value="@{rea}">REASON</option> <option value="@{int}">INTUITION</option> <option value="@{pre}">PRESENCE</option> </select> Properties: <input type="text" name="attr_property"></p></p> <p>≤11: <input type="text" name="attr_powerroll1"> Base Condition <input type="text" name="attr_basecond1"> Potency: <select type="number" name="attr_potselect1" id="attr_potselect1"> <option value="none" selected="selected" >-</option> <option value="-2">WEAK</option> <option value="-1">AVERAGE</option> <option value="0">STRONG</option> </select> Potency Condition <input type="text" name="attr_potcond1"></p> <p>12-16: <input type="text" name="attr_powerroll2"> Base Condition <input type="text" name="attr_basecond2"> Potency: <select type="number" name="attr_potselect2" id="attr_potselect2"> <option value="none" selected="selected" >-</option> <option value="-2">WEAK</option> <option value="-1">AVERAGE</option> <option value="0">STRONG</option> </select> Potency Condition <input type="text" name="attr_potcond2"></p> <p>17+: <input type="text" name="attr_powerroll3"> Base Condition <input type="text" name="attr_basecond3"> Potency: <select type="number" name="attr_potselect3" id="attr_potselect3"> <option value="none" selected="selected" >-</option> <option value="-2">WEAK</option> <option value="-1">AVERAGE</option> <option value="0">STRONG</option> </select> Potency Condition <input type="text" name="attr_potcond3"></p> <p><input type="text" name="attr_description"></p> </div> </div> <div class="display"> <p><button type="roll" name="roll_attack" class="btn ui-draggable abil_name"><span name="attr_atkname"></span> <input type="text" name="attr_property" value> </button></p> <p><button type="roll" name="attr_roll_dmg" class="btn ui-draggable"> <span style="display: flex; justify-content: flex-start ;">DAMAGE</span> <p><input type="number" name="attr_powerroll1" value></p> <p><input type="number" name="attr_powerroll2" value></p> <p><input type="number" name="attr_powerroll3" value></p> </button> <button type="roll" name="attr_roll_dmg" class="btn ui-draggable"> <span style="display: flex; justify-content: flex-start ;">BASE COND.</span> <p><input type="text" name="attr_basecond1" value></p> <p><input type="text" name="attr_basecond2" value></p> <p><input type="text" name="attr_basecond3" value></p> </button> <button type="roll" name="potency_result" class="btn ui-draggable"> <span style="display: flex; justify-content: flex-start ;">POTENCY</span> <p><input type="number" name="attr_potency_result1" value></p> <p><input type="number" name="attr_potency_result2" value></p> <p><input type="number" name="attr_potency_result3" value></p> </button> <button type="roll" name="attr_roll_dmg" class="btn ui-draggable"> <span style="display: flex; justify-content: flex-start ;">POTENCY COND.</span> <p><input type="text" name="attr_potcond1" value></p> <p><input type="text" name="attr_potcond2" value></p> <p><input type="text" name="attr_potcond3" value></p> </button> <button type="roll" name="roll_dmg" class="btn ui-draggable"><span name="attr_description"></span> </button></p> </div> </fieldset> <div class="repcontainer ui-sortable" data-groupname="repeating_attack"></div> <script type="text/worker"> on("change:mig change:agi change:rea change:int change:pre change:mainstat change skillstat sheet:opened", function() { getAttrs(["skillstat", "mainstat", "mig", "agi", "rea", "int", "pre"], function(values) { let mainref = parseInt(values.mainstat) || 0; let skillref = parseInt(values.skillstat) || 0; let mig = parseInt(values.mig) || 0; let agi = parseInt(values.agi) || 0; let rea = parseInt(values.rea) || 0; let int = parseInt(values.int) || 0; let pre = parseInt(values.pre) || 0; let mainvalue = 0; let skillvalue = 0; if (mainref = "@{mig}") mainvalue = parseInt(values.mig) || 0; else if (mainref = "@{agi}") mainvalue = parseInt(values.agi) || 0; else if (mainref = "@{rea}") mainvalue = parseInt(values.rea) || 0; else if (mainref = "@{int}") mainvalue = parseInt(values.int) || 0; else if (mainref = "@{pre}") mainvalue = parseInt(values.pre) || 0; if (skillref = "@{mig}") skillvalue = parseInt(values.mig) || 0; else if (skillref = "@{agi}") skillvalue = parseInt(values.agi) || 0; else if (skillref = "@{rea}") skillvalue = parseInt(values.rea) || 0; else if (skillref = "@{int}") skillvalue = parseInt(values.int) || 0; else if (skillref = "@{pre}") skillvalue = parseInt(values.pre) || 0; let burger = (mainref + agi) setAttrs({ "heavymight": burger }); }); }); </script> </div> Below is the html output. I am trying to Add the damage inputs on 11, 12-16, and 17 with the Ability's characteristic and the Potency Selection with the Mainstat. How do I go about doing this with the sheetworker's script?
1756765264
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
You will need to use the getSectionIDs sheetworker  to get all of the row IDs for the section, then assemble an array of attributes to get. Something like this: on('...',(event)=>{ getSectionIDs('repeating_attack',ids => { const getArray = ids.reduce((m,id) => { m.push(`repeating_attack_${id}_powerroll1`,`repeating_attack_${id}_powerroll2`,`repeating_attack_${id}_powerroll3`) },['non repeating attributes to get']); const getAttrs(getArray,attributes => { // do your logic and sheetworker work here }) }); });
Scott C. said: You will need to use the getSectionIDs sheetworker  to get all of the row IDs for the section, then assemble an array of attributes to get. Something like this: on('...',(event)=>{ getSectionIDs('repeating_attack',ids => { const getArray = ids.reduce((m,id) => { m.push(`repeating_attack_${id}_powerroll1`,`repeating_attack_${id}_powerroll2`,`repeating_attack_${id}_powerroll3`) },['non repeating attributes to get']); const getAttrs(getArray,attributes => { // do your logic and sheetworker work here }) }); }); I don't have a background in html and I need a better explaination as I do not know how to plug that in.  on("change:mig change:agi change:rea change:int change:pre change:mainstat change skillstat sheet:opened", function() { getSectionIDs('repeating_attack',ids => { const getArray = ids.reduce((m,id) => { m.push(`repeating_attack_${id}_powerroll1`,`repeating_attack_${id}_powerroll2`,`repeating_attack_${id}_powerroll3`) },['non repeating attributes to get']); const getAttrs(getArray,attributes => { getAttrs(["skillstat", "mainstat", "mig", "agi", "rea", "int", "pre"], function(values) { let mainref = parseInt(values.mainstat) || 0; let skillref = parseInt(values.skillstat) || 0; let mig = parseInt(values.mig) || 0; let agi = parseInt(values.agi) || 0; let rea = parseInt(values.rea) || 0; let int = parseInt(values.int) || 0; let pre = parseInt(values.pre) || 0; let mainvalue = 0; let skillvalue = 0; if (mainref = "@{mig}") mainvalue = parseInt(values.mig) || 0; else if (mainref = "@{agi}") mainvalue = parseInt(values.agi) || 0; else if (mainref = "@{rea}") mainvalue = parseInt(values.rea) || 0; else if (mainref = "@{int}") mainvalue = parseInt(values.int) || 0; else if (mainref = "@{pre}") mainvalue = parseInt(values.pre) || 0; if (skillref = "@{mig}") skillvalue = parseInt(values.mig) || 0; else if (skillref = "@{agi}") skillvalue = parseInt(values.agi) || 0; else if (skillref = "@{rea}") skillvalue = parseInt(values.rea) || 0; else if (skillref = "@{int}") skillvalue = parseInt(values.int) || 0; else if (skillref = "@{pre}") skillvalue = parseInt(values.pre) || 0; let burger = (mainref + agi) setAttrs({ "heavymight": burger }) }); }); </script>
<div class="sheet-abils sheet-block"> <h3>Abilities</h3> <fieldset class="repeating_attack" style="display: none;"> <div class="attack"> <input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked"><span>y</span> <div class="options"> <div class="option-primary"><span></span> <p>Ability Name: <input type="text" name="attr_atkname"></p> <p>Characteristic: <select name="attr_abilstat" id="attr_abilstat"> <option value="@{mig}" selected="selected" >MIGHT</option> <option value="@{agi}">AGILITY</option> <option value="@{rea}">REASON</option> <option value="@{int}">INTUITION</option> <option value="@{pre}">PRESENCE</option> </select> Properties: <input type="text" name="attr_property"></p></p> <p>≤11: <input type="text" name="attr_powerroll1"> Base Condition <input type="text" name="attr_basecond1"> Potency: <select type="number" name="attr_potselect1" id="attr_potselect1"> <option value="none" selected="selected" >-</option> <option value="-2">WEAK</option> <option value="-1">AVERAGE</option> <option value="0">STRONG</option> </select> Potency Condition <input type="text" name="attr_potcond1"></p> <p>12-16: <input type="text" name="attr_powerroll2"> Base Condition <input type="text" name="attr_basecond2"> Potency: <select type="number" name="attr_potselect2" id="attr_potselect2"> <option value="none" selected="selected" >-</option> <option value="-2">WEAK</option> <option value="-1">AVERAGE</option> <option value="0">STRONG</option> </select> Potency Condition <input type="text" name="attr_potcond2"></p> <p>17+: <input type="text" name="attr_powerroll3"> Base Condition <input type="text" name="attr_basecond3"> Potency: <select type="number" name="attr_potselect3" id="attr_potselect3"> <option value="none" selected="selected" >-</option> <option value="-2">WEAK</option> <option value="-1">AVERAGE</option> <option value="0">STRONG</option> </select> Potency Condition <input type="text" name="attr_potcond3"></p> <p> Description: <input type="text" name="attr_description"></p> </div> </div> <div class="display"> <p><button type="roll" name="roll_attack" class="btn ui-draggable abil_name"><span name="attr_atkname"></span> <input type="text" name="attr_property" value> </button></p> <p><button type="roll" name="attr_roll_dmg" class="btn ui-draggable"> <span style="display: flex; justify-content: flex-start ;">DAMAGE</span> <p><input type="number" name="attr_powerrolldmg1" value></p> <p><input type="number" name="attr_powerrolldmg2" value></p> <p><input type="number" name="attr_powerrolldmg3" value></p> </button> <button type="roll" name="attr_roll_dmg" class="btn ui-draggable"> <span style="display: flex; justify-content: flex-start ;">BASE COND.</span> <p><input type="text" name="attr_basecond1" value></p> <p><input type="text" name="attr_basecond2" value></p> <p><input type="text" name="attr_basecond3" value></p> </button> <button type="roll" name="potency_result" class="btn ui-draggable"> <span style="display: flex; justify-content: flex-start ;">POTENCY</span> <p><input type="number" name="attr_potency_result1" value></p> <p><input type="number" name="attr_potency_result2" value></p> <p><input type="number" name="attr_potency_result3" value></p> </button> <button type="roll" name="attr_roll_dmg" class="btn ui-draggable"> <span style="display: flex; justify-content: flex-start ;">POTENCY COND.</span> <p><input type="text" name="attr_potcond1" value></p> <p><input type="text" name="attr_potcond2" value></p> <p><input type="text" name="attr_potcond3" value></p> </button> <button type="roll" name="roll_dmg" class="btn ui-draggable"><span name="attr_description"></span> </button></p> </div> </fieldset> <div class="repcontainer ui-sortable" data-groupname="repeating_attack"></div> <script type="text/worker"> on("change:mig change:agi change:rea change:int change:pre change:mainstat change skillstat sheet:opened", function() { getSectionIDs('repeating_attack',ids => { const getArray = ids.reduce((m,id) => { m.push(`repeating_attack_${id}_powerroll1`,`repeating_attack_${id}_powerroll2`,`repeating_attack_${id}_powerroll3`) },["skillstat", "abilstat", "mainstat", "mig", "agi", "rea", "int", "pre"]); const getAttrs(getArray,attributes => { let mainref = parseInt(values.mainstat) || 0; let skillref = parseInt(values.skillstat) || 0; let abilref = parseInt(values.abilstat) || 0; let mig = parseInt(values.mig) || 0; let agi = parseInt(values.agi) || 0; let rea = parseInt(values.rea) || 0; let int = parseInt(values.int) || 0; let pre = parseInt(values.pre) || 0; let skilldamage1 = (abilstat + powerroll1) let skilldamage2 = (abilstat + powerroll2) let skilldamage3 = (abilstat + powerroll3) }) }); }); </script> </div>
1756818725
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
That looks right 
1756853667
GiGs
Pro
Sheet Author
API Scripter
Shouldn't there be a setAttrs in that last sheet worker since I presume you want to save something to the character sheet. Before I say anything else, Scott, this a great use of reduce: const getArray = ids.reduce((m,id) => { m.push(`repeating_attack_${id}_powerroll1`,`repeating_attack_${id}_powerroll2`,`repeating_attack_${id}_powerroll3`) },['non repeating attributes to get']); I am pretty sure I wouldn't have done that in an example because I avoid using complicated functionsd like reduce in instructional posts, but I like learning new techniques, and I wouldn't have thought of doing it that way. Alexander, in the sheet worker you'll want to change this  change skillstat to change:skillstat Also you have a lot of things like this: <p><input type="number" name="attr_powerrolldmg3" value></p> when you could probably just do <input type="number" name="attr_powerrolldmg3" value> If you need any styling, give them a class. Also, you don't need value on its own like that, so you could do <input type="number" name="attr_powerrolldmg3"> If you want to give them a default value, set it like this <input type="number" name="attr_powerrolldmg3" value="0"> You also have some of these <span style="display: flex; justify-content: flex-start ;">DAMAGE</span> Personally, I would change those styles to classes. A 2-step operation - first create a class name <span class="flex-span">DAMAGE</span> and then in the CSS file, add something with that name, like .flex-style { display: flex; justify-content: flex-start; } then you can change all those spans in one place if you choose to. It'll make your life easier if you get into a habit of creating classes instead of styles more generally.
<script type="text/worker"> on("change:mig change:agi change:rea change:int change:pre change:mainstat change:skillstat sheet:opened", function() { getSectionIDs('repeating_attack',ids => { const getArray = ids.reduce((m,id) => { m.push(`repeating_attack_${id}_powerroll1`,`repeating_attack_${id}_powerroll2`,`repeating_attack_${id}_powerroll3`) },["skillstat", "abilstat", "mainstat", "mig", "agi", "rea", "int", "pre", "powerroll1", "powerroll2", "powerroll3"]); const getAttrs(getArray,attributes => { let mainref = parseInt(values.mainstat) || 0; let skillref = parseInt(values.skillstat) || 0; let abilref = parseInt(values.abilstat) || 0; let mig = parseInt(values.mig) || 0; let agi = parseInt(values.agi) || 0; let rea = parseInt(values.rea) || 0; let int = parseInt(values.int) || 0; let pre = parseInt(values.pre) || 0; let powerroll1 = parseInt(values.powerroll1) || 0; let powerroll2 = parseInt(values.powerroll2) || 0; let powerroll3 = parseInt(values.powerroll3) || 0; let skilldamage1 = (abilref + powerroll1) let skilldamage2 = (abilref + powerroll2) let skilldamage3 = (abilref + powerroll3) const setAttrs(setArray,attributes{ "attr_powerrolldmg1": skilldamage1 "attr_powerrolldmg2": skilldamage2 "attr_powerrolldmg3": skilldamage3 }); }) }); }); </script> I did some fenagling with the script but it is still not displaying the values properly, the fields should display 13, 16, and 17 respectively however I feel Like I'm still doing something wrong with the sheetworkers : Thanks for the CSS tips, I can figure that out after I deal with the HTML since the html is currently the more pressing dilemma. Thanks is greatly appreciated.
Btw, This is what happens when the field's name is set to powerroll1 showing the value as 12. So I know the field is working properly and that the problem is with the script. (Or how it's reading abilstat)
1756909121
GiGs
Pro
Sheet Author
API Scripter
My guess is your problem is here let powerroll1 = parseInt(values.powerroll1) || 0; let powerroll2 = parseInt(values.powerroll2) || 0; let powerroll3 = parseInt(values.powerroll3) || 0; There's no indication of which row of the repeating section you are grabbing those rows from. Repeating sections do present problems. You use the reduce function at the start to build an array that creates all the attribute names, but look here: getSectionIDs('repeating_attack',ids => { const getArray = ids.reduce((m,id) => { m.push(`repeating_attack_${id}_powerroll1`,`repeating_attack_${id}_powerroll2`,`repeating_attack_${id}_powerroll3`) },["skillstat", "abilstat", "mainstat", "mig", "agi", "rea", "int", "pre", "powerroll1", "powerroll2", "powerroll3"]); const getAttrs(getArray,attributes => { console.log(attributes); The log command will show what attributes are actually found. Then again, I may be misunderstanding what you are doing because you have this at the start: <fieldset class="repeating_attack" style="display: none;"> Wont't that display command mean the entire fieldset is not shown (which might not matter, since roll20 doesnt display fieldsets anyway - they get replaced by repcontainers).
1756909318

Edited 1756909476
GiGs
Pro
Sheet Author
API Scripter
Attributes inside a fieldset do not have names like&nbsp; powerroll1 The name will be more like FIELDSET_ROWID_powerroll1 I would look here for more help on repeating sections <a href="https://cybersphere.me/roll20-sheet-author-master-list/#Advanced_Skills" rel="nofollow">https://cybersphere.me/roll20-sheet-author-master-list/#Advanced_Skills</a>
1756913260

Edited 1756913401
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
the problem is in your setAttrs. It looks like you want to set the power roll attributes based on what controlling attributes are. You need to iterate over the ids you got in getSectionIDs and set the attribute for each row. Also, you have a syntaxerror where you are trying to define getAttrs as a constant. Something like this is what you want: // function to do the skill damage calculation so that we can easily calculate based on the non repeating attributes changing or the repeating attributes changing. const calculateSkillDamage = (values, row, rollIndex) =&gt; { // to handle the values of the select, we need to use it to get the appropriate value from the values rather than trying to parseInt it directly. We also need to grab the abilstat ref from the row instead of a non repeating version // Extract the attribute name from the ability stat of the row const abilRef = values[`${row}_abilstat`].replace(/@\{|\}/g,''); const abilScore = abilRef ? parseInt(values[abilRef]) : 0; const powerroll = parseInt(values[`${row}_powerroll${rollIndex}`]) || 0; return abilScore + powerroll; } // the basic attributes we need to get for all the power roll caculations. Note that the powerroll1-3 attributes are removed from these as we need to get these based on the row IDs instead. const baseSkillAttrs = ["skillstat", "mainstat", "mig", "agi", "rea", "int", "pre"]; on("change:mig change:agi change:rea change:int change:pre change:mainstat change:skillstat", function (event) { getSectionIDs('repeating_attack', ids =&gt; { // create the full getarray including the powerroll1-3 attributes for each row. // filter out ids that are not valid ids = ids.filter(id =&gt; id); const getArray = ids.reduce((m, id) =&gt; { m.push(`repeating_attack_${id}_powerroll1`, `repeating_attack_${id}_powerroll2`, `repeating_attack_${id}_powerroll3`, `repeating_attack_${id}_abilstat`); return m; }, [...baseSkillAttrs]); getAttrs(getArray, attributes =&gt; { // Create an empty object to accumulate the changes we want to make const setObj = {}; // iterate over the ids and the powerroll index to calculate the skill damage for each row and powerroll index ids.forEach(id =&gt; { const row = `repeating_attack_${id}`; [1, 2, 3].forEach((n) =&gt; { setObj[`${row}_powerrolldmg${n}`] = calculateSkillDamage(attributes, row, n); }); }); setAttrs(setObj); }); }); }); // react to changes in the selected abilstat for the row on('change:repeating_attack:abilstat',event =&gt; { // extract the rowID of the affected row const [, section, id, attrName] = event.sourceAttribute.match(/(repeating_.+?)_(.+?)_(.+)/) || []; // if something went wrong, bail if (!section) return; // assemble the row name (e.g. repeating_attack_-wers12Sdf). This makes this handling generic so you can use this for any other repeating section that might need this calculation const row = `${section}_${id}`; // create our getarray for this row const getArray = [...baseSkillAttrs, `${row}_powerroll1`, `${row}_powerroll2`, `${row}_powerroll3`, `${row}_abilstat`]; // get our attributes getAttrs(getArray, attributes =&gt; { // empty object to accumulate changes again const setObj = {}; // get the calculation for each powerroll in the row [1,2,3].forEach(rollIndex =&gt; { setObj[`${row}_powerrolldmg${rollIndex}`] = calculateSkillDamage(attributes, row, rollIndex); }) setAttrs(setObj); }) }) // add the powerroll1 - 3 listener as well. Essentially what we're doing in the top listener, but only for one id. on("change:repeating_attack:powerroll1 change:repeating_attack:powerroll2 change:repeating_attack:powerroll3", (event) =&gt; { // extract the rowID of the affected row const [, section, id, attrName] = event.sourceAttribute.match(/(repeating_.+?)_(.+?)_(.+)/) || []; // if something went wrong, bail if (!section) return; // assemble the row name (e.g. repeating_attack_-wers12Sdf). This makes this handling generic so you can use this for any other repeating section that might need this calculation const row = `${section}_${id}`; // get which powerroll specifically changed const rollIndex = attrName.replace(/powerroll/, ''); //e.g. powerroll1 =&gt; 1 // create our getarray for this row const getArray = [...baseSkillAttrs, `${row}_powerroll${rollIndex}`, `${row}_abilstat`]; // get our attributes getAttrs(getArray, attributes =&gt; { // empty object to accumulate changes again const setObj = {}; // get the calculation setObj[`${row}_powerrolldmg${rollIndex}`] = calculateSkillDamage(attributes, row, rollIndex); setAttrs(setObj); }) }) There's also several issues with your html. The fieldset shouldn't have display: none; (Roll20 hides it for us). And we don't add the repcontainer div; Roll20 creates this based on the fieldset. EDIT: Heh, sniped by gigs
1756916651

Edited 1756917441
GiGs said: My guess is your problem is here let powerroll1 = parseInt(values.powerroll1) || 0; let powerroll2 = parseInt(values.powerroll2) || 0; let powerroll3 = parseInt(values.powerroll3) || 0; There's no indication of which row of the repeating section you are grabbing those rows from. Repeating sections do present problems. You use the reduce function at the start to build an array that creates all the attribute names, but look here: getSectionIDs('repeating_attack',ids =&gt; { const getArray = ids.reduce((m,id) =&gt; { m.push(`repeating_attack_${id}_powerroll1`,`repeating_attack_${id}_powerroll2`,`repeating_attack_${id}_powerroll3`) },["skillstat", "abilstat", "mainstat", "mig", "agi", "rea", "int", "pre", "powerroll1", "powerroll2", "powerroll3"]); const getAttrs(getArray,attributes =&gt; { console.log(attributes); The log command will show what attributes are actually found. Then again, I may be misunderstanding what you are doing because you have this at the start: &lt;fieldset class="repeating_attack" style="display: none;"&gt; Wont't that display command mean the entire fieldset is not shown (which might not matter, since roll20 doesnt display fieldsets anyway - they get replaced by repcontainers). The fieldset line was lifted from the Dnd5e sheet itself. From what I understand about that is when the checkbox is unchecked, it will hide where inputs are located in div class: options. Leaving div class: display.&nbsp; The values in div class: options are supposed to be reference values to feed into the div class: display. The mainstat and skillstat values are not repeated values.
For reference everything above the green line is Div class: options. When I was referencing div class: attack, I meant div class: options.
Scott C. said: the problem is in your setAttrs. It looks like you want to set the power roll attributes based on what controlling attributes are. You need to iterate over the ids you got in getSectionIDs and set the attribute for each row. Also, you have a syntaxerror where you are trying to define getAttrs as a constant. Something like this is what you want: // function to do the skill damage calculation so that we can easily calculate based on the non repeating attributes changing or the repeating attributes changing. const calculateSkillDamage = (values, row, rollIndex) =&gt; { // to handle the values of the select, we need to use it to get the appropriate value from the values rather than trying to parseInt it directly. We also need to grab the abilstat ref from the row instead of a non repeating version // Extract the attribute name from the ability stat of the row const abilRef = values[`${row}_abilstat`].replace(/@\{|\}/g,''); const abilScore = abilRef ? parseInt(values[abilRef]) : 0; const powerroll = parseInt(values[`${row}_powerroll${rollIndex}`]) || 0; return abilScore + powerroll; } // the basic attributes we need to get for all the power roll caculations. Note that the powerroll1-3 attributes are removed from these as we need to get these based on the row IDs instead. const baseSkillAttrs = ["skillstat", "mainstat", "mig", "agi", "rea", "int", "pre"]; on("change:mig change:agi change:rea change:int change:pre change:mainstat change:skillstat", function (event) { getSectionIDs('repeating_attack', ids =&gt; { // create the full getarray including the powerroll1-3 attributes for each row. // filter out ids that are not valid ids = ids.filter(id =&gt; id); const getArray = ids.reduce((m, id) =&gt; { m.push(`repeating_attack_${id}_powerroll1`, `repeating_attack_${id}_powerroll2`, `repeating_attack_${id}_powerroll3`, `repeating_attack_${id}_abilstat`); return m; }, [...baseSkillAttrs]); getAttrs(getArray, attributes =&gt; { // Create an empty object to accumulate the changes we want to make const setObj = {}; // iterate over the ids and the powerroll index to calculate the skill damage for each row and powerroll index ids.forEach(id =&gt; { const row = `repeating_attack_${id}`; [1, 2, 3].forEach((n) =&gt; { setObj[`${row}_powerrolldmg${n}`] = calculateSkillDamage(attributes, row, n); }); }); setAttrs(setObj); }); }); }); // react to changes in the selected abilstat for the row on('change:repeating_attack:abilstat',event =&gt; { // extract the rowID of the affected row const [, section, id, attrName] = event.sourceAttribute.match(/(repeating_.+?)_(.+?)_(.+)/) || []; // if something went wrong, bail if (!section) return; // assemble the row name (e.g. repeating_attack_-wers12Sdf). This makes this handling generic so you can use this for any other repeating section that might need this calculation const row = `${section}_${id}`; // create our getarray for this row const getArray = [...baseSkillAttrs, `${row}_powerroll1`, `${row}_powerroll2`, `${row}_powerroll3`, `${row}_abilstat`]; // get our attributes getAttrs(getArray, attributes =&gt; { // empty object to accumulate changes again const setObj = {}; // get the calculation for each powerroll in the row [1,2,3].forEach(rollIndex =&gt; { setObj[`${row}_powerrolldmg${rollIndex}`] = calculateSkillDamage(attributes, row, rollIndex); }) setAttrs(setObj); }) }) // add the powerroll1 - 3 listener as well. Essentially what we're doing in the top listener, but only for one id. on("change:repeating_attack:powerroll1 change:repeating_attack:powerroll2 change:repeating_attack:powerroll3", (event) =&gt; { // extract the rowID of the affected row const [, section, id, attrName] = event.sourceAttribute.match(/(repeating_.+?)_(.+?)_(.+)/) || []; // if something went wrong, bail if (!section) return; // assemble the row name (e.g. repeating_attack_-wers12Sdf). This makes this handling generic so you can use this for any other repeating section that might need this calculation const row = `${section}_${id}`; // get which powerroll specifically changed const rollIndex = attrName.replace(/powerroll/, ''); //e.g. powerroll1 =&gt; 1 // create our getarray for this row const getArray = [...baseSkillAttrs, `${row}_powerroll${rollIndex}`, `${row}_abilstat`]; // get our attributes getAttrs(getArray, attributes =&gt; { // empty object to accumulate changes again const setObj = {}; // get the calculation setObj[`${row}_powerrolldmg${rollIndex}`] = calculateSkillDamage(attributes, row, rollIndex); setAttrs(setObj); }) }) There's also several issues with your html. The fieldset shouldn't have display: none; (Roll20 hides it for us). And we don't add the repcontainer div; Roll20 creates this based on the fieldset. EDIT: Heh, sniped by gigs I'll try this when I get home to see if it works out.
1756920164
GiGs
Pro
Sheet Author
API Scripter
Alexander G. said: The fieldset line was lifted from the Dnd5e sheet itself.&nbsp; I would caution against taking things from the D&amp;D5e sheet. Primariiy that one sheet. The roll20 staff in the past have used techniques with this sheet that aren't available to us, and it might be using Beacon code which is a new way of creating sheets that you won't be using and which&nbsp; the existing documentation does not apply to.
1756922644

Edited 1756922835
GiGs
Pro
Sheet Author
API Scripter
If I'm following you correctly, I think your scriptblock should be more like this const removeRoll20String = score =&gt; score . replace ( '&amp;{' , '' ). replace ( '}' , '' ); on ( "change:mig change:agi change:rea change:int change:pre change:repeating_attack sheet:opened" , function () { &nbsp; &nbsp; getSectionIDs ( 'repeating_attack' , ids =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; const getArray = ids . reduce (( m , id ) =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m . push ( `repeating_attack_ ${ id } _powerroll1` , `repeating_attack_ ${ id } _powerroll2` , `repeating_attack_ ${ id } _powerroll3` , &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `repeating_attack_ ${ id } _abilstat` ) &nbsp; &nbsp; &nbsp; &nbsp; },[ "mig" , "agi" , "rea" , "int" , "pre" ]); &nbsp; &nbsp; &nbsp; &nbsp; getAttrs ( getArray ,values =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let output = {}; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ids . forEach ( id =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let abilref = values [ `repeating_attack_ ${ id } _abilstat` ]; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // this gets a value like @{mig} and @{agi} &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // need to turn it into the appropriate numeric value, by removing the @{ and } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let abilvalue = values [ removeRoll20String ( abilref )] || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let powerroll1 = parseInt ( values [ `repeating_attack_ ${ id } _powerroll1` ]) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let powerroll2 = parseInt ( values [ `repeating_attack_ ${ id } _powerroll2` ]) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let powerroll3 = parseInt ( values [ `repeating_attack_ ${ id } _powerroll3` ]) || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let skilldamage1 = ( abilvalue + powerroll1 ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let skilldamage2 = ( abilvalue + powerroll2 ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let skilldamage3 = ( abilvalue + powerroll3 ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; output [ `repeating_attack_ ${ id } _powerrolldmg1` ] = skilldamage1 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; output [ `repeating_attack_ ${ id } _powerrolldmg2` ] = skilldamage2 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; output [ `repeating_attack_ ${ id } _powerrolldmg3` ] = skilldamage3 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setAttrs ( output ); &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; }); }); Notice how the getAttrs and setAttrs statements do not have a const declaration before them. This is based on your abilref attribute also being inside the repeating section, but the main attributes are bot. Notice you have to build each rows output separately, then you can output them in one go.
1756924192
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
GiGs said: Alexander G. said: The fieldset line was lifted from the Dnd5e sheet itself.&nbsp; I would caution against taking things from the D&amp;D5e sheet. Primariiy that one sheet. The roll20 staff in the past have used techniques with this sheet that aren't available to us, and it might be using Beacon code which is a new way of creating sheets that you won't be using and which&nbsp; the existing documentation does not apply to. The 2014 D&amp;D sheet isn't Beacon and everything it uses is functionality that is broadly available. Some of it, like the charactermancer, isn't really useful in most situations though. I will second GiGs general warning about taking code from the browser though. Roll20 manipulates the code that you upload to the game; doing things like hiding the fieldsets and creating the corresponding repcontainers. These are things that if you do yourself are at best pointless, and at worst might actually cause a problem with the functionality of your sheet.
1756924801
GiGs
Pro
Sheet Author
API Scripter
Scott C. said: I will second GiGs general warning about taking code from the browser though.&nbsp; That's a really good addition.
1756925226
GiGs
Pro
Sheet Author
API Scripter
Here's a tweak to that code inspired by Scott's earlier work const removeRoll20String = score =&gt; score . replace ( '&amp;{' , '' ). replace ( '}' , '' ); on ( "change:mig change:agi change:rea change:int change:pre change:repeating_attack sheet:opened" , function () { &nbsp; &nbsp; getSectionIDs ( 'repeating_attack' , ids =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; const getArray = ids . reduce (( m , id ) =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m . push ( `repeating_attack_ ${ id } _powerroll1` , `repeating_attack_ ${ id } _powerroll2` , `repeating_attack_ ${ id } _powerroll3` , &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; `repeating_attack_ ${ id } _abilstat` ) &nbsp; &nbsp; &nbsp; &nbsp; },[ "mig" , "agi" , "rea" , "int" , "pre" ]); &nbsp; &nbsp; &nbsp; &nbsp; getAttrs ( getArray , values =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let output = {}; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ids . forEach ( id =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let abilref = values [ `repeating_attack_ ${ id } _abilstat` ]; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // this gets a value like @{mig} and @{agi} &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // need to turn it into the appropriate numeric value, by removing the @{ and } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; let abilvalue = values [ removeRoll20String ( abilref )] || 0 ; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [ 1 , 2 , 3 ]. forEach ( index =&gt; { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; output [ `repeating_attack_ ${ id } _powerrolldmg ${ index } ` ] = abilvalue &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; + ( parseInt ( values [ `repeating_attack_ ${ id } _powerroll ${ index } ` ]) || 0 ); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setAttrs ( output ); &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; }); });
Scott C. said: GiGs said: Alexander G. said: The fieldset line was lifted from the Dnd5e sheet itself.&nbsp; I would caution against taking things from the D&amp;D5e sheet. Primariiy that one sheet. The roll20 staff in the past have used techniques with this sheet that aren't available to us, and it might be using Beacon code which is a new way of creating sheets that you won't be using and which&nbsp; the existing documentation does not apply to. The 2014 D&amp;D sheet isn't Beacon and everything it uses is functionality that is broadly available. Some of it, like the charactermancer, isn't really useful in most situations though. I will second GiGs general warning about taking code from the browser though. Roll20 manipulates the code that you upload to the game; doing things like hiding the fieldsets and creating the corresponding repcontainers. These are things that if you do yourself are at best pointless, and at worst might actually cause a problem with the functionality of your sheet. I understand that. I just lifted the code specifically to understand how repeated divs are added with certain info visible rather than others to create a settings section that goes with the displayed info.
1756994737

Edited 1756994922
GiGs said: Here's a tweak to that code inspired by Scott's earlier work So I tried inputing the code in as so. Just so I can understand I used the first instance so I can follow with what is going on: &lt;div class="sheet-abils sheet-block"&gt; &lt;h3&gt;Abilities&lt;/h3&gt; &lt;fieldset class="repeating_attack" style="display: none;"&gt; &lt;div class="attack"&gt; &lt;input class="options-flag" type="checkbox" name="attr_options-flag" checked="checked"&gt;&lt;span&gt;y&lt;/span&gt; &lt;div class="options"&gt; &lt;div class="option-primary"&gt;&lt;span&gt;&lt;/span&gt; &lt;p&gt;Ability Name: &lt;input type="text" name="attr_atkname"&gt;&lt;/p&gt; &lt;p&gt;Characteristic: &lt;select name="attr_abilstat" id="attr_abilstat"&gt; &lt;option value="@{mig}" selected="selected" &gt;MIGHT&lt;/option&gt; &lt;option value="@{agi}"&gt;AGILITY&lt;/option&gt; &lt;option value="@{rea}"&gt;REASON&lt;/option&gt; &lt;option value="@{int}"&gt;INTUITION&lt;/option&gt; &lt;option value="@{pre}"&gt;PRESENCE&lt;/option&gt; &lt;/select&gt; Properties: &lt;input type="text" name="attr_property"&gt;&lt;/p&gt;&lt;/p&gt; &lt;p&gt;&amp;le;11: &lt;input type="text" name="attr_powerroll1"&gt; Base Condition &lt;input type="text" name="attr_basecond1"&gt; Potency: &lt;select type="number" name="attr_potselect1" id="attr_potselect1"&gt; &lt;option value="none" selected="selected" &gt;-&lt;/option&gt; &lt;option value="-2"&gt;WEAK&lt;/option&gt; &lt;option value="-1"&gt;AVERAGE&lt;/option&gt; &lt;option value="0"&gt;STRONG&lt;/option&gt; &lt;/select&gt; Potency Condition &lt;input type="text" name="attr_potcond1"&gt;&lt;/p&gt; &lt;p&gt;12-16: &lt;input type="text" name="attr_powerroll2"&gt; Base Condition &lt;input type="text" name="attr_basecond2"&gt; Potency: &lt;select type="number" name="attr_potselect2" id="attr_potselect2"&gt; &lt;option value="none" selected="selected" &gt;-&lt;/option&gt; &lt;option value="-2"&gt;WEAK&lt;/option&gt; &lt;option value="-1"&gt;AVERAGE&lt;/option&gt; &lt;option value="0"&gt;STRONG&lt;/option&gt; &lt;/select&gt; Potency Condition &lt;input type="text" name="attr_potcond2"&gt;&lt;/p&gt; &lt;p&gt;17+: &lt;input type="text" name="attr_powerroll3"&gt; Base Condition &lt;input type="text" name="attr_basecond3"&gt; Potency: &lt;select type="number" name="attr_potselect3" id="attr_potselect3"&gt; &lt;option value="none" selected="selected" &gt;-&lt;/option&gt; &lt;option value="-2"&gt;WEAK&lt;/option&gt; &lt;option value="-1"&gt;AVERAGE&lt;/option&gt; &lt;option value="0"&gt;STRONG&lt;/option&gt; &lt;/select&gt; Potency Condition &lt;input type="text" name="attr_potcond3"&gt;&lt;/p&gt; &lt;p&gt; Description: &lt;input type="text" name="attr_description"&gt;&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class="display"&gt; &lt;p&gt;&lt;button type="roll" name="roll_attack" class="btn ui-draggable abil_name"&gt;&lt;span name="attr_atkname"&gt;&lt;/span&gt; &lt;input type="text" name="attr_property" value&gt; &lt;/button&gt;&lt;/p&gt; &lt;p&gt;&lt;button type="roll" name="attr_roll_dmg" class="btn ui-draggable"&gt; &lt;span style="display: flex; justify-content: flex-start ;"&gt;DAMAGE&lt;/span&gt; &lt;p&gt;&lt;input type="number" name="attr_powerrolldmg1" value&gt;&lt;/p&gt; &lt;p&gt;&lt;input type="number" name="attr_powerrolldmg2" value&gt;&lt;/p&gt; &lt;p&gt;&lt;input type="number" name="attr_powerrolldmg3" value&gt;&lt;/p&gt; &lt;/button&gt; &lt;button type="roll" name="attr_roll_dmg" class="btn ui-draggable"&gt; &lt;span style="display: flex; justify-content: flex-start ;"&gt;BASE COND.&lt;/span&gt; &lt;p&gt;&lt;input type="text" name="attr_basecond1" value&gt;&lt;/p&gt; &lt;p&gt;&lt;input type="text" name="attr_basecond2" value&gt;&lt;/p&gt; &lt;p&gt;&lt;input type="text" name="attr_basecond3" value&gt;&lt;/p&gt; &lt;/button&gt; &lt;button type="roll" name="potency_result" class="btn ui-draggable"&gt; &lt;span style="display: flex; justify-content: flex-start ;"&gt;POTENCY&lt;/span&gt; &lt;p&gt;&lt;input type="number" name="attr_potency_result1" value&gt;&lt;/p&gt; &lt;p&gt;&lt;input type="number" name="attr_potency_result2" value&gt;&lt;/p&gt; &lt;p&gt;&lt;input type="number" name="attr_potency_result3" value&gt;&lt;/p&gt; &lt;/button&gt; &lt;button type="roll" name="attr_roll_dmg" class="btn ui-draggable"&gt; &lt;span style="display: flex; justify-content: flex-start ;"&gt;POTENCY COND.&lt;/span&gt; &lt;p&gt;&lt;input type="text" name="attr_potcond1" value&gt;&lt;/p&gt; &lt;p&gt;&lt;input type="text" name="attr_potcond2" value&gt;&lt;/p&gt; &lt;p&gt;&lt;input type="text" name="attr_potcond3" value&gt;&lt;/p&gt; &lt;/button&gt; &lt;button type="roll" name="roll_dmg" class="btn ui-draggable"&gt;&lt;span name="attr_description"&gt;&lt;/span&gt; &lt;/button&gt;&lt;/p&gt; &lt;/div&gt; &lt;script type="text/worker"&gt; const removeRoll20String = score =&gt; score.replace('&amp;{', '').replace('}',''); on("change:mig change:agi change:rea change:int change:pre change:repeating_attack sheet:opened", function() { getSectionIDs('repeating_attack',ids =&gt; { const getArray = ids.reduce((m,id) =&gt; { m.push(`repeating_attack_${id}_powerroll1`,`repeating_attack_${id}_powerroll2`,`repeating_attack_${id}_powerroll3`, `repeating_attack_${id}_abilstat`) },["mig", "agi", "rea", "int", "pre"]); getAttrs(getArray,values =&gt; { let output = {}; ids.forEach(id =&gt; { let abilref = values[`repeating_attack_${id}_abilstat`]; // this gets a value like @{mig} and @{agi} // need to turn it into the appropriate numeric value, by removing the @{ and } let abilvalue = values[removeRoll20String(abilref)] || 0; [1, 2, 3].forEach(index =&gt; { output[`repeating_attack_${id}_powerrolldmg${index}`] = abilvalue + (parseInt(values[`repeating_attack_${id}_powerroll${index}`]) || 0); }); }); setAttrs(output); }); }); }); &lt;/script&gt; &lt;/fieldset&gt; &lt;div class="repcontainer ui-sortable" data-groupname="repeating_attack"&gt;&lt;/div&gt; &lt;/div&gt; I removed the parts that are Fieldsets and I figured out what they controlled Outlined in blue. However, the values aren't adding together. Do I need another part of the script to get the ID's?
Would I need to do something to generate a new rowID when an attack is added?
1756997223
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
If you removed the fieldset, then the repeating section will not exist and nothing will happen. If you're using the last code that I posted, it should work out of the box, I tested it in a game before posting.
Alexander G. said: Scott C. said: the problem is in your setAttrs. It looks like you want to set the power roll attributes based on what controlling attributes are. You need to iterate over the ids you got in getSectionIDs and set the attribute for each row. Also, you have a syntaxerror where you are trying to define getAttrs as a constant. Something like this is what you want: There's also several issues with your html. The fieldset shouldn't have display: none; (Roll20 hides it for us). And we don't add the repcontainer div; Roll20 creates this based on the fieldset. EDIT: Heh, sniped by gigs Oh, I guess I'm just a silly boy. You are correct. Thank you bunches
Scott C. said: If you removed the fieldset, then the repeating section will not exist and nothing will happen. If you're using the last code that I posted, it should work out of the box, I tested it in a game before posting. So If I'm reading the code right; If I wanted to parse the value of Potency, I would have my starting point as to something like this? The value of the Potency Would be the mainstat from the sheet plus the value of the potency dropdown. I'm going to try and code it myself but critique would be super helpful. const calculatePotency = (values, row, rollIndex) =&gt; { const mainRef = values[`mainstat`].replace(/@\{|\}/g,''); const potRef = parseInt(values[`${row}_potselect${rollIndex}`]) || 0; const potScore = potRef ? parseInt(values[potRef]) : 0; return mainRef + potScore; }
So, I have done the following parts to deal with the Potency calculation based on Scott's work. I know It is wrong as once again my lack of knowledge evades me. Am I at least close to getting it right? lol :) &lt;script type="text/worker"&gt; const calculatePotency = (values, row, rollIndex) =&gt; { const mainRef = values[`mainstat`].replace(/@\{|\}/g,''); const potRef = parseInt(values[`${row}_potselect${rollIndex}`]) || 0; const potScore = potRef ? parseInt(values[potRef]) : 0; return mainRef + potScore; } on("change:mig change:agi change:rea change:int change:pre change:mainstat change:skillstat", function (event) { getSectionIDs('repeating_attack', ids =&gt; { ids = ids.filter(id =&gt; id); const getArray = ids.reduce((m, id) =&gt; { m.push(`repeating_attack_${id}_potselect1`, `repeating_attack_${id}_potselect2`, `repeating_attack_${id}_potselect3`); return m; }, [...baseSkillAttrs]); getAttrs(getArray, attributes =&gt; { const setObj = {}; ids.forEach(id =&gt; { const row = `repeating_attack_${id}`; [1, 2, 3].forEach((n) =&gt; { setObj[`${row}_potency${n}`] = calculatePotency(attributes, row, n); }); }); setAttrs(setObj); }); }); }); on('change:mainstat',event =&gt; { const [, section, id, attrName] = event.sourceAttribute.match(/(repeating_.+?)_(.+?)_(.+)/) || []; if (!section) return; const row = `${section}_${id}`; const getArray = [...baseSkillAttrs, `${row}_potselect1`, `${row}_potselect2`, `${row}_potselect3`]; getAttrs(getArray, attributes =&gt; { const setObj = {}; [1,2,3].forEach(rollIndex =&gt; { setObj[`${row}_potency${rollIndex}`] = calculatePotency(attributes, row, rollIndex); }) setAttrs(setObj); }) }) on("change:repeating_attack:potselect1 change:repeating_attack:potselect2 change:repeating_attack:potselect3", (event) =&gt; { const [, section, id, attrName] = event.sourceAttribute.match(/(repeating_.+?)_(.+?)_(.+)/) || []; if (!section) return; const row = `${section}_${id}`; const rollIndex = attrName.replace(/potselect/, ''); //e.g. powerroll1 =&gt; 1 const getArray = [...baseSkillAttrs, `${row}_potselect${rollIndex}`]; getAttrs(getArray, attributes =&gt; { const setObj = {}; setObj[`${row}_potency${rollIndex}`] = calculatePotency(attributes, row, rollIndex); setAttrs(setObj); }) }) &lt;/script&gt;
1757019604

Edited 1757019639
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
That looks right in general, however there are a few things. This needs to be in the same script tag as the code from before. This is because it uses many of the same variables that are defined in that script tag (e.g. baseSkillAttrs). Generally, all sheetworkers should be in the same script tag of the sheet, usually placed at the very end of the html code. For reacting to the mig, agi, etc attributes, I'd put&nbsp;setObj[`${row}_potency${n}`] = calculatePotency(attributes, row, n); in the same iteration over row IDs that you do for the powerdmg attributes. Just get the potselect attributes in that same array assembly that is used for the powerdmg attributes. This way you only react to the change once and only iterate over the IDs once. This will make your code more performant and it should be more maintainable as you won't have essentially the same code in multiple places.
So If I want to add more variables, I still use the same sections that you previously laid out? For example: const getArray = [...baseSkillAttrs, `${row}_powerroll1`, `${row}_powerroll2`, `${row}_powerroll3`, `${row}_abilstat`]; //Would Be vvvvv const getArray = [...baseSkillAttrs, `${row}_powerroll1`, `${row}_powerroll2`, `${row}_powerroll3`, `${row}_abilstat`, `${row}_potselect1`, `${row}_potselect2`, `${row}_potselect3`];
1757030633

Edited 1757030701
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
For something like the listener to the repeating row attributes, e.g.: on("change:repeating_attack:potselect1 change:repeating_attack:potselect2 change:repeating_attack:potselect3", (event) =&gt; { That will work because when it runs it will know what row it is working with. For the listeners that are responding to non repeating events, but affecting repeating rows, like: on("change:mig change:agi change:rea change:int change:pre change:mainstat change:skillstat", function (event) { You will edit parts of the .reduce call const getArray = ids.reduce((m, id) =&gt; { &nbsp;&nbsp;&nbsp;&nbsp;// For any repeating attributes from this same section, you'll add them to the list of attributes that are being pushed to the array m.push(`repeating_attack_${id}_potselect1`, `repeating_attack_${id}_potselect2`, `repeating_attack_${id}_potselect3`); return m; }, [...baseSkillAttrs]); //For any non repeating attribute, you'll add it to this starting array that is described at the end of the reduce. And then, there's an error in the listener for mainstat. It's a non repeating attribute and so needs to be treated like the listener for mig, agi, etc. From the code, you have, you already have this listener included in the mig, agi, etc listener, so this separate listener isn't needed.