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

RepeatingSum, but with NonRepeating Stuff Added

1550293992

Edited 1550403793
SOLVED!&nbsp; (Thanks to the amazing GiGs) See:&nbsp; <a href="https://app.roll20.net/forum/permalink/7216689/" rel="nofollow">https://app.roll20.net/forum/permalink/7216689/</a> Sorry to be of bother to anyone, but as a complete newb to sheet worker scripts I am trying to help a friend of mine with simplifying the encumberance calculations for his Mutant Year: Zero session by having them automatically calculate. One part of the Gear is in a Repeating format, so I used&nbsp; "RepeatingSum" . However, I need to include a few values to this calculation that are not part of the repeating section... namely the first row of the inventory is just a simple row with regular attributes (gear-name, gear-encumberance, gear-amount, gear-carried). Then I am trying to include the food and water supply (even though I suppose that's not strictly necessary) via the attributes "grub" and "water" which each count as 0.25 units of weight. I studied the code for a few hours and went to modifying it, but I am very much of a beginner at this and it's rather advanced, so I probably just broke something. &nbsp; So naturally, when I reload and try it out, the encuberance value isn't updating. So I assume I must have commited some excruciating programming sin in so crudely modifying GiGs example script, so I'm sorry for that! x.x I already thank anyone attempting to help, although I am sorry if I'm wasting your time with this! Here are the atrocities I have commited on RepeatingSum to have it include a couple of nonrepeating attributes: const repeatingSum = (destination, section, fields, multiplier = 1) =&gt; { if (!Array.isArray(fields)) fields = [fields]; getSectionIDs(`repeating_${section}`, idArray =&gt; { const attrArray = idArray.reduce( (m,id) =&gt; [...m, ...(fields.map(field =&gt; `repeating_${section}_${id}_${field}`))],[]); getAttrs(attrArray, v =&gt; { console.log("===== values of v: "+ JSON.stringify(v) +" ====="); const getValue = (section, id,field) =&gt; parseFloat(v[`repeating_${section}_${id}_${field}`], 10)||1; // default value of 1, so any faulty value inputs are ignored and the calculation still works. const sumTotal = idArray.reduce((total, id) =&gt; total + fields.reduce((subtotal,field) =&gt; subtotal * getValue(section, id,field),1),0); var almostdone = sumTotal * multiplier; }); }); getAttrs(["gear-amount", "gear-carried", "grub", "water"], almostdone { var onegearmulti = parseFloat(values.gear-carried, 10)||1; var onegear = values.gear-amount * onegearmulti; var grub25 = values.grub * 0.25; var water25 = values.water * 0.25; setAttrs({[destination]: almostdone + onegear + grub25 + water25 }); }); }); }; And this is the function which calls the repeatingSum: on('change:repeating_gear remove:repeating_gear change:grub change:water change:gear-carried change: gear-amount', function() { repeatingSum("encumberance","gear",["gear-encumberance", "gear-amount", "gear-carried"]); }); Edit: Alright I toyed around a bit more and even the standalone RepeatingSum script only outputs the number of items in the repeating field and doesn't interact with other values I input at all. ;_;
1550310856

Edited 1550355693
GiGs
Pro
Sheet Author
API Scripter
You have a space in this line: on('change:repeating_gear remove:repeating_gear change:grub change:water change:gear-carried change: gear-amount', function() { It should be on('change:repeating_gear remove:repeating_gear change:grub change:water change:gear-carried change:gear-amount', function() { Your function probably still won't work for a bunch of reasons. The line " Here are the atrocities I have commited" &nbsp;gave me a chuckle. Here are the ones I spotted (I'm listing them to help you understand the issues, not for criticism - javascript is far from intuitive): you will be running into the asynchronous nature of roll20's sheet workers. Those two getAttrs might not work in sequence like that. repeatingSum expects attributes you supply in that array&nbsp; ["gear-encumberance", "gear-amount", "gear-carried"]&nbsp; to all be repeating set attributes, and it looks like they aren't, you cant use almostdone in the second getAtrrs line the way you have - you need a function(values) statement there - even if it worked without that, the other attributes (var onegear = values.gear-amount, etc) wouldn't return values because they come from the function(values) statement. you have several numerical attributes there that aren't coerced into numbers (you have one parseFloat, you need that or parseInt on the others too to handle invalid values (like empty inputs), e) when calling attributes with hyphens in the name you have to use this syntax&nbsp; values[gear-carried] instead of&nbsp; values.gear-carried. (I encourage everyone to avoid hyphens in attribute names for this reason: underscores are fine.) I do plan to rewrite repeatingSum to handle situations like the one you face better, but before I do, there is a simple solution to your problem: 1. Do not edit repeatingSum. Use the original version. 2. Create a hidden input (outside the repeating fieldset) to store the output from it. Let's say you call it "enc_holding". 3. You'd then create two sheet workers: the first monitors the repeating fieldset for changes and stores them in the hidden enc_holding attribute. on('change:repeating_gear remove:repeating_gear', function() { repeatingSum("enc_holding","gear","???"); }); I assume your repeating fieldset is called gear, but I cant tell which attribute or attributes in your example above come from the repeatingfieldset. Replace the ??? above with those - only &nbsp;including attributes from the repeating fieldset. Also I'd recommend (it's not essential, just better) changing the first change (change:repeating_gear) to (change:repeating_gear:attribute) with 'attribute' being the specific attribute name in the fieldset. Add one such change statement for each attribute you are monitoring. The second fieldset monitors the non-repeating attributes, including the hidden enc_holding attribute. Again, not 100% sure I have the attributes right here, but it should be all &nbsp;the non-repeating attributes, including the new enc_holding attribute. on('change:enc_holding change:grub change:water change:gear-carried change:gear-amount', function() { getAttrs(["enc-holding", "gear-amount", "gear-carried", "grub", "water"], function(values) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var encholding = parseFloat(values.enc-holding, 10)||0; var onegearmulti = parseFloat(values[gear-carried], 10)||1; var onegear = parseFloat(values[gear-amount],10)||0 * onegearmulti; var grub25 = parseFloat(values.grub,10)||0 * 0.25; var water25 = parseFloat(values.water,10)||0 * 0.25; setAttrs({ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"encumberance": encholding + onegear + grub25 + water25 }); }); }); So whenever the repeating fieldset changes, enc_holding changes, and that triggers the second sheet workers.&nbsp; PS: check the spelling on the sheet is actually&nbsp; encumberance . That's a misspelling, it should be&nbsp; encumbrance , but maybe the sheet is misspelled too.
GiGs said: You have a space in this line: on('change:repeating_gear remove:repeating_gear change:grub change:water change:gear-carried change: gear-amount', function() { It should be on('change:repeating_gear remove:repeating_gear change:grub change:water change:gear-carried change:gear-amount', function() { Your function probably still won't work for a bunch of reasons. The line " Here are the atrocities I have commited" &nbsp;gave me a chuckle. Here are the ones I spotted (I'm listing them to help you understand the issues, not for criticism - javascript is far from intuitive): you will be running into the asynchronous nature of roll20's sheet workers. Those two getAttrs might not work in sequence like that. repeatingSum expects attributes you supply in that array&nbsp; ["gear-encumberance", "gear-amount", "gear-carried"]&nbsp; to all be repeating set attributes, and it looks like they aren't, you cant use almostdone in the second getAtrrs line the way you have - you need a function(values) statement there - even if it worked without that, the other attributes (var onegear = values.gear-amount, etc) wouldn't return values because they come from the function(values) statement. you have several numerical attributes there that aren't coerced into numbers (you have one parseFloat, you need that or parseInt on the others too to handle invalid values (like empty inputs), e) when calling attributes with hyphens in the name you have to use this syntax&nbsp; values[gear-carried] instead of&nbsp; values.gear-carried. (I encourage everyone to avoid hyphens in attribute names for this reason: underscores are fine.) I do plan to rewrite repeatingSum to handle situations like the one you face better, but before I do, there is a simple solution to your problem: 1. Do not edit repeatingSum. Use the original version. 2. Create a hidden input (outside the repeating fieldset) to store the output from it. Let's say you call it "enc_holding". 3. You'd then create two sheet workers: the first monitors the repeating fieldset for changes and stores them in the hidden enc_holding attribute. on('change:repeating_gear remove:repeating_gear change:grub change:water change:gear-carried change:gear-amount', function() { repeatingSum("enc_holding","gear","???"); }); I assume your repeating fieldset is called gear, but I cant tell which attribute or attributes in your example above come from the repeatingfieldset. Replace the ??? above with those - only &nbsp;including attributes from the repeating fieldset. Also I'd recommend (it's not essential, just better) changing the first change (change:repeating_gear) to (change:repeating_gear:attribute) with 'attribute' being the specific attribute name in the fieldset. Add one such change statement for each attribute you are monitoring. The second fieldset monitors the non-repeating attributes, including the hidden enc_holding attribute. Again, not 100% sure I have the attributes right here, but it should be all &nbsp;the non-repeating attributes, including the new enc_holding attribute. on('change:enc_holding change:grub change:water change:gear-carried change:gear-amount', function() { getAttrs(["enc-holding", "gear-amount", "gear-carried", "grub", "water"], function(values) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var encholding = parseFloat(values.enc-holding, 10)||0; var onegearmulti = parseFloat(values[gear-carried], 10)||1; var onegear = parseFloat(values[gear-amount],10)||0 * onegearmulti; var grub25 = parseFloat(values.grub,10)||0 * 0.25; var water25 = parseFloat(values.water,10)||0 * 0.25; setAttrs({ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"encumberance": encholding + onegear + grub25 + water25 }); }); }); So whenever the repeating fieldset changes, enc_holding changes, and that triggers the second sheet workers.&nbsp; PS: check the spelling on the sheet is actually&nbsp; encumberance . That's a misspelling, it should be&nbsp; encumbrance , but maybe the sheet is misspelled too. Haha, oh boy. Yeah I assumed there was a BUNCH of things wrong with it. I did catch the encumberance typo after posting however! :D Thank you a LOT to take your time to look at my spaghetti code! :D What you made makes a lot more sense than what I cooked up! I also had the idea of using an intermittent value and then combine them to the final one, but my execution of that idea failed. I tried this solution just now, however it seems that repeatingSum only outputs the amount of rows found in the repeating field (Says 1, add another field it's 2, add another it's 3, remove one it's back to 2, etc), instead of doing anything with the values it's given... :&lt; Could it be that it doesn't like the float values? A problem I see is that the field values are called gear_encumbrance. So when it consolidates the parts via the script, it would create repeating_gear_X_gear_encumbrance? Maybe it doesnt like that? xD Anyways, thanks again &lt;3
1550355860

Edited 1550355920
GiGs
Pro
Sheet Author
API Scripter
By the way, I just editted the first of the sheet workers, I left a bunch of stuff in that shouldnt have been there (I could have sworn I'd removed it before posting): on('change:repeating_gear remove:repeating_gear', function() { repeatingSum("enc_holding","gear","???"); }); I tried this solution just now, however it seems that repeatingSum only outputs the amount of rows found in the repeating field (Says 1, add another field it's 2, add another it's 3, remove one it's back to 2, etc), instead of doing anything with the values it's given... :&lt; Could it be that it doesn't like the float values? A problem I see is that the field values are called gear_encumbrance. So when it consolidates the parts via the script, it would create repeating_gear_X_gear_encumbrance? Maybe it doesnt like that? xD Names with multiple underscores like that are fine. roll20 is smart enough to know how to handle them. I don't have enough info to figure out the problem. Can you post the full text of your sheet workers, and also the html for your repeating fieldsection?&nbsp;
1550368519

Edited 1550368609
GiGs said: By the way, I just editted the first of the sheet workers, I left a bunch of stuff in that shouldnt have been there (I could have sworn I'd removed it before posting): on('change:repeating_gear remove:repeating_gear', function() { repeatingSum("enc_holding","gear","???"); }); I tried this solution just now, however it seems that repeatingSum only outputs the amount of rows found in the repeating field (Says 1, add another field it's 2, add another it's 3, remove one it's back to 2, etc), instead of doing anything with the values it's given... :&lt; Could it be that it doesn't like the float values? A problem I see is that the field values are called gear_encumbrance. So when it consolidates the parts via the script, it would create repeating_gear_X_gear_encumbrance? Maybe it doesnt like that? xD Names with multiple underscores like that are fine. roll20 is smart enough to know how to handle them. I don't have enough info to figure out the problem. Can you post the full text of your sheet workers, and also the html for your repeating fieldsection?&nbsp; Alright wiiiiill do &lt;3 Field Section: &lt;fieldset class="repeating_gear"&gt; &lt;table class="sheet-table-twocolumn sheet-table-notop"&gt; &lt;tr class="sheet-row-light sheet-table-notop"&gt; &lt;td class="sheet-table-notop"&gt; &lt;input class="sheet-textinput-gear" name="attr_gear_name_repeating" type="text"&gt; &lt;/td&gt; &lt;td align="center" class="sheet-table-notop" valign="middle" width="130"&gt; &lt;select name="attr_gear_encumbrance_repeating"&gt; &lt;option value="0"&gt; 0 - Tiny &lt;/option&gt; &lt;option value="0.25"&gt; 1/4 - Very Light &lt;/option&gt; &lt;option value="0.5"&gt; 1/2 - Light &lt;/option&gt; &lt;option value="1"&gt; 1 - Basic &lt;/option&gt; &lt;option value="2"&gt; 2 - Heavy &lt;/option&gt; &lt;option value="3"&gt; 3 - Very Heavy &lt;/option&gt; &lt;/select&gt; &lt;/td&gt; &lt;td align="center" class="sheet-table-notop" width="86"&gt; &lt;input class="sheet-value-brighter sheet-value-singledigit" name="attr_gear_rating_repeating" type="number"&gt; / &lt;input class="sheet-value-brighter sheet-value-singledigit" name="attr_gear_rank_repeating_max" type="number"&gt; &lt;/td&gt; &lt;td align="center" class="sheet-table-notop" width="49"&gt; &lt;input class="sheet-value-brighter sheet-value-doubledigit" name="attr_gear_amount_repeating" type="number"&gt; &lt;/td&gt; &lt;td align="center" class="sheet-table-notop" width="49"&gt; &lt;input name="attr_gear_carried_repeating" type="checkbox"&gt; &lt;/td&gt; &lt;/tr&gt; &lt;/table&gt; &lt;/fieldset&gt; I am aware that there was some mixups between underscores and hyphens like name-type VS name_type but as far as I know I should have cleaned those out so far... x-x This would be the Sheet Worker... hard working sheet worker... const repeatingSum = (destination, section, fields, multiplier = 1) =&gt; { if (!Array.isArray(fields)) fields = [fields]; getSectionIDs(`repeating_${section}`, idArray =&gt; { const attrArray = idArray.reduce( (m,id) =&gt; [...m, ...(fields.map(field =&gt; `repeating_${section}_${id}_${field}`))],[]); getAttrs(attrArray, v =&gt; { console.log("===== values of v: "+ JSON.stringify(v) +" ====="); const getValue = (section, id,field) =&gt; parseFloat(v[`repeating_${section}_${id}_${field}`], 10)||1; // default value of 1, so any faulty value inputs are ignored and the calculation still works. const sumTotal = idArray.reduce((total, id) =&gt; total + fields.reduce((subtotal,field) =&gt; subtotal * getValue(section, id,field),1),0); setAttrs({[destination]: sumTotal * multiplier}); }); }); }; on('change:repeating_gear remove:repeating_gear', function() { repeatingSum('enc_holding','gear',['gear_encumbrance','gear_amount','gear_carried']); }); on('change:enc_holding change:grub change:water change:gear-carried change:gear-amount', function() { getAttrs(["enc_holding", "gear-amount", "gear-carried", "grub", "water"], function(values) { var encholding = parseFloat(values.enc_holding, 10)||0; var onegearmulti = parseFloat(values[gear-carried], 10)||1; var onegear = parseFloat(values[gear-amount],10)||0 * onegearmulti; var grub25 = parseFloat(values.grub,10)||0 * 0.25; var water25 = parseFloat(values.water,10)||0 * 0.25; setAttrs({ "encumbrance": encholding + onegear + grub25 + water25 }); }); }); We had our RPG session today so I didn't get to pour over it again! I probably missed something SUPER obvious... or something. ;__; Thanks again x-x
1550383734
GiGs
Pro
Sheet Author
API Scripter
I see the problem. You aren't using the correct attribute names. For instance, you have &lt;select name="attr_gear_encumbrance_repeating"&gt; This name is gear_encumbrance_repeating . But in the sheet worker, you list it as gear_encumbrance . You need to either drop the _repeating from the end of the names in the repeating field, or add it to these names in the sheet worker:&nbsp; ['gear_encumbrance','gear_amount','gear_carried']
1550403705

Edited 1550405847
GiGs said: I see the problem. You aren't using the correct attribute names. For instance, you have &lt;select name="attr_gear_encumbrance_repeating"&gt; This name is gear_encumbrance_repeating . But in the sheet worker, you list it as gear_encumbrance . You need to either drop the _repeating from the end of the names in the repeating field, or add it to these names in the sheet worker:&nbsp; ['gear_encumbrance','gear_amount','gear_carried'] Of course it had to be something like this! XD Oh boy, thank you so much, this finally works oh dear... and like a charm, too! This is the final, working worker script that does everything it should... xD /* ===== PARAMETERS ========== destination = the name of the attribute that stores the total quantity section = name of repeating fieldset, without the repeating_ fields = the name of the attribute field to be summed can be a single attribute: 'weight' or an array of attributes: ['weight','number','equipped'] multiplier (optional) = a multiplier to to entire fieldset total. For instance, if summing coins of weight 0.02, might want to multiply the final total by 0.02. */ const repeatingSum = (destination, section, fields, multiplier = 1) =&gt; { if (!Array.isArray(fields)) fields = [fields]; getSectionIDs(`repeating_${section}`, idArray =&gt; { const attrArray = idArray.reduce( (m,id) =&gt; [...m, ...(fields.map(field =&gt; `repeating_${section}_${id}_${field}`))],[]); getAttrs(attrArray, v =&gt; { console.log("===== values of v: "+ JSON.stringify(v) +" ====="); // getValue: if not a number, returns 1 if it is 'on' (checkbox), otherwise returns 0.. const getValue = (section, id,field) =&gt; parseFloat(v[`repeating_${section}_${id}_${field}`], 10) || (v[`repeating_${section}_${id}_${field}`] === 'on' ? 1 : 0); const sumTotal = idArray.reduce((total, id) =&gt; total + fields.reduce((subtotal,field) =&gt; subtotal * getValue(section, id,field),1),0); setAttrs({[destination]: sumTotal * multiplier}); }); }); }; /* This calls the repeating fields and saves them to enc_holding for later processing! */ on('change:repeating_gear remove:repeating_gear', function() { repeatingSum('enc_holding','gear',['gear_encumbrance_repeating','gear_amount_repeating','gear_carried_repeating']); }); /* This calls all the other, non-repeating values and combines everything together into the final encubmrance value! */ on('change:enc_holding change:grub change:water change:gear-encumbrance change:gear-carried change:gear-amount', function() { getAttrs(["enc_holding", "gear-encumbrance","gear-amount", "gear-carried", "grub", "water"], function(values) { var encholding = parseFloat(values["enc_holding"], 10)||0; var onegearcarried = parseFloat(values["gear-carried"] === 'on' ? 1 : 0); var onegearmulti = parseFloat(values["gear-amount"],10)||1; var onegear = parseFloat(values["gear-encumbrance"],10)||0 * onegearmulti * onegearcarried; var grub25 = parseFloat(values["grub"],10) * 0.25; var water25 = parseFloat(values["water"],10) * 0.25; setAttrs({ "encumbrance": encholding + onegear + grub25 + water25 }); }); }); Thank you VERY much, I was pouring over this for hours and I would have not have seen the light of day without your help... xD Sorry for putting your script through all that trouble! But it came out right in the end! ;ω; Edit: I discovered my sheet needs the "steps" value in the input fields to better handle float values! &lt;input type = "number" step = "0.01" &gt;
1550405657
GiGs
Pro
Sheet Author
API Scripter
Oh believe me, I have been through that too. I'm happy we got it sorted :)