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

Calculating Repeating Rows into new entry.

Simple question. I have Inventory items on my character sheet as a repeating attribute so you can add as many as you want or need. My question is, is there a way to have the values of the variety entries calculated into a new attribute? For example, the Encumbrance Value (EV) of each item funneled into an Encumbrance Attribute. This is how my repeating attribute lines look.     <fieldset name="attr_repeatinginventory" class="repeating_inventory">              <Table> <td><input type="text" name="attr_Inventory" width="150" STYLE="width: 150px" size="0" class="medium" value="" /></td> <td><input type="text" name="attr_itemcost" class="short alcenter" value="" /></td> <td><input type="number" name="attr_itemE.V." class="short alcenter" value=""/></td>     </table>     </fieldset><p></p>
1635775773

Edited 1635799162
GiGs
Pro
Sheet Author
API Scripter
There's more than one way to do it. I'm quite partial to the repeatingSum script (since I wrote it), but you can achieve what you want with a fairly simple sheet worker. First thing to note, you have an error on this line: <fieldset name="attr_repeatinginventory" class="repeating_inventory"> It should be <fieldset class="repeating_inventory"> Fieldsets don't have a name. Their class is their name. I dont know if that error wiuld cause any issues, it's probably just redundant. Then I'd recomment removing the periods from this line: <input type="number" name="attr_itemE.V." class="short alcenter" value=""/> change it to <input type="number" name="attr_itemEV" class="short alcenter" value=""/> There's a lot of ways having periods in there can mess things up (both at the sheet code side, and the user side making macros), it's just better to avoid them in attribute names. Assuming the attribute you want to save the value in is called Encumbrance, a sheet worker might look like this: on ( 'change:repeating_inventory:itemev remove:repeating_inventory ' , () => {     getSectionIDs ( 'repeating_inventory' , ids => {         // build an array of all the row ids, and use that to construct each field name         const fields = [];         ids . forEach ( id => fields . push ( `repeating_inventory_ ${ ids } _itemEV` ));         // get the value of each itemEV         getAttrs ( fields , v => {             // initialise the value we will hold the final sum.             let sumEVs = 0 ;             // loop throw the rows again, and add the value to the sum             ids . forEach ( id => {                 sumEVs += ( parseInt ( v [ `repeating_inventory_ ${ id } _itemEV` ]) || 0 );             });             // finally save the sum             setAttrs ({                 Encumbrance : sumEVs             });         });     }); }); There's a lot of ways you can improve this code (e.g. using the reduce function in place of the two forEachs, using Object.values to get the EVs values), but they'd make the code a lot less readable. Alternatively you could insert the code from this page , and then your entire sheet worker would be on ( 'change:repeating_inventory:itemev remove:repeating_inventory' , function () {     repeatingSum ( "Encumbrance" , "inventory" , "itemev" ); }); As described on the linked page, you just need to supply the attribute name where you want to save the total, the repeating section name with repeating_ removed, and the field you want to sum up within that section.
Thank you for the information, but I can't get it to work. I have tried both options and attribute does not update. I am 100% positive I am misunderstanding something.
1635865367
GiGs
Pro
Sheet Author
API Scripter
Can you post your full sheet code in pastebin or gist.github.com?
Before we get to that mess (it is a huge sheet) I will just tell  you want happens. The first item works fine. I add roll, insert a value for the EV and it is mimicked in the Sum attribute. Any subsequent row/item added when I give the new item an EV reduces the Sum attribute to 0.
Got it. Once I realized I needed it all in the same sheet worker. Thanks!
1635872516

Edited 1635872680
GiGs
Pro
Sheet Author
API Scripter
Can you copy out the entire fieldset, and the script bits you've added to do this in a pastebin. Without know what code you're using, its difficult to replicate. Also whats the sum attribute named? Great! I dont understand what you mean by "all in the same sheet worker" but if it's working, it's working.
<script> Everything goes here ! </script>
1635874080

Edited 1635874148
GiGs
Pro
Sheet Author
API Scripter
ahhh yes, all workers and scripts go in the same script block. yes.
Follow up question. Two actually. I want one of the repeating attributes to be a calculated result, essentially the ItemEV X 1, where the 1 is a checkbox. The checkbox indicates if the item is in a backpack or quiver, etc. And therefore that weight isn't counted against the character (as per the capacity rules in Castles and Crusades). When do this with a disabled="true" the values just look at me blankly. So is there a way to do a sheet worker that sets one repeating attribute based on another repeating attribute (or two)? Secondly, capacity items in Castles and Crusades have a limit to the number of items they can hold. Is there a way to limit the max number of repeating rows created? And have that max based on an attribute not in the repeating section?
1636135121

Edited 1636135191
GiGs
Pro
Sheet Author
API Scripter
For your first question, sheet workers cannot work with autocalc values. The way to avoid this is to replace that autocacl with another sheet worker, and change disable="true" to readonly (that stops players from editing its value, but workers can change it). For your second question, there is a way but its tricky. Here are the steps: Basically you have to have a change event based on any value in the repeating section, and the max attribute counts the number of rows in the repeating section (you can do this with the getSectionIDs array length), Then delete every row above the length you want (get their idsfrom the above array, and use removeRepeatingRow( RowID ) . So it's not as easy as it could be, it's a bit clunky. Basically you let players create the extra row, and then delete them. As an alternative, you could use CSS to hide the Add button when getSectionIDs array length is above maximum. Basically steps 1 & 2 are the same as above, but then your sheetworker changes the value of a hidden attribute between 0 and 1, based on whether rows equal or exceed the max, and a CSS rule to hide the Add button based on the class of that value. You might also want to do the above step, so if the Max Rows stat value decreases, the bottom rows of the repeating section are deleted. (This applies to the above process too - max can decrease there too after all.) But that can be tricky - players might have reordered the list, and you then need to look into the function that returnsd ids based on current order. (It might be on the same page linked above). So whatever you do, it's pretty clunky but doable. It would be easier just to show the max number of items, and have a hidden message popup saying, "You are over the max items, delete X number of items" and let players handle it. This also gives players control over what items they keep when max drops.
So how do you do a sheetworker to change the value of a repeating attribute? Because it isn't this: <script type="text/worker"> on("change:inbaga change:itemeva", function() {          getAttrs(["inbaga","itemEVa"], function(values) {     let itemeva = parseInt(values.itemEVa)||0; ;     setAttrs({inbaga : itemeva});  }); }); </script>
1636393982

Edited 1636394109
GiGs
Pro
Sheet Author
API Scripter
If you want to change a repeating section attribute, you need to include the repeating section name. First, you should only have one script block in your entire html plage. So this: <script type="text/worker"> </script> Should be ther only once. All your sheet workers go inside that. That's why when people post worker solutions, they dont include the script lines - because they are separate from the workers, and are only written once. They create a separate Javascript "page" within the html page. Your sheet worker would then look something like on("change:repeating_inventory:itemeva", function() {          getAttrs(["repeating_inventory_itemEVa"], function(values) {     let itemeva = parseInt(values.repeating_inventory.itemEVa)||0;     setAttrs({ repeating_inventory_inbaga:itemeva });  }); }); Notice the different syntax on the change line to everywhere else - once again that is a bit different. Iuse a colon there and an underscore everywhere else. You also don't need to break the setAttrrs line across separate lines like I did there, it's just a common custom (and is easier when you start having multiple attributes in setAttrs). Note that you dont need to grab the inbaga attribute value in getAttrs, because it isnt being used in the worker - its just being set (overwritten). You also definitely dont want it on the change line, because that triggers too many types: itemEva changes, so the worker fires, which finishes by setting inbaga. That causes inbaga to change, which is detected by the change:inbaga line, so that triggers the worker to fire again unneccessarily. Finally, if inbaga is not in the repeating section, you need more complex code. Is that the case? PS: thank you for including the code you tried.
OK, so this is where I am at and I think it is a problem of just not knowing how to write the names of the attributes properly. on("change:repeating_inventory:inbaga change:repeating_inventory:itemeva", function() {          getAttrs(["repeating_inventory_inbaga","repeating_inventory_itemEVa"], function(values) {     let itemeva = parseInt(values.repeating_inventory.itemEVa)||0;     let inbaga = parseInt(values.repeating_inventory.inbaga)||0;     let repeating_inventory:bageva = itemeva - itemeva * inbaga     setAttrs({       repeating_inventory:bageva     });    }); });
1636426744

Edited 1636426784
I use this on the non repeating variation (the first item in the inventory) and it works just fine. When you check inbag0 the bagev0 becomes "0", otherwise it is equal to itemEV0.  The "a" variants (itemEVa, inbaga, bageva) are the attributes in the repeating sections. on("change:inbag0 change:bagev0 change:itemev0", function() {          getAttrs(["itemEV0","inbag0"], function(values) {     let inbag0 = parseInt(values.inbag0)||0;     let itemev0 = parseInt(values.itemEV0)||0;     let bagev0 = itemev0 - itemev0 * inbag0 setAttrs({                                 bagev0 }); }); });
1636428213
GiGs
Pro
Sheet Author
API Scripter
Sorry, I see I made a typo earlier. I used a period where I should have used an underscore. This: let itemeva = parseInt(values.repeating_inventory.itemEVa)||0; should have been let itemeva = parseInt(values.repeating_inventory_itemEVa)||0; the same as in the getAttrs function. So your worker would be on("change:repeating_inventory:inbaga change:repeating_inventory:itemeva", function() {          getAttrs(["repeating_inventory_inbaga","repeating_inventory_itemEVa"], function(values) {     let itemeva = parseInt(values.repeating_inventory_itemEVa)||0;     let inbaga = parseInt(values.repeating_inventory_inbaga)||0;     let calculation = itemeva - itemeva * inbaga     setAttrs({       repeating_inventory_bageva:calculation     });    }); }); You do have a syntax error here     let repeating_inventory:bageva = itemeva - itemeva * inbaga     setAttrs({       repeating_inventory:bageva     });  On that first line you are creating a variable, and variable names cannot include a colon. The way I did is just one way. You could also do it:     let repeating_inventory_bageva = itemeva - itemeva * inbaga     setAttrs({       repeating_inventory_bageva:repeating_inventory_bageva     });  In setAttrs, you have two terms: attribute : variable The left side of the colon is the name of the attribute on the character sheet; the right side of the colon is the variable used within the worker. They are different, but can be named the same. Now, when they are named the same, you can omit the colon and the right term, so you can do this (in theory):     let repeating_inventory_bageva = itemeva - itemeva * inbaga     setAttrs({       repeating_inventory_bageva     });  I've never done this for a repeating section attribute, so I don't know if it's work there. In principle it should, but there's only one way to find out!
Thank you so much GiGs. I am learning (or copying and pasting at least) a lot.  I finally got it to work! Yay! Now, sheet worker to limit the number of repeating attributes based on an attribute outside of the fieldset. :) LOL
1636477349

Edited 1636477364
GiGs
Pro
Sheet Author
API Scripter
First, let me say I don't recomment that you do that automatically. It's very easy for a player to accidentally delete their entire inventory list. But if you want to do it, something like this would work: on ( 'change:repeating_inventory change:outer' , () => {     getAttrs ([ 'outer' ], v => {         const limit = parseInt ( v . outer ) || 0 ; // recommand a higher number than zero, you could accidentally delete the entire section         getSectionIDs ( 'repeating_inventory' , idarray => {             const rows = idarray . length ;             if ( rows > limit ) {                 idarray . forEach (( id , index ) => {                     if ( index > limit ) removeRepeatingRow ( `repeating_inventory_ ${ id } ` );                 });             }         });     }); }); A couple of things to note: This worker fires on changes of the repeating section, or an attribute called outer . You'll need to replace all copies of outer with your attribute name. Also, i honestly dont know if the change:repeating_inventory bit will work - you might need to supply an attribute within the section, like change:repeating_inventory :test If you do, choose an attribute that is guaranteed to be created. I would create a hidden attribute in the repeating section with a default value - whenever a row is created, you know that is given a value so will always trigger the above worker. Then, this line const limit = parseInt(v.outer) || 0; // recommand a higher number than zero, you could accidentally delete the entire section I would recommend changing the || 0 to || 10 or || 99 or some number higher than anyone will actually have. The || 0 is setting a default value in case the outer attribute isnt a number - but if its set to 0, the rest of the code will completely erase the entire repeating section. You dont want to that happen. Ideally you want the outer attribute to be itself calculated by a sheet worker, and be readonly, so players cant accidentally set its value too low and delete repeating section rows that they didnt intend to.
1636481016

Edited 1636481078
GiGs
Pro
Sheet Author
API Scripter
Oh, I whipped up that code quickly and forgot it wont respect player sorting. If people have changed the order of their repeating section (the way you can drag rows up and down), it won't necessarily delete the bottom ones. There's a custom function on the wiki for that, but I can't find it right now. But bear in mind that players will want to choose what they drop - those items do't just vanish into thin air. Whereas if you delete them from teh character sheet, they might as well, becayuse players might forget what was there. So either you make the function more complicated (add the sorting function, fdisplay a list of what was dropped0, or you just display a number that shows the naximum, and let players handle it (maybe report when they are carrying too much). Remember just because you can automate something, it doesn't mean you should . Sometimes there are downsides (especially when the coding is clunky as in this case).
OK. It worked but I didn't like it. I think I would prefer the hidden option that reveals a number of entries equal to the capacity of the item.
1637626793
GiGs
Pro
Sheet Author
API Scripter
So it doesnt delete rows, just hides numbers past a certain amount? That can be done with a mostly CSS solution. You'd probably use a sheet worker to change a hidden value for a row to 0 if it exceeds the value, and 1 otherwise. I'd recommend having a button to "show all" which toggles all hidden values to 1, or the calculated value, so that players can see what has been hidden. Also, if you are using something which totals the value of the section, you might want to make sure it only totals the visible values (multiplying by the hidden value would work). Remember that if you need to respect player order (their moving rows around), you'll need a separate function.