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] Update Repeating Sections based on change of non-repeating attribute?

I want a sheetworker to update some attributes for all items in a particular repeating section when a certain non-repeating attribute changes. Specifically, when Base Attack Bonus (attr_bab) changes, I want to update some attributes on all repeating_melee items. How do I loop through all the repeating_melee items? Is there a good example to do this? Is there a sheet with a sheetworker that already does this that I could look at? To complicate things, the sheetworker also runs on some changes within the repeating_melee items as well. Do I need to make a different code block for each change condition (repeating change vs non-repeating change)? Thanks in advance for any advice and assistance!
1530993175
Jakob
Sheet Author
API Scripter
Here's what I do in the Blades sheet. Here, the repeating attribute `repeating_cohort_${id}_roll_formula` depends on both the non-repeating crew_tier attribute and a bunch of row-specific repeating attributes. (I changed it slightly to reduce stuff that's not important for this). calculateCohortDice = prefixes => { const sourceAttrs = [ "crew_tier", ...prefixes.map(p => `${p}_impaired`), ...prefixes.map(p => `${p}_type`), ...prefixes.map(p => `${p}_roll_formula`), ]; getAttrs(sourceAttrs, v => { const setting = prefixes.reduce((m, prefix) => { const dice = (parseInt(v.crew_tier) || 0) - (parseInt(v[`${prefix}_impaired`]) || 0) + ((v[`${prefix}_type`] === "elite" || v[`${prefix}_type`] === "expert") ? 1 : 0); const formula = buildRollFormula(dice); m[`${prefix}_roll_formula`] = formula; return m; }, {}); setAttrs(setting); }); }; How exactly dice is constructed doesn't matter that much, what's important is how the repeating attributes are handled here. The function has to be called in a different way, depending on if you want to run it for a single section or all sections at once. Here are the event handlers: // Handler for repeating attributes on("change:repeating_cohort", () => calculateCohortDice(["repeating_cohort"])); // Handler for non-repeating attributes on("change:crew_tier", () => { getSectionIDs("repeating_cohort", a => calculateCohortDice(a.map(id => `repeating_cohort_${id}`))); });
Many thanks for your quick and detailed assistance, Jakob! Apologies for the delay in my responding. I have not had time to implement this yet, and I kept thinking I would get a chance to play with it before now. I have a much more clear understanding of what to do now, but I reserve the right to need further aid in the details! ;-) Thanks again!
Having worked through this further, the parameters of my problem have simplified somewhat. The change event is no longer triggered by any repeating attributes, but there are now two non-repeating attributes to watch. Also, I need to run the attributes through an if/then and a switch. Here is my pseudocode (parts that need to be covered by something else are in bold ): on("change:bab change:bab_flurry", function() { getAttrs(["bab", "bab_flurry", "repeating_melee_mwpn_attack_type" ], function(value) { bab = parseInt(value["bab"], 10); bab_flurry = parseInt(value["bab_flurry"], 10); forEach(repeating_melee) if ( (value["repeating_melee_mwpn_attack_type"] != "Primary") && (value["repeating_melee_mwpn_attack_type"] != "Secondary") ) { //RESET FLAGS AND VALUES TO NULL bab_flag = 0; flurry_flag = 0; attacks2_show = 0; attacks3_show = 0; attacks4_show = 0; attacks5_show = 0; strike02 = ""; strike03 = ""; strike04 = ""; strike05 = ""; switch(value.repeating_melee_mwpn_attack_type) { case "BAB": bab_flag = 1; if (bab > 15) { // 4 ATTACKS attacks2_show = 1; attacks3_show = 1; attacks4_show = 1; strike02 = "@{mwpn_strike02_macro}"; strike03 = "@{mwpn_strike03_macro}"; strike04 = "@{mwpn_strike04_macro}"; } else if (bab > 10) { // 3 ATTACKS attacks2_show = 1; attacks3_show = 1; strike02 = "@{mwpn_strike02_macro}"; strike03 = "@{mwpn_strike03_macro}"; } else if (bab > 5) { // 2 ATTACKS attacks2_show = 1; strike02 = "@{mwpn_strike02_macro}"; } break; case "Flurry": flurry_flag = 1; attacks2_show = 1; strike02 = "@{mwpn_strike02_macro}"; if (bab_flurry > 14) { // 5 ATTACKS attacks3_show = 1; attacks4_show = 1; attacks5_show = 1; strike03 = "@{mwpn_strike03_macro}"; strike04 = "@{mwpn_strike04_macro}"; strike05 = "@{mwpn_strike05_macro}"; } else if (bab_flurry > 10) { // 4 ATTACKS attacks3_show = 1; attacks4_show = 1; strike03 = "@{mwpn_strike03_macro}"; strike04 = "@{mwpn_strike04_macro}"; } else if (bab_flurry > 7) { // 3 ATTACKS attacks3_show = 1; strike03 = "@{mwpn_strike03_macro}"; } break; } setAttrs({ "repeating_melee_mwpn_bab_flag": bab_flag, "repeating_melee_mwpn_flurry_flag": flurry_flag, "repeating_melee_mwpn_primary_flag": primary_flag, "repeating_melee_mwpn_secondary_flag": secondary_flag, "repeating_melee_mwpn_attacks2_show": attacks2_show, "repeating_melee_mwpn_attacks3_show": attacks3_show, "repeating_melee_mwpn_attacks4_show": attacks4_show, "repeating_melee_mwpn_attacks5_show": attacks5_show, "repeating_melee_mwpn_strike02": strike02, "repeating_melee_mwpn_strike03": strike03, "repeating_melee_mwpn_strike04": strike04, "repeating_melee_mwpn_strike05": strike05 }); }); //END OF FOREACH }); // END OF IF/THEN }); Any suggestions?
1533602308
Jakob
Sheet Author
API Scripter
Well, it was a bit of a mess, but here's my version. It's not the type of code I would write, but I made sufficient changes to make it run. on("change:bab change:bab_flurry", function () { getSectionIDs("repeating_melee", function (idArray) { getAttrs([ "bab", "bab_flurry", // the next line maps the array of ids to the corresponding attribute names ...idArray.map(id => `repeating_melee_${id}_mwpn_attack_type`) ], function (value) { // never use undeclared variables const bab = parseInt(value["bab"], 10); const bab_flurry = parseInt(value["bab_flurry"], 10); idArray.forEach(function (id) { // executes the function once for each repeating section ID if ((value[`repeating_melee_${id}_mwpn_attack_type`] != "Primary") && (value[`repeating_melee_${id}_mwpn_attack_type`] != "Secondary")) { //RESET FLAGS AND VALUES TO NULL // never use undeclared variables let bab_flag = 0; let flurry_flag = 0; let attacks2_show = 0; let attacks3_show = 0; let attacks4_show = 0; let attacks5_show = 0; let strike02 = ""; let strike03 = ""; let strike04 = ""; let strike05 = ""; switch (value.repeating_melee_mwpn_attack_type) { case "BAB": bab_flag = 1; if (bab > 15) { // 4 ATTACKS attacks2_show = 1; attacks3_show = 1; attacks4_show = 1; strike02 = "@{mwpn_strike02_macro}"; strike03 = "@{mwpn_strike03_macro}"; strike04 = "@{mwpn_strike04_macro}"; } else if (bab > 10) { // 3 ATTACKS attacks2_show = 1; attacks3_show = 1; strike02 = "@{mwpn_strike02_macro}"; strike03 = "@{mwpn_strike03_macro}"; } else if (bab > 5) { // 2 ATTACKS attacks2_show = 1; strike02 = "@{mwpn_strike02_macro}"; } break; case "Flurry": flurry_flag = 1; attacks2_show = 1; strike02 = "@{mwpn_strike02_macro}"; if (bab_flurry > 14) { // 5 ATTACKS attacks3_show = 1; attacks4_show = 1; attacks5_show = 1; strike03 = "@{mwpn_strike03_macro}"; strike04 = "@{mwpn_strike04_macro}"; strike05 = "@{mwpn_strike05_macro}"; } else if (bab_flurry > 10) { // 4 ATTACKS attacks3_show = 1; attacks4_show = 1; strike03 = "@{mwpn_strike03_macro}"; strike04 = "@{mwpn_strike04_macro}"; } else if (bab_flurry > 7) { // 3 ATTACKS attacks3_show = 1; strike03 = "@{mwpn_strike03_macro}"; } break; } setAttrs({ [`repeating_melee_${id}_mwpn_bab_flag`]: bab_flag, [`repeating_melee_${id}_mwpn_flurry_flag`]: flurry_flag, [`repeating_melee_${id}_mwpn_attacks2_show`]: attacks2_show, [`repeating_melee_${id}_mwpn_attacks3_show`]: attacks3_show, [`repeating_melee_${id}_mwpn_attacks4_show`]: attacks4_show, [`repeating_melee_${id}_mwpn_attacks5_show`]: attacks5_show, [`repeating_melee_${id}_mwpn_strike02`]: strike02, [`repeating_melee_${id}_mwpn_strike03`]: strike03, [`repeating_melee_${id}_mwpn_strike04`]: strike04, [`repeating_melee_${id}_mwpn_strike05`]: strike05 }); } }); }); }); });
Many thanks again, Jakob. The syntax for mapping and arrays still baffle me. Not asking you to re-write my code, but can you describe in a general sense the approach you would take? At some point I may attempt to tighten things up. Thanks again!