There are a couple of mistakes here. First the "attr_" is not part of the attribute name. When you have this: name="attr_SERPENT_Mystik" the attr_ part is to tell roll20 that this is an attribute. This is because there are different types of named items, you can have "roll_" and "act_". so roll20 uses that prefix to treat that as a character attribute. So in this case, the attribute name is SERPENT_Mystik. Secondly, attributes on the change line must always be in lower case. So for this attribute, you'd use change:serpent_mystik . If you use the attribute name anywhere else (in the getAttrs[] section, or later in the sheet worker), you use its name as listed in this sheet. So for a sheet worker that just used that attribute, you would have on('change:serpent_mystik', function() { getAttrs['SERPENT_Mystik', function(v) { For this reason it is recommended to have attribute names always in lower case, so you dont have to remember to treat them differently here. The third thing to correct: sections like this parseInt(PHENIX_0) Notice in the example i did, i had a v. before the attribute name. Sheet workers have known knowledge of the character sheet. When a sheet worker runs, the getAttrs function grabs the values of attributes you list, and stores them in the variable named inside the brackets. So here getAttrs['SERPENT_Mystik', function(values) { the getattrs function looks in the sheet, finds the SERPENT_Mystik attribute, and stores it in a variable called values . You can change the name to anything you want, people use v or values by custom. Then when you want to use that attribute, you need to get it out of the values object, and you do it like so let serpent = parseInt(values.SERPENT_Mystik ); In the earlier sheet worker I used v., but in this one i used values for the variable name, so i have to use values. to get its value. So with those things to correct, we can look at your worker and see if it actually does what you want. You have this in the middle of your expression: ( parseInt(values.SANGLIER_Corps) || parseInt(values.SANGLIER_Social) || parseInt(values.SANGLIER_Mystik)) +
(parseInt(values.DRAGON_Corps) || parseInt(values.DRAGON_Social) || parseInt(values.DRAGON_Mystik) )
+(parseInt(values.SERPENT_Corps) || parseInt(values.SERPENT_Social) || parseInt(values.SERPENT_Mystik) ) I added spaces and linebreaks to make it clearer what this is doing. In javascript, || is a logical OR operator. When you have a chain of || like this, the expression will use the first that has a meaningful value and ignore the rest. So for this ( parseInt(values.SANGLIER_Corps) || parseInt(values.SANGLIER_Social) || parseInt(values.SANGLIER_Mystik)) it will always use SANGLIER_Corps if it exists and is a number, and will ignore the other two. They will not be included in the calculation. Is that what you want to happen? When you have a big calculation with multiple attributes, its best to assign them to individual variables first, like this const aigle = parseInt(values.aigle_0) ||0; const singe = parseInt(values.singe_1_1) || 0; const guepard = parseInt(values.GUEPARD_1_1) || 0; If the calculation isnt working correctly, this allows you to check the values of individual attributes to make sure they are working properly. The || 0 at the end of each attribute might not be necessary. What it does is check if the value is valid, and if not, sets it to 0. So for instance, if GUEPARD_1_1 was not a valid number, the sheet worker would crash unless you have some thing to handle errors. Thats what the || 0 does - it stops a fatal error. If all these attributes are checkboxes, with value=1, this isnt necessary - since values can be only 0 or 1. But for any other kinds of input, it is necessary. So assuming the sanglier, dragon, and serpent sections are correct, and you can only ever only use 1 of each of them, this is what your corrected sheet worker might look like: on('change:aigle_0 change:singe_1_1 change:guepard_1_1 change:pegaze_0 change:phenix_0 change:sanglier_corps change:sanglier_social change:sanglier_mystik change:dragon_corps change:dragon_social change:dragon_mystik change:serpent_corps change:serpent_social change:serpent_mystik change:tortue_0 sheet:opened', function () { getAttrs(['aigle_0','singe_1_1','GUEPARD_1_1','PEGAZE_0','PHENIX_0','SANGLIER_Corps','SANGLIER_Social','SANGLIER_Mystik',' DRAGON_Corps','DRAGON_Social','DRAGON_Mystik','SERPENT_Corps','SERPENT_Social','SERPENT_Mystik','TORTUE_0'], function (values) { const aigle = parseInt(values.aigle_0) ||0; const singe = parseInt(values.singe_1_1) || 0; const guepard = parseInt(values.GUEPARD_1_1) || 0; const pegaze = parseInt(values.PEGAZE_0) || 0; const phenix = parseInt(values.PHENIX_0) || 0; const sanglier = (parseInt(values.SANGLIER_Corps) || 0) || (parseInt(values.SANGLIER_Social) || 0) || (parseInt(values.SANGLIER_Mystik) || 0); const dragon = (parseInt(values.DRAGON_Corps) || 0) || (parseInt(values.DRAGON_Social) || 0) || (parseInt(values.DRAGON_Mystik) || 0); const serpent = (parseInt(values.SERPENT_Corps) || 0) || (parseInt(values.SERPENT_Social) || 0) || (parseInt(values.SERPENT_Mystik) || 0); const tortue = parseInt(values.TORTUE_0) || 0; const sumTotal = (aigle + singe +guepard + pegaze + phenix + sanglier + dragon + serpent + tortue) *5; setAttrs({ totalAtout: sumTotal }); }); });