Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

Sheetworker Help: Generic Functions? Combining Tasks?

Two sheetworker questions. I have code that does the same thing for multiple different attributes, for example: on("sheet:opened change:str_base change:str_race change:str_magic change:str_misc change:str_temp", function() {   getAttrs(["str", "str_base", "str_race", "str_magic", "str_misc", "str_temp"], function(values) {     setAttrs({       str: ( (parseInt(values["str_base"],10) || 10) + (parseInt(values["str_race"],10) || 0) + (parseInt(values["str_magic"],10) || 0)             + (parseInt(values["str_misc"],10) || 0) + (parseInt(values["str_temp"],10) || 0) )     });   }); }); I have a separate block of code like the above for each ability score (Str, Dex, Con, etc.). How do I make this a generic function that will work for each ability? I thought I had seen something like this in the forums here, but I cannot find it now. Secondly, a "best practices" type of question. Say I have code like the above that watches a number of attributes for changes, and I have another block of code that executes only when one of those attributes changes. For example, I have another sheetworker that only executes when str_base changes. Is it more efficient/better coding to combine them into one event, or should I keep them separate ones? Or does it matter at the scales character sheets work at? Thanks in advance for any insight/advice!
1494772313
Jakob
Sheet Author
API Scripter
For your first question, you want something like this: let stats = ['str', 'dex', 'con']; //Extend as necessary stats.forEach(function (stat) { let attrs = [`${stat}_base`, `${stat}_race}`, `${stat}_magic}`]; //Extend as necessary on('sheet:opened ' + attrs.map(str => `change:${str}`).join(' '), function () { getAttrs(attrs, function (values) { let setting = {}; setting[stat] = (parseInt(values[`${stat}_base`],10) || 10) + (parseInt(values[`${stat}_race`],10) || 0) + (parseInt(values[`${stat}_magic`],10) || 0); setAttrs(setting); }); }); });
Thanks, Jakob! That does look familiar with the let and the array. However, it's only reflecting changes to the _base attribute, not adding the other values. Here is the code I am using, extended some from your example: let stats = ['str', 'dex', 'con', 'int', 'wis', 'cha']; stats.forEach(function (stat) {   let attrs = [`${stat}_base`, `${stat}_race}`, `${stat}_magic}`, `${stat}_misc}`, `${stat}_temp}`];   on('sheet:opened ' + attrs.map(str => `change:${str}`).join(' '), function () {     getAttrs(attrs, function (values) {       let setting = {};       setting[stat] = (parseInt(values[`${stat}_base`],10) || 10) + (parseInt(values[`${stat}_race`],10) || 0)         + (parseInt(values[`${stat}_magic`],10) || 0) + (parseInt(values[`${stat}_misc`],10) || 0) + (parseInt(values[`${stat}_temp`],10) || 0);       setAttrs(setting);     });   }); }); Did I screw something up?
1494777524

Edited 1494777541
Jakob
Sheet Author
API Scripter
No, I made a typo in the attrs array, sorry... there's an extra closing curly brace at the end - `${stat}_race`, not `${stat}_race}` (same for magic etc).
Ah... I should have caught that. I was too focused on using the backticks properly. Many thanks again!
1494819615

Edited 1494819693
Now I want to add an additional calculation based on the ability score just set. Should that be a separate script or can it be added to the above? It needs to getAttrs attributes that are not monitored for change (str_mod, dex_mod, etc.) I tried the following (and some other variations) but it did not work: let stats = ['str', 'dex', 'con', 'int', 'wis', 'cha']; stats.forEach(function (stat) {     let attrs = [`${stat}_base`, `${stat}_race`, `${stat}_magic`, `${stat}_misc`, `${stat}_temp`];     on('sheet:opened ' + attrs.map(str => `change:${str}`).join(' '), function () {         getAttrs( `${stat}_mod,` + attrs, function (values) {             let setting = {};             setting[stat] = (parseInt(values[`${stat}_base`],10) || 10) + (parseInt(values[`${stat}_race`],10) || 0)                 + (parseInt(values[`${stat}_magic`],10) || 0) + (parseInt(values[`${stat}_misc`],10) || 0) + (parseInt(values[`${stat}_temp`],10) || 0);             setting[`${stat}_mod`] = Math.floor( ( (parseInt(values.[`${stat}`],10) || 10) / 2 ) - 5 );             setAttrs(setting);         });     }); }); Can setting[] set more than one attribute at a time? Additionally, back to my second question, if I decided to do all calculations in a single script, I assume I could add an IF...THEN or SWITCH  within the above somehow, right? Thanks for helping the Javascript clueless!
1494828758

Edited 1494840494
Jakob
Sheet Author
API Scripter
Okay, so first off, unless you plan to use its value, you do not need to include the mod in the getAttrs to set it later. There would be no problem in getting non-monitored attributes, but since you never use values[`${stat}_mod`] anywhere, there's no need to get it. Secondly, `${stat}_mod,` + attrs will add a string to an array, which will definitely not produce an array that contains attrs and `${stat}_mod` (it will convert the array to a String, I think, and the output is a garbled string). Instead, if you wanted to do this (but you don't need to ), you would do it with an expression like this attrs.concat(`${stat}_mod`) Anyway, once you fix this this, yes, you can definitely set more than one attribute at a time with setAttrs. However, this expression values.[`${stat}`] is problematic for 3 reasons. 1) No period if you access object properties via brackets (just values[`${stat}`], or indeed values[stat], which is syntactically equivalent). 2) You haven't included stat in attrs, so values[stat] is undefined. 3) Even if it were defined, it would be the OLD value of the attribute instead of the new, calculated one that you actually want to use. So, use setting[stat] instead. (Incidentally, you can then drop the parseInt() here, ince setting[stat] is already an integer.) With regard to your second question, don't worry about performance, use the most readable/maintainable variant. But you can definitely add conditional statements here.
1494869355
Lithl
Pro
Sheet Author
API Scripter
Jakob said: if you wanted to do this (but you don't need to ), you would do it with an expression like this attrs.concat(`${stat}_mod`) Or, you could use the spread operator: var arr = [1, 2, 3]; console.log([0, ...arr]); // [0, 1, 2, 3] console.log([...arr, 4]); // [1, 2, 3, 4]
1494902400

Edited 1494902437
Many, many thanks, Jakob (and you, too, Brian). Here is my final product (so far - I may add more later, now that I have a better understanding), shared for anyone else using D&D 3.5-ish stats. Entering base stats and modifiers, this sheetworker will compute the final ability score, the ability score mod, and the ability score point cost (per 3.5 DMG p.169): let stats = ['str', 'dex', 'con', 'int', 'wis', 'cha']; stats.forEach(function (stat) {     let attrs = [`${stat}_base`, `${stat}_race`, `${stat}_magic`, `${stat}_misc`, `${stat}_temp`];     on('sheet:opened ' + attrs.map(str => `change:${str}`).join(' '), function () {         getAttrs(attrs, function (values) {             let setting = {};             setting[stat] = (parseInt(values[`${stat}_base`],10) || 10) + (parseInt(values[`${stat}_race`],10) || 0)                 + (parseInt(values[`${stat}_magic`],10) || 0) + (parseInt(values[`${stat}_misc`],10) || 0) + (parseInt(values[`${stat}_temp`],10) || 0);             setting[`${stat}_mod`] = Math.floor( ( (setting[stat]) / 2 ) - 5 );             setting[`${stat}_cost`] = Math.max(( (values[`${stat}_base`] || 10) - 8), 0) + Math.max(( (values[`${stat}_base`] || 10) - 14), 0)                 + Math.max(( (values[`${stat}_base`] || 10) - 16), 0);             setAttrs(setting);         });     }); });
1496285893

Edited 1496285947
OK, next problem. I have searched for this on various JavaScript sites and forums, but I cannot find anything like this. I might not be using the correct terms in my searching - all I end up with is how to combine two arrays into a single array (a useful function, to be sure, but not what I am looking for here). Say I have a long list of attributes to monitor for change and perform math functions on. Can I use arrays to build the list of attributes more easily? Can I do the math more easily? For a short example, say I have attributes in the form of attr_color_fruit, and I have blue, green, and red for colors, and banana, apple, and pear for fruits. I want to monitor blue_banana, blue_apple, blue_pear, green_banana, green_apple, green_pear, red_banana, red_apple, and red_pear for changes. Then I want to sum their values and apply the total to another attribute. Is there an easier way to write this out using arrays of [blue, green, red] and [banana, apple, pear]? Do I just build another forEach loop inside the first one? And is using the arrays more efficient in terms of processing, or is it just easier to write, makes more legible code, and is less text to download? Thanks again for assistance!
Here's what I have so far. The hard part I need syntax assistance with is bolded. let colors = ['blue', 'green', 'red']; let fruits = ['banana', 'apple', 'pear']; colors.forEach(function (color) {   fruits.forEach(function (fruit) {     let attrs = [`${color}_${fruit}`];     on('sheet:opened ' + attrs.map(str => `change:${color}_${fruit}`).join(' '), function () {         getAttrs(attrs, function (values) {             let setting = {};             setting[`new_attr`] = //use map to build the array; get the values from the array; use reduce to total them up ;             setAttrs(setting);             });         });     }); });
1496428149

Edited 1496428516
Jakob
Sheet Author
API Scripter
Okay, what you are doing will not work if you just change the bolded part, since the attrs array only consist of one element every time. You'll want to build the compounded array first, so you can feed the complete list of attributes to getAttrs. Something like this: let colors = ['blue', 'green', 'red'],   fruits = ['banana', 'apple', 'pear'],   colorsAndFruits = colors.reduce((m, color) => m.concat(fruits.map(fruit => `${color}_${fruit}`)), []),   colorFruitEvent = 'sheet:opened ' + colorsAndFruits.map(str => `change:${str}`).join(' '); on(colorFruitEvent, () => {   getAttrs(colorsAndFruits, function(values) {     let setting = {};     setting[`new_attr`] = colorsAndFruits.map(attrName => values[attrName]).reduce(); // TODO: give arguments for reduce()     setAttrs(setting);   }); }); The core part is the construction of colorsAndFruits in the bolded line.
Many thanks again, Jakob. I would never have thought to build the array of values outside the event. I still have much to learn about JavaScript and its syntax and processing.