Character Vault
Any Concept / Any System
Compendium
Your System Come To Life
Roll20 for Android
Streamlined for your Tablet
Roll20 for iPad
Streamlined for your Tablet

Personal tools

Difference between revisions of "RepeatingSum"

From Roll20 Wiki

Jump to: navigation, search
m (Changed parseInt to parseFloat in 2nd function)
Line 11: Line 11:
 
destination = the name of the attribute that stores the total quantity
 
destination = the name of the attribute that stores the total quantity
 
section = name of repeating fieldset, without the repeating_
 
section = name of repeating fieldset, without the repeating_
quantity = the name of the field to be summed
+
fields = the name of the attribute field to be summed
units (optional) = the name a field that multiplies quantity.
+
      can be a single attribute: 'weight'
  Set as "false" or don't include this parameter, if ignored. If using a checkbox rather than a number input, set its value to 1.
+
      or an array of attributes: ['weight','number','equipped']
multiplytotal (optional) = a multiplier to every item in the fieldset. For instance, if summing coins, might want to multiply the final total by 0.02 (if each coin weighs 0.02).
+
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.
 
*/
 
*/
let repeatingSum = function (destination, section, quantity, units, multiplytotal){
+
const repeatingSum = (destination, section, fields, multiplier = 1) => {
    let multiplier = true;
+
     if (!Array.isArray(fields)) fields = [fields];
    if (units === undefined || units === false || units === 'false') multiplier = false;
+
     getSectionIDs(`repeating_${section}`, idArray => {
     if (multiplytotal === undefined) multiplytotal = 1;
+
         const attrArray = idArray.reduce( (m,id) => [...m, ...(fields.map(field => `repeating_${section}_${id}_${field}`))],[]);
     getSectionIDs(`repeating_${section}`, (idArray) => {
+
         getAttrs(attrArray, v => {
         const quantityFields = idArray.map(id => `repeating_${section}_${id}_${quantity}`);
+
        const unitFields = multiplier ? idArray.map(id => `repeating_${section}_${id}_${units}`) : [];
+
         getAttrs(quantityFields.concat(unitFields), (v) => {
+
 
             console.log("===== values of v: "+ JSON.stringify(v) +" =====");
 
             console.log("===== values of v: "+ JSON.stringify(v) +" =====");
            let sumTotal = 0;
+
             const getValue = (section, id,field) => parseInt(v[`repeating_${section}_${id}_${field}`], 10)||0;
             const rowValues = quantityFields.map(attr => parseFloat(v[attr], 10) || 0);
+
             const sumTotal = idArray.reduce((total, id) => total + fields.reduce((subtotal,field) => subtotal * getValue(section, id,field),1),0);
             if(multiplier) {
+
             setAttrs({[destination]: sumTotal * multiplier});     
                const rowMultiples = unitFields.map(attr => parseFloat(v[attr], 10) || 0);
+
                sumTotal = _.zip(rowValues, rowMultiples).map(([a, b]) => a*b).reduce((m, x) => m + x, 0);
+
            } else {
+
                sumTotal = rowValues.reduce((m, x) => m + x, 0);
+
            }
+
             setAttrs({[destination]: sumTotal * multiplytotal});     
+
 
         });
 
         });
 
     });
 
     });
}
+
};
 
</pre>
 
</pre>
 
== Using The Function ==
 
== Using The Function ==
 +
=== Simple Example ===
 
Let's say you have an fieldset called '''repeating_inventory''', and in that set you have fields '''item_name''' and '''item_weight'''. You want to sum all the weights, and show that in an attribute outside the fieldset named '''encumbrance_total'''.
 
Let's say you have an fieldset called '''repeating_inventory''', and in that set you have fields '''item_name''' and '''item_weight'''. You want to sum all the weights, and show that in an attribute outside the fieldset named '''encumbrance_total'''.
  
Line 47: Line 39:
 
});
 
});
 
</pre>
 
</pre>
 
+
=== Weight * Number ===
 
Most inventory lists are a little more complicated. You might have an extra field named '''item_number'''. For instance, your equipment list might include:
 
Most inventory lists are a little more complicated. You might have an extra field named '''item_number'''. For instance, your equipment list might include:
 
<pre>
 
<pre>
Line 56: Line 48:
 
<pre>
 
<pre>
 
on('change:repeating_inventory remove:repeating_inventory', function() {
 
on('change:repeating_inventory remove:repeating_inventory', function() {
repeatingSum("encumbrance_total","inventory","item_weight","item_number");
+
repeatingSum("encumbrance_total","inventory",["item_weight","item_number"]);
 
});
 
});
 
</pre>
 
</pre>
 
+
=== Conditional Sums (e.g. using a Checkbox) ===
 
You could use it for conditional items. Let's say you have a '''repeating_armour''' fieldset, with field names, '''armour_piece''' and '''armour_worn'''. Armour_worn is a checkbox, with a value of 1. So, you can list a variety of armours, and decide which ones you are wearing by ticking the checkbox. The following function would total the worn armour pieces and add it to an '''armour_weight''' attribute, and ignore the armour not being worn.
 
You could use it for conditional items. Let's say you have a '''repeating_armour''' fieldset, with field names, '''armour_piece''' and '''armour_worn'''. Armour_worn is a checkbox, with a value of 1. So, you can list a variety of armours, and decide which ones you are wearing by ticking the checkbox. The following function would total the worn armour pieces and add it to an '''armour_weight''' attribute, and ignore the armour not being worn.
 
<pre>
 
<pre>
 
on('change:repeating_armour remove:repeating_armour', function() {
 
on('change:repeating_armour remove:repeating_armour', function() {
repeatingSum("armour_weight", "armour","armour_piece","armour_worn");
+
repeatingSum("armour_weight", ["armour","armour_piece","armour_worn"]);
 
});
 
});
 
</pre>
 
</pre>
 
+
=== Multiplying the Total ===
 
For a final example, lets say you have a '''repeating_coinage''' fieldset, with fields '''coin_name''', '''coin_value''', and '''coin_number'''. In this set, names and values are fixed (copper, 1, silver, 10, gold, 100), and players enter the number of each they have. You want to report (a) the total value, and (b) the total weight of the coins.
 
For a final example, lets say you have a '''repeating_coinage''' fieldset, with fields '''coin_name''', '''coin_value''', and '''coin_number'''. In this set, names and values are fixed (copper, 1, silver, 10, gold, 100), and players enter the number of each they have. You want to report (a) the total value, and (b) the total weight of the coins.
  
 
<pre>
 
<pre>
 
on('change:repeating_coinage remove:repeating_coinage', function() {
 
on('change:repeating_coinage remove:repeating_coinage', function() {
repeatingSum("total_coin_value", "coinage", "coin_number", "coin_value");
+
repeatingSum("total_coin_value", "coinage", ["coin_number", "coin_value"]);
repeatingSum("total_coin_weight", "coinage", "coin_number", "coin_weight", '0.02');
+
repeatingSum("total_coin_weight", "coinage", ["coin_number", "coin_weight"], '0.02');
 
});
 
});
 
</pre>
 
</pre>
The first repeatingSum above multiples the number gold coins by god value, adds that to the number of silver coins multiplied by silver value, and adds to that the number of copper coins multiplied by copper value.  
+
The first repeatingSum above multiples the number gold coins by gold value, adds that to the number of silver coins multiplied by silver value, and adds to that the number of copper coins multiplied by copper value.  
 
The second repeatingSum does the same for weights, but then multiples the total weight by 0.02 (so you get 50 coins per pound). This allows you to list coin weights as relative weights - lets say copper and silver coins both weigh then same, so the coin_weight is 1 for those. But gold coins are heavier and have a coin_weight of 2.  
 
The second repeatingSum does the same for weights, but then multiples the total weight by 0.02 (so you get 50 coins per pound). This allows you to list coin weights as relative weights - lets say copper and silver coins both weigh then same, so the coin_weight is 1 for those. But gold coins are heavier and have a coin_weight of 2.  
  
== Simpler Version of the Function ==
+
 
If you're creating your own character sheet, you might want to understand how the repeatingSum function works. The function below has exactly the same functionality, but is written using expressions that might be more familiar to those without much javascript experience. It'll likely still take some studying to figure out, but I present it as a self-teaching tool. I leave the studying of it as an exercise to the reader!
+
<pre>
+
function repeatingSum (destination, section, quantity, units, multiplytotal){
+
var repSection = "repeating_" + section;
+
    var multiplier = true;
+
    // check if then optional parameters exist, and handle if they don't.
+
if (units === undefined || units === false || units === 'false') multiplier = false;
+
if (multiplytotal === undefined) multiplytotal = 1;
+
getSectionIDs(repSection, function(idArray) {
+
//Construct the Arrays of fields to get attributes for...
+
var quantityFields = [];
+
var unitFields = [];
+
for (var a=0; a< idArray.length; a++) {
+
            // each row in a repeating section has its own id.
+
            // idArray is an array of those ids.
+
            // using them you can build a n array of the full attribute names for quantity and units fields.
+
quantityFields[quantityFields.length] = repSection + "_" + idArray[a] + "_" + quantity;
+
if(multiplier) unitFields[unitFields.length] = repSection + "_" + idArray[a] + "_" + units;
+
        }
+
        // combine the arrays so that getAttrs works properly
+
var fields = quantityFields.concat(unitFields);
+
getAttrs(fields, function(v) {
+
console.log("%%% values of v: "+ JSON.stringify(v) +" %%%");
+
var sumTotal = 0;
+
            var tempSum = 0;
+
           
+
for(a=0; a<idArray.length; a++){
+
                // for each row in the repeating section, get the quantity and unit attributes, and sum them up.
+
    tempSum = parseFloat(v[quantityFields[a]]) || 0;
+
    if(multiplier) {
+
        tempSum = tempSum * parseFloat(v[unitFields[a]]) || 0;
+
    }
+
sumTotal += tempSum;
+
console.log("$$$ sumTotal: " + sumTotal + " $$$");
+
}
+
var setSumValue = {};
+
setSumValue[destination]=sumTotal * multiplytotal;
+
setAttrs(setSumValue);
+
});
+
});
+
}
+
</pre>
+
 
'''Author:''' [https://app.roll20.net/users/157788 GiGs](G-G-G on github).
 
'''Author:''' [https://app.roll20.net/users/157788 GiGs](G-G-G on github).
 
__FORCETOC__
 
__FORCETOC__
 
[[Category:Tips]]
 
[[Category:Tips]]
 
[[Category:User content]]
 
[[Category:User content]]

Revision as of 00:48, 5 February 2019

One request that crops up on the roll20 forums over and over again, is how can you add up all the items in a repeating section?

Say you have an inventory section, listing the items you are carrying, and you need their total weight. Or you have section listing all the coins of different types, and you want their values. Or a skill or power section, and you want the total character points used to buy them.

The sheet worker function below will do that for you. Some examples of how to use it are listed below.

Contents

repeatingSum Function

Include the following function in the sheet worker script section of your character sheet. (A simpler version of the function is at the bottom of the page, for teaching purposes.)

/* ===== 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) => {
    if (!Array.isArray(fields)) fields = [fields];
    getSectionIDs(`repeating_${section}`, idArray => {
        const attrArray = idArray.reduce( (m,id) => [...m, ...(fields.map(field => `repeating_${section}_${id}_${field}`))],[]);
        getAttrs(attrArray, v => {
            console.log("===== values of v: "+ JSON.stringify(v) +" =====");
            const getValue = (section, id,field) => parseInt(v[`repeating_${section}_${id}_${field}`], 10)||0;
            const sumTotal = idArray.reduce((total, id) => total + fields.reduce((subtotal,field) => subtotal * getValue(section, id,field),1),0);
            setAttrs({[destination]: sumTotal * multiplier});    
        });
    });
};

Using The Function

Simple Example

Let's say you have an fieldset called repeating_inventory, and in that set you have fields item_name and item_weight. You want to sum all the weights, and show that in an attribute outside the fieldset named encumbrance_total.

You'd add the above function, and the following one:

on('change:repeating_inventory remove:repeating_inventory', function() {
	repeatingSum("encumbrance_total","inventory","item_weight");
});

Weight * Number

Most inventory lists are a little more complicated. You might have an extra field named item_number. For instance, your equipment list might include:

item_name: bow, item_weight: 3, item_number: 1
item_name: arrows: item_weight: 0.1, item_number: 20

and so on. In this case, you'd use the following function:

on('change:repeating_inventory remove:repeating_inventory', function() {
	repeatingSum("encumbrance_total","inventory",["item_weight","item_number"]);
});

Conditional Sums (e.g. using a Checkbox)

You could use it for conditional items. Let's say you have a repeating_armour fieldset, with field names, armour_piece and armour_worn. Armour_worn is a checkbox, with a value of 1. So, you can list a variety of armours, and decide which ones you are wearing by ticking the checkbox. The following function would total the worn armour pieces and add it to an armour_weight attribute, and ignore the armour not being worn.

on('change:repeating_armour remove:repeating_armour', function() {
	repeatingSum("armour_weight", ["armour","armour_piece","armour_worn"]);
});

Multiplying the Total

For a final example, lets say you have a repeating_coinage fieldset, with fields coin_name, coin_value, and coin_number. In this set, names and values are fixed (copper, 1, silver, 10, gold, 100), and players enter the number of each they have. You want to report (a) the total value, and (b) the total weight of the coins.

on('change:repeating_coinage remove:repeating_coinage', function() {
	repeatingSum("total_coin_value", "coinage", ["coin_number", "coin_value"]);
	repeatingSum("total_coin_weight", "coinage", ["coin_number", "coin_weight"], '0.02');
});

The first repeatingSum above multiples the number gold coins by gold value, adds that to the number of silver coins multiplied by silver value, and adds to that the number of copper coins multiplied by copper value. The second repeatingSum does the same for weights, but then multiples the total weight by 0.02 (so you get 50 coins per pound). This allows you to list coin weights as relative weights - lets say copper and silver coins both weigh then same, so the coin_weight is 1 for those. But gold coins are heavier and have a coin_weight of 2.


Author: GiGs(G-G-G on github).