The first thing you're using the same name for your array at the start as the last one: const stats = ["1handedcrushweapons", "1handededgedweapons"]; You cant use the same variable names within the scope. You'll need to change one or the other, and check over to make sure you dont have others named the same. You can use the same name inside workers and functions, but not at the same level. I'd change this to weapon_skills . You dont need to change anything inside the function - just the name of the variable, and the start of the forEach. Also your stat declarations use value , not values , so that would cause the function to fail. Example: let sd = parseInt(value.sd_bonus)||0; Youre also completely missing the values from this line const skill_stats = [`${stat}stats`]; Also this line on(`change:${stat}ranks`, () => { doesnt include the stats themselves. Which means the bonuses wont change then the stat scores and their bonuses change. On to your actual issue. You can handle this pretty simply. Assuming all your bonuses are like "AG", "AG/SD", "AG/ME/RE" or whatever. make sure you use a slash to separate them, and the two letters shown perfectly match the two letters at the start of " co _bonus", " ag _bonus", etc. Case doesnt matter. You'd start the worker like this: const skill_stats = [`${stat}stats`]; const stat_names = skill_stats.split('/'); This will give you an array of the attribute labels, like so: ['AG'], ["AG", "SD"], ["AG", "ME", "RE" ] Then you loop through the array let stat_bonus = 0;
stat_names.forEach(name => { stat_bonus += int(values[`${name.toLowerCase()}_bonus`]); }); This gets the label of the stat used, and gets its value, and adds it to a running total. It works for any number of stats in the label. Breaking it down we have this: name.toLowerCase() the toLowerCase() part isnt actually necessary, but its here in case roll20 does ever start to respecting case in their attribute names. It converts AG to ag. Then values[`${name.toLowerCase()}_bonus`] becomes values[`ag_bonus`] so it gets the right attribute. And the loop handles each attribute. Putting it together you get: const weaponskills = ["1handedcrushweapons", "1handededgedweapons"]; weaponskills.forEach(stat => { on(`change:${stat}ranks`, () => { getAttrs([`${stat}stats`, `${stat}statbonus`, 'co_bonus', 'ag_bonus', 'sd_bonus', 'me_bonus', 're_bonus', 'st_bonus', 'qu_bonus', 'pr_bonus', 'em_bonus', 'in_bonus'], values => { const skill_stats = [`${stat}stats`]; let stat_names = skill_stats.split('/'); let stat_bonus = 0; stat_names.forEach(name => { stat_bonus += int(values[`${name.toLowerCase()}_bonus`]); }); setAttrs({ [`${stat}statbonus`]: stat_bonus }); }); }); }); IMPORTANT If any of your skills have names that are not the 10 above: co, ag, sd, me, re, st, qu, pr, em, in , the function above might not work as intended. You need to tell me what the other possibilities are and I can modify the function above to handle them. But if you make sure all the skills you feed to this function just use those codes, this should be fine. Since you might want to use this function in other workers you could convert the important part to a function. I have a couple of suggestions for that and other streamlining I'll post in the next post.