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

Custom character sheet problems (script help)

1598993220

Edited 1598993254
The current script I have is doing perfect for what it's coded for...which is increasing the stat bonus based on the amount in each block. I've been reading that you should try to keep it to just 1 script so here's me trying. <script type="text/worker"> const int = score => parseInt(score, 10) || 0; const stats = ["strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma", "defense"]; stats.forEach(stat => {     on("change:${stat} sheet:opened", () => {         getAttrs([stat], values => {             const stat_base = int(values[stat]);             console.log(stat_base);             let stat_bonus = 1;             if (stat_base >= 275) stat_bonus = "25";             else if (stat_base >= 255) stat_bonus = "24";             else if (stat_base >= 235) stat_bonus = "23";             else if (stat_base >= 215) stat_bonus = "22";             else if (stat_base >= 195) stat_bonus = "21";             else if (stat_base >= 175) stat_bonus = "20";             else if (stat_base >= 160) stat_bonus = "19";             else if (stat_base >= 145) stat_bonus = "18";             else if (stat_base >= 130) stat_bonus = "17";             else if (stat_base >= 115) stat_bonus = "16";             else if (stat_base >= 100) stat_bonus = "15";             else if (stat_base >= 90) stat_bonus = "14";             else if (stat_base >= 80) stat_bonus = "13";             else if (stat_base >= 70) stat_bonus = "12";             else if (stat_base >= 60) stat_bonus = "11";             else if (stat_base >= 50) stat_bonus = "10";             else if (stat_base >= 45) stat_bonus = "9";             else if (stat_base >= 40) stat_bonus = "8";             else if (stat_base >= 35) stat_bonus = "7";             else if (stat_base >= 30) stat_bonus = "6";             else if (stat_base >= 25) stat_bonus = "5";             else if (stat_base >= 20) stat_bonus = "4";             else if (stat_base >= 15) stat_bonus = "3";             else if (stat_base >= 10) stat_bonus = "2";             else if (stat_base >= 5) stat_bonus = "1";             else if (stat_base >= 0) stat_bonus = "1";             else stat_bonus = "1";                          setAttrs({                 [`${stat}_bonus`]: stat_bonus             });         });     }); }); </script> What I'm trying to add to this script is having abilities like stealth, investigation and arcane knowledge modifiers increase when points are put into the base stats that these abilities pull off of. On top of that, I have a weight system I saw and currently trying to use but the number value won't increase and change the corresponding.  What I have for that is this:             <fieldset class="repeating_inventory">                 <input type="hidden" class="equippedflag" name="attr_equipped">                 <div class="item">                     <input class="count" type="text" name="attr_itemcount" value="1" style="width: 70px;">                     <input class="name" type="text" name="attr_itemname" style="width: 200px;">                     <input class="weight" type="text" name="attr_itemweight" style="width: 50px;">                     <div class="subitem">                         <div class="row">                             <input class="equipped" type="checkbox" name="attr_equipped" value="1" checked="checked">                             <span class="equippedlabel">EQUIPPED</span>                             <input class="equipped" type="checkbox" name="attr_useasresource" value="1">                             <span class="equippedlabel">USE AS A RESOURCE</span>                             <input class="equipped" type="checkbox" name="attr_hasattack" value="1">                             <span class="equippedlabel">HAS AN ATTACK</span>                             <span class="label">MODS:</span>                             <input class="subfield" type="text" name="attr_itemmodifiers">                         </div>                         </div>                     <input type="hidden" name="attr_itemattackid">                     <input type="hidden" name="attr_itemresourceid">                 </div>             </fieldset>             <div class="weighttotal">                 <span class="label">TOTAL WEIGHT</span>                 <input type="text" name="attr_weighttotal" value="@{itemweight}">                 <input type="hidden" class="encumberance" name="attr_encumberance">             </div>             <div class="sheet-container">                 <input type="hidden" name="attr_overweight" value="" class="sheet-hidden sheet-overweight">                 <div class="sheet-overweight"></div>             </div> I tried putting the value for attr_overweight as @ {weighttotal} since the weighttotal is trying to pull off itemweight. Itemweight is for each individual piece to come up with the weighttotal. Is there something I'm missing or coding wrong?
1599003481

Edited 1599003664
Kavini
Roll20 Production Team
Marketplace Creator
Sheet Author
Compendium Curator
So, the first thing we can do is clean up your existing code a little using Ternary Operators . Also, you can remove an extraneous 'getAttrs' here (which is generally a good idea if possible) because the new value will be passed via eventInfo . It's also worth noting to make sure you're using template literals (`template literal`) when trying to include ${expressions}. You'd used a string ("string") which wouldn't have worked. const stats = ["strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma", "defense"]; const int = score => parseInt(score, 10) || 0; stats.forEach(stat => { on( `change:${stat} sheet:opened` , ( eventInfo ) => { const stat_base = int( eventInfo.newValue ); let stat_bonus = (stat_base >= 725) ? "25" : (stat_base >= 255) ? "24" : (stat_base >= 235) ? "23" : (stat_base >= 215) ? "22" : (stat_base >= 195) ? "21" : (stat_base >= 175) ? "20" : (stat_base >= 160) ? "19" : (stat_base >= 145) ? "18" : (stat_base >= 130) ? "17" : (stat_base >= 115) ? "16" : (stat_base >= 100) ? "15" : (stat_base >= 90) ? "14" : (stat_base >= 80) ? "13" : (stat_base >= 70) ? "12" : (stat_base >= 60) ? "11" : (stat_base >= 50) ? "10" : (stat_base >= 45) ? "9" : (stat_base >= 40) ? "8" : (stat_base >= 35) ? "7" : (stat_base >= 30) ? "6" : (stat_base >= 25) ? "5" : (stat_base >= 20) ? "4" : (stat_base >= 15) ? "3" : (stat_base >= 10) ? "2" : "1"; setAttrs({ [`${stat}_bonus`]: stat_bonus }); }); }); If you want to add some effects that are made on the basis of the bonus being updated, the simplest (and most reliable method) is to set up another event handler watching for those changes. i.e. on(`change:${stat}_bonus`, (eventInfo) => { const new_value = parseInt(eventInfo.newValue); // Do something with new_value. }); There are a number of ways to proverbially skin the cat when it comes to totaling a repeating section, but probably the easiest is to get all of the repeating ids for your inventory section and add them to a getAttrs, map the values into ints, and then reduce the array: on(`change:repeating_inventory`, () => { getSectionIDs("equipment", (id_array) => { const weights = []; id_array.forEach(id => weights.push(`repeating_inventory_${id}_itemweight`)); getAttrs(weights, values => { const total = values.map(item => parseInt(item) || 0) .reduce((a,b) => a+b); setAttrs({weighttotal:total}); }); }); });
Please forgive me for my lack of information and some meanings. I've just recently started working with HTML and all this coding stuff a little over a week now. The current script I have is a copy (altered of course to fix what I needed) from a test html. All i really did was change the names of the stats and added a few extra as well as the value change. As for the template literals, it originally had the ' ' around change put I started reading others and it showed " " so I changed it thinking it was a mess up. I can definitely change it back. Adding a new event handler would still be under the current script right? Alot of these words are still confusing and I'm trying to learn the meaning and proper use of them. The script and rolltemplate are my biggest headaches right now.
1599012292

Edited 1599012538
GiGs
Pro
Sheet Author
API Scripter
Devildog said: As for the template literals, it originally had the ' ' around change put I started reading others and it showed " " so I changed it thinking it was a mess up. I can definitely change it back. Note that template literals use backticks: ` , these are different from both single quotes  ' and double quotes  " . You'll find the backtick on the number line of your keyboard, to the left of the number 1 (at least on a UK keyboard, not sure if other keyboards are the same). Adding a new event handler would still be under the current script right? Yes, always add all sheet workers inside the same script block. I'd also differ in some of my advice: Nic recommended using eventInfo, but for a long, long time, there was a bug with eventInfo.newValue (if you used API scripts to update a character sheet, any worker using eventInfo would fail), so I'm wary of relying on it. Has it been fixed? That is a very elegant way of calculating the repeating section weights though.
Sorry about that GiGs, It originally was the ` key. I know its only opposite end of the keyboard but it was a typo lol. I think I'm missing a key step or something with the weight event. Am I suppose to add any values or anything to that piece of script?
1599021857
GiGs
Pro
Sheet Author
API Scripter
The code had two errors in it. Both very easy errors to make. First, the getSectionIDs was targeting "equipment", it should have been targeting "inventory". Second, it tries to use map on the values object, but you need to convert that into an array first. Here's the fixed code:     on(`change:repeating_inventory`, () => {         getSectionIDs("repeating_inventory", (id_array) => {             const weights = [];             id_array.forEach(id => weights.push(`repeating_inventory_${id}_itemweight`));             getAttrs(weights, values => {                 const total = Object.values(values).map(item => parseInt(item) || 0)                                     .reduce((a,b) => a+b);                 setAttrs({weighttotal:total});             });         });     }); Note that I used          getSectionIDs("repeating_inventory", (id_array) => { but you could use         getSectionIDs("inventory", (id_array) => { I prefer the former. I have a vague memory of encountering a problem when not using repeating_ in some situation, but I cant remember what it was, so it's mainly personal preference at this point.
1599023781

Edited 1599024567
Changing it to inventory definitely fixed the issue for calculating Total Weight. Thank you for that. Also, I'll take your word for the repeating_  part is I have no knowledge of this kinda thing. How does one go about converting it into an array? Just type (id_array) or is there something extra you have to do? Just so I know for future reference and encase I need to sure it for another handler, whats the difference between the gets? part. I understand getattrs is getting anything with the attr_ tag, but what does the getSectionIDs gather? And are they others you could use? I'm assuming now that I have to total weight event handler entered in, I need to convert that to the attr_overweight bar? Something like:     on(`change:weighttotal`, () => {         getSectionIDs("weighttotal", (total) => {             let weighttotal = parsInt(values.weighttotal)||0;             let bagsize = weighttotal             setAttrs({                 "overweight": bagsize             });         });     });   Or is this wrong?
1599024903
GiGs
Pro
Sheet Author
API Scripter
You only use getSectionIDs for repeating sections, where you need to gather values from more than one row of the section. Attributes in a repeating section have more complicated names than normal attributes. Where a normal attribute looks like 'attribute_name', an attribute in a repeating section looks like 'repeating_section_ROWID_attribute_name'. getSectionIDs gets the row id for all rows in a section, so you can then do things with them. For your worker above, as with nearly all situations, you should just use getAttrs (or the eventInfo method Nic suggested, if you trust it). So your code could be  on(`change:weighttotal`, () => {         getAttrs("weighttotal",  values  => {             let weighttotal = int(values.weighttotal);             let bagsize = weighttotal;             setAttrs({                 "overweight": bagsize             });         });     });  HOWEVER if you have an attribute that is based on weight total, and is equal to it, you should set it at the same time as weighttotal. Like so:  on(`change:repeating_inventory`, () => {         getSectionIDs("repeating_inventory", (id_array) => {             const weights = [];             id_array.forEach(id => weights.push(`repeating_inventory_${id}_itemweight`));             getAttrs(weights, values => {                 const total = Object.values(values).map(item => parseInt(item) || 0)                                     .reduce((a,b) => a+b);                 setAttrs({                      weighttotal:total,                                  overweight: total                 });             });         });     }); Do you need two stats that are identical?
It's not as much as an identical issue. It's suppose to be a visual aid to the players saying hey, your bag is this full. Might want to stop picking up trash and/or sell somethings. I manually changed the bar to show the current value of the total weight in the bar. Was trying to make it where the bar would auto calculate to the total weight. Otherwise I'll have to get rid of the bar all together.
1599065152

Edited 1599065164
Kavini
Roll20 Production Team
Marketplace Creator
Sheet Author
Compendium Curator
Whoops! Nice spot GiGs; this is why you should test your code before asserting it works! GiGs said: The code had two errors in it. Both very easy errors to make. First, the getSectionIDs was targeting "equipment", it should have been targeting "inventory". Second, it tries to use map on the values object, but you need to convert that into an array first. Devil, I assume the thing controlling the bar at the bottom is some sort of CSS styling? We would need to see the CSS to properly identify what you need to do here.
1599067756

Edited 1599068594
I acquired the coding for it from the CSS Wizardry progression bar tool. I have altered it to fit what I needed it for and just tried changing the sheet name to match the weighttotal but with no luck. Looking back at it, going off the weighttotal would but everyone at the same carry capacity which is not what I'm wanting. I need to make it to where the progress bar would show the attr_carry_weight since every players weight would be different. But heres the css I have for it div.sheet-weighttotal {     width: 90%;     height: 20px;     border: 1px solid black;     color: black;     text-align: center; } input.sheet-weighttotal[value="0"] ~ div.sheet-weighttotal {     background: white; } input.sheet-weighttotal[value="0-49"] ~ div.sheet-weighttotal {     background: linear-gradient(to left, white 70%, green 75%);     }     input.sheet-weighttotal[value="50"] ~ div.sheet-weighttotal:before {         content:"Bags quarter full";     } input.sheet-weighttotal[value="51-99"] ~ div.sheet-weighttotal {     background: linear-gradient(to left, white 50%, yellow 45%); }     input.sheet-weighttotal[value="100"] ~ div.sheet-weighttotal:before {         content:"Bags half full";     } input.sheet-weighttotal[value="101-149"] ~ div.sheet-weighttotal {     background: linear-gradient(to left, white 15%, orange 35%); }     input.sheet-weighttotal[value="150"] ~ div.sheet-weighttotal:before {         content: "Bags nearly full";     } input.sheet-weighttotal[value="151-199"] ~ div.sheet-weighttotal {     background: linear-gradient(to left, white 0%, red 0%); }     input.sheet-weighttotal[value="200"] ~ div.sheet-weighttotal:before {         content: "Bags full !";     } input.sheet-weighttotal[value="201"] ~ div.sheet-weighttotal {     background: black;     color: white; }     input.sheet-weighttotal[value="201"] ~ div.sheet-weighttotal:before {         content: "Bags too heavy ! Can't move."; } 
1599109800
GiGs
Pro
Sheet Author
API Scripter
You cant do this: input.sheet-weighttotal[value="51-99"]  CSS matches only the text value, and you cant use a range of values. You need to explicitly list each value (you could use a comma separated list and an appropriate match method, but for 51-99, thats a lot of values). The appropriate way in roll20 to handle this is to have a sheet worker which creates a new attribute value, so when weighttotal is in the range 51-99, it might create weight_css = 5. (Just an example). Then your css rule would match against whatever class the weight_css attribute has, maybe weight-css, which would look like: weight-css[value="5"].
1599118421

Edited 1599118534
GiGs
Pro
Sheet Author
API Scripter
One that that has been bothering me about the first script, and my mild OCD wont let it lie: Nic made this excellent tweak to the bonus calculation: let stat_bonus = (stat_base >= 725) ? "25" : (stat_base >= 255) ? "24" : (stat_base >= 235) ? "23" : (stat_base >= 215) ? "22" : (stat_base >= 195) ? "21" : (stat_base >= 175) ? "20" : (stat_base >= 160) ? "19" : (stat_base >= 145) ? "18" : (stat_base >= 130) ? "17" : (stat_base >= 115) ? "16" : (stat_base >= 100) ? "15" : (stat_base >= 90) ? "14" : (stat_base >= 80) ? "13" : (stat_base >= 70) ? "12" : (stat_base >= 60) ? "11" : (stat_base >= 50) ? "10" : (stat_base >= 45) ? "9" : (stat_base >= 40) ? "8" : (stat_base >= 35) ? "7" : (stat_base >= 30) ? "6" : (stat_base >= 25) ? "5" : (stat_base >= 20) ? "4" : (stat_base >= 15) ? "3" : (stat_base >= 10) ? "2" : "1"; This is a great way to streamline the if statement, but you can go further. Notice how the bands are separated by units of 5, then units of 10, then units of 15, and finally units of 20 (working from the bottom up), but the value goes up by 1 each time? This points to a way to make the assignment a fair bit more concise: let stat_bonus = (stat_base >= 725) ? 25 :         (stat_base >= 175) ? Math.floor((stat_base-175)/20) + 20 :         (stat_base >= 100) ? Math.floor((stat_base-100)/15) + 15 :         (stat_base >= 50)  ? Math.floor((stat_base-50)/10) + 10 :         (stat_base >= 10)  ? Math.floor(stat_base/5) :         1; Also, I didnt set them as strings - just in case you needed to do arithmetic with them. The value is never 0, so no need to worry about special effects, and it will get turned to a string when it's saved back to the character sheet anyway.
That would help out alot. I'm currently at 680 lines so cleaning up a bit would be nice. So would I be able to use the same principle for the weighttotal progress bar like with the stat bonus? Something like this or would I have to double to total? I figured it'd be pointless to divide by 1 right?     on(`weighttotal`, () => {         getAttrs("weighttotal", (values) => {             let total = (total >= 200) ? 4:                 (total >= 150) ? math.floor((total-150)/2) 3:                 (total >= 100) ? math.floor((total-100)/2) 2:                 (total >= 50) ? math.floor((total-50)/2) 1:                 0;             setAttrs({                  weighttotal:total             });         });          });   });
1599149689
GiGs
Pro
Sheet Author
API Scripter
You have a bunch of problems there: First, this is invalid syntax: math.floor((total-150)/2) 3 you need something before the 3 there, like a + or -, or whatever. Second, I can't say whether that calculation is correct because you havent described what it's goal is. But third, and by far most important: The attribute you are changing in setAttrs, is the attribute that triggers it in the on line. Luckily the syntax is incorrect there - it should be on('change:weightotal' But dont do that. You would create an infinite loop: weighttotal changes, triggering this sheet worker, which changes weighttotal, which triggers this sheet worker, which changes weighttotal, which triggers this sheet worker, and so on for ever. There's a way to avoid that, but it's better if you describe what you are trying to do here, so we can make sure its right. For the record, the getAttrs line is missing [ ] brackets too.
Must have deleted a few things as I was retyping the correct info. I was trying to create an event handler for the progress bar to show how full someones backpack is. Like I mentioned before, looking back at it, I'll have to change the value because everyones carrying weight will be different. With that being said, is there a way, using an event handler or such, to have the carrying weight of each individual show up on a progress bar to let them know they are reaching their limit? Say player A has their racial bonus for carry weight of 75lbs. plus 2lbs per strength point giving them an extra 50lbs with a total of 125lbs they can carry. If they acquire 100lbs in loot, how can I get the weight bar to show them they are getting close to their max weight before being unable to move.
1599211558
GiGs
Pro
Sheet Author
API Scripter
I'm not sure the gradations you want to use because of the way you have written the CSS. Example: input.sheet-weighttotal[value="0"] ~ div.sheet-weighttotal {     background: white; } input.sheet-weighttotal[value="0-49"] ~ div.sheet-weighttotal {     background: linear-gradient(to left, white 70%, green 75%);     } input.sheet-weighttotal[value="50"] ~ div.sheet-weighttotal:before {         content:"Bags quarter full"; } input.sheet-weighttotal[value="51-99"] ~ div.sheet-weighttotal {     background: linear-gradient(to left, white 50%, yellow 45%); } You are alternating between rules for background and coentent, but have different values (value 50, then 51-99) for each of those rules. Are they supposed to be same values for the content and background rules?
1599212056
GiGs
Pro
Sheet Author
API Scripter
Devildog said: Say player A has their racial bonus for carry weight of 75lbs. plus 2lbs per strength point giving them an extra 50lbs with a total of 125lbs they can carry. If they acquire 100lbs in loot, how can I get the weight bar to show them they are getting close to their max weight before being unable to move. You also need to create a weightlimit , or carrying_capacity, or similar attribute, to hold the weight they can carry. That would be created with something like: on(`change:strength change:racial_lift`, () => {         getAttrs(["strength", "racial_lift"],  values  => {             let str = int(values.strength);             let race = int(values.racial_lift);             let carry = race + str *2;             setAttrs({                 load_capacity: carry             });         });     });  In this, I assume you have a strength attribute named strength , a race-based bonus named racial_lift , and it gets saved to a an attribute called load_capacity . Your stat names will likely be different and so you'd need to change every occurrence of those names in the above to match. If you change any names on the first line (with the change: parts), write them in lower case on this line no matter how the attribute is actually defined. Once you have the carrying capacity, you can then work on comparing the amount actually carried (the weighttotal) to it.
1599240869

Edited 1599240993
You also need to create a weightlimit , or carrying_capacity, or similar attribute, to hold the weight they can carry. I already have an attribute called carry_weight as a roll function for now like this: <div class="ability">                         <input name="attr_carry_weight" type="number" value="0">                         <button type="roll" name="roll_carry-weight" value="&{template:default} {{name=Carry Weight}} {{result=[[@{carry_weight}+(@{strength}*2)]]}}">Carry Weight</button>                     </div> In this, I assume you have a strength attribute named strength , a race-based bonus named racial_lift , and it gets saved to a an attribute called load_capacity . As for the race-based bonus...I'm not sure how to go about setting those for each individual race. For the CSS, I was trying to set it to where the bar would be color coded but the words like Bag quarter full would only appear at a certain value.
I still can't seem to get the concept of making scripts work properly. When I try to add a new one, the previous on changes stop working. I've tried various formulas that I've been provided for the previous changes. What I currently have an issue with is this: on(`change:defense change:constitution change:armorbonus`, (eventInfo) => {     const defense = int(eventInfo.newValue);     let defense_bonus = (defense >= 550) ? 50 :     (defense >= 350) ? Math.floor((defense-350)/40) + 40 :     (defense >= 200) ? Math.floor((defense-200)/30) + 30 :     (defense >= 100)  ? Math.floor((defense-100)/20) + 20 :     (defense >= 20)  ? Math.floor(defense/10) :     1;     let defense = contitution + armorbonus     setAttrs({         [`defense_bonus`]: defense_bonus         [`defense`]: defense     });     }); I've added in change: constitution change: armorbonus as well as the defense=con+armorbonus and [`defense`]: defense. Without these parts, the script and functions work perfectly. I've tried creating a new on change work order for just this added part as well as what you see above. Neither option worked and made the values unable to change. What I'm trying to incorporate is when constitution or the armor bonus of a players gear changes, it'll change the base value of the defense to in turn, create the corresponding defense bonus that will be rolled. I'm chalking it up to me not properly coding things since I still don't know everything about sheetworker. Can any of you by chance see the issue or do I need to provide more info?
1599684906

Edited 1599684956
This is the other way I've tried it: on("change:constitution change:armorbonus", () {     getAttrs("constitution", "armorbonus", values => {         let constitution = int(eventInfo.newValue);         let armorbonus = int(eventInfo.newValue);         let defense = constitution + armorbonus;         setAttrs({             [`defense`]: defense         });        });    });
1599707627

Edited 1600156384
GiGs
Pro
Sheet Author
API Scripter
There are a couple of problems with your first sheet worker. Remember How I recommended use of getAttrs early on? It was precisely because relying on eventInfo would fail you very quickly. Forget about using eventInfo - thats something to use when you are more comfortable with how sheet workers work. I'm going to give you a template, and all your sheet workers should be built like this: on('change:attribute1 change:attribute2',  () => {     getAttrs(['attribute1', 'attribute2'], values => {             let variable1 = parseInt(values.attribute1) || 0;         let variable2 = parseInt(values.attribute2) || 0;         /* do some work here - the following bit will vary in every worker */         let variable3 = variable1 + variable2;         /* now the work is over, back to the stuff that will look the same in each worker */              setAttrs({             attribute3: variable3         });     }); }); So, look at the structure of this worker. It starts with an event line (on('change:attribute')). This line is telling roll20 what attributes in the character sheet to keep an eye on. Whenever any of those attributes change, the sheet worker will spring into life. The next line is the getAttrs function: that stands for "Get Attributes" and is exactly what it does. You give it a list of attribute names, and it looks at the character sheet, finds those attributes, and stores their names and values  in a special variable called values .  This is because sheet workers have no knowledge of the character sheet. You must tell it what attributes to work with, and the getAttrs scans the sheet and gets their names and values. The next step is to extract those attributes from the values object, so you can actually use them. Thats what the next lines do: let variable1 = parseInt(values.attribute1) || 0;         This line is grabbing the attribute1 score from the values object. parseInt here is making sure it is a number. All of the steps up to this point are just set up. They are to get worker to the point where you can actually do things with attributes. Now you can do whatever calculations you need, and then the setAttr() function finishes things off - by updating the character sheet with new values. So, lets look at your second sheet worker, and what isn't quite right: on("change:constitution change:armorbonus", () {     getAttrs(["constitution", "armorbonus"], values => {         let constitution = int(eventInfo.newValue);         let armorbonus = int(eventInfo.newValue);         let defense = constitution + armorbonus;         setAttrs({             [`defense`]: defense         });        });    }); This is almost correct. Firstly though, your getAttrs function isn't quite right. The attributes you list must be in an array - that's a special kind of set. You show that by putting square brackets around them: [ ]. Then your variable assignments are using eventInfo instead of values. Those lines should look like this:         let constitution = int(values.newValue);         let armorbonus = int(values.newValue); Note that for this to work you must have an int function already declared in your script block. From your first post, it looks like you do. Just bear in mind, the int function is just doing the work of the parseInt function in my previous explanation - they are doing the same thing. You could add your defense bonus calculation from the previous worker, so it would look like this: on("change:constitution change:armorbonus", () => {     getAttrs(["constitution", "armorbonus"], values => {         let constitution = int(values.newValue);         let armorbonus = int(values.newValue);         let defense = constitution + armorbonus;             let defense_bonus = (defense >= 550) ? 50 :         (defense >= 350) ? Math.floor((defense-350)/40) + 40 :         (defense >= 200) ? Math.floor((defense-200)/30) + 30 :         (defense >= 100)  ? Math.floor((defense-100)/20) + 20 :         (defense >= 20)  ? Math.floor(defense/10) :         1;         setAttrs({             defense: defense,              defense_bonus: defense_bonus         });        });    }); Notice two things about the setAttrs section: the left hand attributes do not need to be surrounded with [' ']. There are situations you need to use that syntax, but when you are just using attribute names like this, you dont need them. Secondly, when you have more than one attribute being assigned, you need to separate them with a comma. This format will work for 90%+ of your sheet workers. Just make sure you get the first parts and last part correct. Edit:  corrected some syntax errors in the second worker that were not caught from the original code.
How would I know when you use this let variable1 = parseInt(values.attribute1) || 0; instead of this let armorbonus = int(eventInfo.newValue); or are they the same? I guess my question is what is || 0; used for?
1599759178
GiGs
Pro
Sheet Author
API Scripter
If you look at the int function, it has an || 0 in it. || 0 says (essentially) "if the value before this is an error, use 0 instead" parseInt(something) tries to turn a value into a number. Remember you are feeding it attribute values, and attriute values might be words, or even empty, which is not a number. If parseInt cant turn a value into a number, it will cause an error and crash the sheet worker. Hence the reason to add || 0 after it. so this expression let variable1 = parseInt(values.attribute1) || 0; gets the value of the attribute1 attribute, converts into a number, and if its not a number, sets variable1 to 0. Since you have the int fucntion in your code, you can do let variable1 = int(values.attribute1); and it will do exactly the same. I created the int function as a way to avoid having to write  parseInt(something) || 0; all the time. int(something) is a lot shorter.
Gotcha. So as long as I have the int function coded already, I no longer need the parseInt and just need to use int. Don't use eventInfo cause it doesn't like to always work properly unless I get better at the sheet workers. Use int(values.attribute); One last question about this...I hope x.x When you said I can combine the 2 into one sheetworker, do I not have to include defense in the on(change  for the defense bonus variables? 
1599761995

Edited 1599762020
GiGs
Pro
Sheet Author
API Scripter
Devildog said: Don't use eventInfo cause it doesn't like to always work properly unless I get better at the sheet workers. Use int(values.attribute); The main reason not to use eventInfo is because it can only give you information on one attribute. It gives you information about the attribute that just changed to trigger the sheet worker, but in these sheet workers you need information on multiple attributes. My earlier objection about eventInfo being unreliable in certain situations is a reason I avoid using it, but in this specific case, it's the fact you need information on multiple attributes, eventInfo cannot do the job you need even if it works properly. Devildog  said: When you said I can combine the 2 into one sheetworker, do I not have to include defense in the on(change  for the defense bonus variables?  The way you have it set up, defense is being calculated by two other stats. Since those stats determine the value of defense, you dont need to monitor defense directly. You just recalculate it in the sheet worker every time it changes, and then use that updated value to calculate the bonus. Then you update the defence and bonus stats simultaneously. This is more efficient than calculating defense, and then defense bonus separately in another worker.
That makes alot more sense now. I've scoured all over the sheet worker wiki looking for anything I've missed. This has helped out alot and I think I have a better grasp on the outline of it now. Thank you for all the help GiGs. 
Well, I just tried adding in the new sheet worker how you suggested but the stat bonuses from the previous on change quit working. Is that because constitution is in both on change?
The whole sheet work looks as such: <script type="text/worker"> const stats = ["strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma"]; const int = score => parseInt(score, 10) || 0; stats.forEach(stat => { on(`change:${stat}`, (eventInfo) => { const stat_base = int(eventInfo.newValue); let stat_bonus = (stat_base >= 275) ? 25 : (stat_base >= 175) ? Math.floor((stat_base-175)/20) + 20 : (stat_base >= 100) ? Math.floor((stat_base-100)/15) + 15 : (stat_base >= 50) ? Math.floor((stat_base-50)/10) + 10 : (stat_base >= 10) ? Math.floor(stat_base/5) : 1; setAttrs({ [`${stat}_bonus`]: stat_bonus }); }); }); on("change:constitution change:armorbonus", () { getAttrs("constitution", "armorbonus", values => { let constitution = int(values.newValue); let armorbonus = int(values.newValue); let defense = constitution + armorbonus; let defense_bonus = (defense >= 550) ? 50 : (defense >= 350) ? Math.floor((defense-350)/40) + 40 : (defense >= 200) ? Math.floor((defense-200)/30) + 30 : (defense >= 100) ? Math.floor((defense-100)/20) + 20 : (defense >= 20) ? Math.floor(defense/10) : 1; setAttrs({ defense: defense, defense_bonus: defense_bonus }); }); }); </script>
1599764267
GiGs
Pro
Sheet Author
API Scripter
Its because you have a syntax error on this line: on("change:constitution change:armorbonus", () { It should be on("change:constitution change:armorbonus", () => {
1599764691
GiGs
Pro
Sheet Author
API Scripter
Also, not a syntax error, but you might want to check your defence bonus progression: let defense_bonus = (defense >= 550) ? 50 : (defense >= 350) ? Math.floor((defense-350)/40) + 40 : (defense >= 200) ? Math.floor((defense-200)/30) + 30 : (defense >= 100) ? Math.floor((defense-100)/20) + 20 : (defense >= 20) ? Math.floor(defense/10) : 1; For this to be accurate, the thresholds of each level should generate the same score. So at 100 defence, you should get the same total by the >20 rule, and the > 100 rule. Under the >=20 rule, 100 defence gives a score of 10. Under the >= 100 rule, it gives a score of 20. Likewise the >=100 rule and >=200 rule should give the same score with 200 defence, but they give 25 and 30 respectively. And finally the >=200 and >=350 rules should give the same score with 350 defence, and the give 35 and 40 respectively.  So something is wrong with that progression. Maybe it should be let defense_bonus = (defense >= 550) ? 50 : (defense >= 350) ? Math.floor((defense-350)/40) + 20 : (defense >= 200) ? Math.floor((defense-200)/30) + 15 : (defense >= 100) ? Math.floor((defense-100)/20) + 10 : (defense >= 20) ? Math.floor(defense/10) : 1; But I'd have to see the original table to work it out properly.
1599765147

Edited 1599765187
The progression for the defense is as such: every 10 points up to 100 will give you 1 bonus. The value increases by 10 every 5 bonus points after the first 100. So it'll be 20 points from 100 to 200. 30 points from 200 to 350 and so on.  It's like the main stats but instead of 5 every change it's 10
1599766400
GiGs
Pro
Sheet Author
API Scripter
That sounds like this is correct, then (though I missed the defence>= 550 in my previous post - corrected here): let defense_bonus = (defense >= 550) ? 25 : (defense >= 350) ? Math.floor((defense-350)/40) + 20 : (defense >= 200) ? Math.floor((defense-200)/30) + 15 : (defense >= 100) ? Math.floor((defense-100)/20) + 10 : (defense >= 20) ? Math.floor(defense/10) : 1;
Okay, I still seem to be running into an issue with this defense value stuff. I'm currently using the script as such like it was suggested. on("change:constitution change:armorbonus", () => { getAttrs("constitution", "armorbonus", values => { let constitution = int(values.newValue); let armorbonus = int(values.newValue); let defense = constitution + armorbonus; let defense_bonus = (defense >= 550) ? 50 : (defense >= 350) ? Math.floor((defense-350)/40) + 40 : (defense >= 200) ? Math.floor((defense-200)/30) + 30 : (defense >= 100) ? Math.floor((defense-100)/20) + 20 : (defense >= 20) ? Math.floor(defense/10) : 1; setAttrs({ defense: defense, defense_bonus: defense_bonus }); }); }); This is still not changing the value of Defense when either constitution or armorbonus is changed. Would it have something to do with the actual html coded part for defense? What I have for that is this: <input name="attr_defense" type="number" value="" style="width: 50px"> <button type="roll" name="roll_defense" value="&{template:default} {{name=Defense}} {{result=[[{@{defense_bonus}d6!}>5f<1[DEF]]]}} {{Resist=@{element}}} {{Resist Total=[[@{element_resist}d6]]}}" class="txt-btn medium"><span>Defense</span></button> <span class="attr" name="attr_defense_bonus" title="@{defense_bonus}">0</span> I have a feeling this might be my issue but not sure which part. It looks just like the attributes such as str and dex so can't really figure out where I'm going wrong. Would it be the type="number" in input name that is cause it not to work? If so what would I have to put in its place in order for the sheet worker to take affect?
1600156235
GiGs
Pro
Sheet Author
API Scripter
That html is fine. There is a problem in your sheetworker. the getAttrs line should be getAttrs(["constitution", "armorbonus"], values =&gt; { I described that in this previous post:&nbsp; <a href="https://app.roll20.net/forum/permalink/9165513/" rel="nofollow">https://app.roll20.net/forum/permalink/9165513/</a> But immediately after my code didnt include that, oops. I'll update that post.
So far, I've managed to figure out how to get the defense value to change when increasing con and armor. Sadly, it has broken the bonus from changing when it reaches a certain point. The coding for this is as follows: &lt;div class="header"&gt; &lt;div class="header-info sheet-display"&gt; &lt;div class="sheet-row"&gt; &lt;div class="hlabel-container"&gt; &lt;span class="label"&gt;Alignment&lt;/span&gt; &lt;input type="text" name="attr_alignment" style="width: 15%;"&gt; &lt;span&gt;Hit Point&lt;/span&gt; Current&lt;input name="attr_hit_points" style="width: 60px" type="number" value=""&gt;Max&lt;input name="attr_hit_points_max" style="width: 60px" type="number" disabled="disabled" value="[[@{constitution}*10]]"&gt; &lt;br&gt; &lt;button name="roll_init" type="roll" value="&amp;{template:default} {{name=Initiative}} {{result=[[1d20+(@{dexterity_bonus}) &amp;{tracker}]]}}" class="txt-btn medium"&gt;&lt;span&gt;Initiative&lt;/span&gt;&lt;/button&gt; &lt;span class="label" style="text-align: right;"&gt;Movement&lt;/span&gt; &lt;input type="text" name="attr_movement" style="width: 60px"&gt; &lt;span&gt;Defense&lt;/span&gt; &lt;input name="attr_defense" type="number" disabled="disabled" value="[[@{constitution}+@{totalarmor}]]" style="width: 50px"&gt; &lt;button type="roll" name="roll_defense" value="&amp;{template:default} {{name=Defense}} {{result=[[{@{defense_bonus}d6!}&gt;5f&lt;1[DEF]]]}} {{Resist=@{element}}} {{Resist Total=[[@{element_resist}d6]]}}" class="txt-btn medium"&gt;&lt;span&gt;Defense&lt;/span&gt;&lt;/button&gt; &lt;span class="attr" name="attr_defense_bonus" title="@{defense_bonus}"&gt;0&lt;/span&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;br&gt; &lt;div class="3colrow"&gt; &lt;div class="col"&gt; &lt;span class="label" style="text-align: center;"&gt;ATTRIBUTES&lt;/span&gt; &lt;br&gt; &lt;br&gt; &lt;div class="attributes-container" style="margin-top 30px;"&gt; &lt;h1&gt;&lt;div class="attr_container"&gt; &lt;button type="roll" name="roll_strength" value="&amp;{template:default} {{name=Strength}} {{mod=@{strength_bonus}}} {{result=[[{@{strength_bonus}d6!}&gt;5f&lt;1[STR]]]}}" class="txt-btn medium"&gt;Strength&lt;/button&gt; &lt;input name="attr_strength" type="number" value="1"&gt; &lt;span class="attr" name="attr_strength_bonus" title="@{strength_bonus}"&gt;0&lt;/span&gt; &lt;/div&gt; &lt;br&gt; &lt;/h1&gt; &lt;h2&gt; &lt;br&gt; &lt;div class="attr_container"&gt; &lt;button type="roll" name="roll_dexterity" value="&amp;{template:default} {{name=Dexterity}} {{mod=@{dexterity_bonus}}} {{result=[[{@{dexterity_bonus}d6!}&gt;5f&lt;1[DEX]]]}}" class="txt-btn medium"&gt;Dexterity&lt;/button&gt; &lt;input name="attr_dexterity" type="number" value="1"&gt; &lt;span class="attr" name="attr_dexterity_bonus" title="@{dexterity_bonus}"&gt;0&lt;/span&gt; &lt;/div&gt; &lt;br&gt; &lt;/h2&gt; &lt;h3&gt; &lt;div class="attr_container"&gt; &lt;button type="roll" name="roll_constitution" value="&amp;{template:default} {{name=Constitution}} {{mod=@{constitution_bonus}}} {{result=[[{@{constitution_bonus}d6!}&gt;5f&lt;1[CON]]]}}" class="txt-btn medium"&gt;Constitution&lt;/button&gt; &lt;input name="attr_constitution" type="number" value="1"&gt; &lt;span class="attr" name="attr_constitution_bonus" title="@{constitution_bonus}"&gt;0&lt;/span&gt; &lt;/div&gt; &lt;br&gt; &lt;/h3&gt; &lt;h4&gt; &lt;br&gt; &lt;br&gt; &lt;div class="attr_container"&gt; &lt;button type="roll" name="roll_intelligence" value="&amp;{template:default} {{name=Intelligence}} {{mod=@{intelligence_bonus}}} {{result=[[{@{intelligence_bonus}d6!}&gt;5f&lt;1[INT]]]}}" class="txt-btn medium"&gt;Intelligence&lt;/button&gt; &lt;input name="attr_intelligence" type="number" value="1"&gt; &lt;span class="attr" name="attr_intelligence_bonus" title="@{intelligence_bonus}"&gt;0&lt;/span&gt; &lt;/div&gt; &lt;br&gt; &lt;br&gt; &lt;br&gt; &lt;/h4&gt; &lt;h5&gt; &lt;div class="attr_container"&gt; &lt;button type="roll" name="roll_wisdom" value="&amp;{template:default} {{name=Wisdom}} {{mod=@{wisdom_bonus}}} {{result=[[{@{wisdom_bonus}d6!}&gt;5f&lt;1[WIS]]]}}" class="txt-btn medium"&gt;Wisdom&lt;/button&gt; &lt;input name="attr_wisdom" type="number" value="1"&gt; &lt;span class="attr" name="attr_wisdom_bonus" title="@{wisdom_bonus}"&gt;0&lt;/span&gt; &lt;/div&gt; &lt;br&gt; &lt;br&gt; &lt;/h5&gt; &lt;h6&gt; &lt;div class="attr_container"&gt; &lt;button type="roll" name="roll_charisma" value="&amp;{template:default} {{name=Charisma}} {{mod=@{charisma_bonus}}} {{result=[[{@{charisma_bonus}d6!}&gt;5f&lt;1[CHA]]]}}" class="txt-btn medium"&gt;Charisma&lt;/button&gt; &lt;input name="attr_charisma" type="number" value="1"&gt; &lt;span class="attr" name="attr_charisma_bonus" title="@{charisma_bonus}"&gt;0&lt;/span&gt; &lt;/div&gt; &lt;br&gt; &lt;br&gt; &lt;/h6&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class="armors-container armor-list"&gt; &lt;div class="armor"&gt; &lt;fieldset class="repeating_armor"&gt; &lt;input type="hidden" class="defendingflag" name="attr_defending"&gt; &lt;div class="load section flex-center flex-middle flex-down"&gt; &lt;span class="label" style="text-align: center;"&gt;Armor&lt;/span&gt; &lt;br&gt; &lt;br&gt; &lt;span&gt; &lt;input name="attr_armor_name" type="text" placeholder="Armor" style=" width: 100px;"&gt; &lt;input type="hidden" name="attr_armorproperty_display" class="sheet-flag"&gt; &lt;select class="hiding armorproperty" name="attr_armorproperty" style="width: 80px;"&gt; &lt;option value="Property"&gt;Property&lt;/option&gt; &lt;option value="@{lightarmor}"&gt;Light&lt;/option&gt; &lt;option value="@{mediumarmor}"&gt;Medium&lt;/option&gt; &lt;option value="@{heavyarmor}"&gt;Heavy&lt;/option&gt; &lt;/select&gt; &lt;span&gt;Value&lt;/span&gt; &lt;input name="attr_armorbonus" type="number" value="" class="small" style="width: 40px;"&gt; &lt;button name="roll_armorbonus" type="roll" value="[[@{base_level}d@{armorproperty}]]" class="txt-btn medium"&gt;Armor Value&lt;/button&gt; &lt;select class="hiding stat bonus" name="attr_stat_bonus" style="width: 100px;" value=""&gt; &lt;option value="Normal"&gt;Normal&lt;/option&gt; &lt;option value="strength"&gt;Strength&lt;/option&gt; &lt;option value="dexterity"&gt;Dexterity&lt;/option&gt; &lt;option value="constitution"&gt;Constitution&lt;/option&gt; &lt;option value="intelligence"&gt;Intelligence&lt;/option&gt; &lt;option value="wisdom"&gt;Wisdom&lt;/option&gt; &lt;option value="crit rate"&gt;Crit Rate&lt;/option&gt; &lt;option value="strength/dexterity"&gt;Str/Dex&lt;/option&gt; &lt;option value="strength/constitution"&gt;Str/Con&lt;/option&gt; &lt;option value="strength/intelligence"&gt;Str/Int&lt;/option&gt; &lt;option value="strength/wisdom"&gt;Str/Wis&lt;/option&gt; &lt;option value="strength/crit rate"&gt;Str/Crit Rate&lt;/option&gt; &lt;option value="dexterity/constitution"&gt;Dex/Con&lt;/option&gt; &lt;option value="dexterity/intelligence"&gt;Dex/Int&lt;/option&gt; &lt;option value="dexterity/wisdom"&gt;Dex/Wis&lt;/option&gt; &lt;option value="dexterity/crit rate"&gt;Dex/Crit Rate&lt;/option&gt; &lt;option value="constitution/intelligence"&gt;Con/Int&lt;/option&gt; &lt;option value="constitution/wisdom"&gt;Con/Wis&lt;/option&gt; &lt;option value="constitution/crit rate"&gt;Con/Crit Rate&lt;/option&gt; &lt;option value="intelligence/wisdom"&gt;Int/Wis&lt;/option&gt; &lt;option value="intelligence/crit rate"&gt;Int/Crit Rate&lt;/option&gt; &lt;option value="wisdom/crit rate"&gt;Wis/Crit Rate&lt;/option&gt; &lt;/select&gt; &lt;span&gt;Bonus&lt;/span&gt; &lt;input name="attr_extra_bonus" type="number" value="" class="small" style="width: 40px;"&gt; &lt;/span&gt; &lt;textarea name="attr_armordesc" placeholder="Description" style="height: 50px; width: 350px;"&gt;&lt;/textarea&gt; &lt;/div&gt; &lt;/fieldset&gt; &lt;div class="sheet-container"&gt; &lt;span class="label"&gt;TOTAL ARMOR VALUE&lt;/span&gt; &lt;input type="text" name="attr_totalarmor" value="" style="width: 50px;"&gt; &lt;div class="sheet-totalarmor"&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;input name="attr_lightarmor" type="hidden" value="4"&gt; &lt;input name="attr_mediumarmor" type="hidden" value="6"&gt; &lt;input name="attr_heavyarmor" type="hidden" value="8"&gt; &lt;input name="attr_strength_bonus" type="hidden" value="1"&gt; &lt;input name="attr_dexterity_bonus" type="hidden" value="1"&gt; &lt;input name="attr_constitution_bonus" type="hidden" value="1"&gt; &lt;input name="attr_intelligence_bonus" type="hidden" value="1"&gt; &lt;input name="attr_wisdom_bonus" type="hidden" value="1"&gt; &lt;input name="attr_charisma_bonus" type="hidden" value="1"&gt; &lt;input name="attr_defense_bonus" type="hidden" value="1"&gt; &lt;script type="text/worker"&gt; const stats = ["strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma"]; const int = score =&gt; parseInt(score, 10) || 0; stats.forEach(stat =&gt; { on(`change:${stat}`, (eventInfo) =&gt; { const stat_base = int(eventInfo.newValue); let stat_bonus = (stat_base &gt;= 275) ? 25 : (stat_base &gt;= 175) ? Math.floor((stat_base-175)/20) + 20 : (stat_base &gt;= 100) ? Math.floor((stat_base-100)/15) + 15 : (stat_base &gt;= 50) ? Math.floor((stat_base-50)/10) + 10 : (stat_base &gt;= 10) ? Math.floor(stat_base/5) : 1; setAttrs({ [`${stat}_bonus`]: stat_bonus }); }); }); on(`change:repeating_armor`, () =&gt; { getSectionIDs("repeating_armor", (id_array) =&gt; { const armor = []; id_array.forEach(id =&gt; armor.push(`repeating_armor_${id}_armorbonus`)); getAttrs(armor, values =&gt; { const tarm = Object.values(values).map(item =&gt; parseInt(item) || 0) .reduce((a,b) =&gt; a+b); setAttrs({ totalarmor: tarm }); }); }); }); on(`change:constitution change:totalarmor`, () =&gt; { getAttrs(["constitution", "totalarmor"], values =&gt; { let constitution = int(values.newValue); let totalarmor = int(values.newValue); let defense = constitution + totalarmor; setAttrs({ defense: defense }); }); }); on(`change:defense`, (eventInfo) =&gt; { const defense = int(eventInfo.newValue); let defense_bonus = (defense &gt;= 550) ? 25 : (defense &gt;= 350) ? Math.floor((defense-350)/40) + 20 : (defense &gt;= 200) ? Math.floor((defense-200)/30) + 15 : (defense &gt;= 100) ? Math.floor((defense-100)/20) + 10 : (defense &gt;= 20) ? Math.floor(defense/10) : 1; setAttrs({ defense_bonus: defense_bonus }); }); &lt;/script&gt; Granted I'm not very knowledgeable on coding and such, but I can't seem to see what the issue is. I've followed all the notes and suggestions everyone thus far has provided as well as learning alot of new things. Doesn't anything stick out or anyone know why the defense bonus value won't increase when defense hits a threshold?&nbsp;
1600976734
GiGs
Pro
Sheet Author
API Scripter
Your defense attribute is disabled. Sheet workers cannot update disabled attributes. Change this: &lt;input name="attr_defense" type="number" disabled="disabled" value="[[@{constitution}+@{totalarmor}]]" style="width: 50px"&gt; to &lt;input name="attr_defense" type="number" value="0" style="width: 50px" readonly&gt;
1600976892

Edited 1600978413
GiGs
Pro
Sheet Author
API Scripter
Also mentioned earlier you dont need a separate sheet worker for each of efense and defense bonus. You are creating unnecessary lag in your sheet by doing it this way. Use this instead: Edit: &nbsp;following code is fixed. on(`change:constitution&nbsp;change:totalarmor`,&nbsp;()&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;getAttrs(['constitution',&nbsp;'totalarmor'],&nbsp;values&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;constitution&nbsp;=&nbsp;int(values.constitution); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;totalarmor&nbsp;=&nbsp;int(values.totalarmor); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;defense&nbsp;=&nbsp;constitution&nbsp;+&nbsp;totalarmor; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;defense_bonus&nbsp;=&nbsp;(defense&nbsp;&gt;=&nbsp;550)&nbsp;?&nbsp;25&nbsp;: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(defense&nbsp;&gt;=&nbsp;350)&nbsp;?&nbsp;Math.floor((defense-350)/40)&nbsp;+&nbsp;20&nbsp;: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(defense&nbsp;&gt;=&nbsp;200)&nbsp;?&nbsp;Math.floor((defense-200)/30)&nbsp;+&nbsp;15&nbsp;: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(defense&nbsp;&gt;=&nbsp;100)&nbsp;?&nbsp;Math.floor((defense-100)/20)&nbsp;+&nbsp;10&nbsp;: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(defense&nbsp;&gt;=&nbsp;20)&nbsp;&nbsp;?&nbsp;Math.floor(defense/10)&nbsp;: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setAttrs({ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;defense:&nbsp;defense, &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;defense_bonus:&nbsp;defense_bonus &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;}); });
Thats one of the issues I've been running into. I've tried combining the two into one. The defense value wouldn't change when I change con or totalarmor. When I have defense disabled, the value will change but the bonus would not.
1600978363

Edited 1600978897
GiGs
Pro
Sheet Author
API Scripter
I copied that code from your last post - and it contains an error. These two lines should be replaced: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;constitution&nbsp;=&nbsp;int(values.newValue); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;totalarmor&nbsp;=&nbsp;int(values.newValue); I'll correct the code immediately after posting this. Edit: &nbsp;scrolling back through previous posts I see that error has been in the code since the first time you asked for help with this worker, back here , but I missed it till now.
That did fix it all. Thank you once again for all your help GiGs
1600979825
GiGs
Pro
Sheet Author
API Scripter
You're welcome - sorry it too so long! I'd suggest making a copy of the sheet worker template I posted at the same time. The start and end of most of your sheet workers will look like the start and end of this. (since you have an int function, I changed the two lines that had parseInt in them) on('change:attribute1 change:attribute2', &nbsp;() =&gt; { &nbsp; &nbsp; getAttrs(['attribute1', 'attribute2'], values =&gt; {&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;let variable1 = int(values.attribute1); &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;let variable2 = int(values.attribute2); &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;/* do some work here - the following bit will vary in every worker */ &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;let variable3 = variable1 + variable2; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;/* now the work is over, back to the stuff that will look the same in each worker */ &nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;setAttrs({ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;attribute3: variable3 &nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp; &nbsp; }); });
No worries. Things take time and I'm in no rush. Learning as I go so I'm not as much of a bother lol. So when it comes to value changes, it'll end up being int = (values.attrname) instead of (values.newValue)? And repeating lists is a whole new ballgame from the one previously provided but I believe I have that one down since my armor list is working properly. My next biggest tackle will be roll templates x.x
1600991503
GiGs
Pro
Sheet Author
API Scripter
newValue is just for eventInfo, which I encourage people new to sheet workers to ignore completely. One thing I'd change on your repeating section worker is the first line on(`change:repeating_armor:armorbonus`, () =&gt; { Thing of each on(change) line as setting up a little spy to watch the sheet, and when a change happens, it triggers the sheet worker. This&nbsp;change:repeating_armor&nbsp;watches every attribute inside the fieldset, and triggers whenever any of them change. But since only one of them actually matters, that's wasteful. Adding :armorbonus makes it only watch that one specific attribute, and makes the code a bit more efficient.
1600991750

Edited 1600991845
Not sure I follow on that part. Each armor piece has an individual armor value that gets added to the totalarmor which in turn ups the players defense value. All I did for this one was copy the coding for the repeat inventory you helped me with and just changed the names of whats being used. If you hop into that game I invited you to, you'll be able to see what I have going on with the workings right?
1600991982
GiGs
Pro
Sheet Author
API Scripter
That repeating section is adding up the armorbonus attribute in each row of the repeating section. There are other attributes in that section, like defending, stat_bonus, and more. Whenever any of those attributes change, the sheet worker triggers-&nbsp; but for this sheet worker, armorbonus is the only one that matters. None of the other attributes are used in the worker, so its best to restrict the change event to only watching that attribute.&nbsp;
So to fix that I'd replace the first line with the one you just provided? Which was adding :armorbonus to the end?
1600992504
GiGs
Pro
Sheet Author
API Scripter
thats correct.
For this next section, I've tried numerous ways and none seem to work how I'm hoping. What I'm looking to do in this section is when you select your base class, i.e. magician, mercenary, cleric... each base class branches off to subclasses after a certain level. I'm trying to make it to where only the subclasses for the base class will show in the next dropdown instead of all from every class. What I currently have is this: &lt;span&gt;Class:&lt;/span&gt; &lt;select class="hiding class" name="attr_class" style="width: 135px;"&gt; &nbsp;&nbsp;&nbsp;&nbsp;&lt;option value=""&gt;Choose&lt;/option&gt; &lt;option value="@{maritalartist}"&gt;Martial Artist&lt;/option&gt; &lt;option value="@{magician}"&gt;Magician&lt;/option&gt; &lt;option value="@{ranger}"&gt;Ranger&lt;/option&gt; &lt;option value="@{mercenary}"&gt;Mercenary&lt;/option&gt; &lt;option value="@{cleric}"&gt;Cleric&lt;/option&gt; &lt;/select&gt; &lt;span&gt;Subclass&lt;/span&gt; &lt;select class="hiding subclass" name="attr_subclass"&gt; &nbsp;&nbsp;&nbsp;&nbsp;&lt;option value=""&gt;Choose&lt;/option&gt; &lt;option value="@{martialartist}"&gt;Martial1&lt;/option&gt; &lt;option value="@{martialartist}"&gt;Martial2&lt;/option&gt; &lt;option value="@{magician}"&gt;Mage1&lt;/option&gt; &lt;option value="@{magician}"&gt;Mage2&lt;/option&gt; &lt;option value="@{ranger}"&gt;Ranger1&lt;/option&gt; &lt;option value="@{ranger}"&gt;Ranger2&lt;/option&gt; &lt;option value="@{mercenary}"&gt;Merc1&lt;/option&gt; &lt;option value="@{mercenary}"&gt;Merc2&lt;/option&gt; &lt;option value="@{cleric}"&gt;Cleric1&lt;/option&gt; &lt;option value="@{cleric}"&gt;Cleric2&lt;/option&gt; &lt;/select&gt; What I've tried so far is adding in an input type="hidden" as well as changing the value to 1, 2, 3, etc for base class. Do I need to create a hidden input field, like I do for the base such as such: &lt;input name="attr_strength_bonus" type="hidden" value="1"&gt; &lt;input name="attr_dexterity_bonus" type="hidden" value="1"&gt; &lt;input name="attr_constitution_bonus" type="hidden" value="1"&gt; &lt;input name="attr_intelligence_bonus" type="hidden" value="1"&gt; &lt;input name="attr_wisdom_bonus" type="hidden" value="1"&gt; &lt;input name="attr_charisma_bonus" type="hidden" value="1"&gt; &lt;input name="attr_defense_bonus" type="hidden" value="1"&gt; I've tried to create a CSS for the subclasses like: .charsheet div[class^="sheet-subclass"] { display: none; } .charsheet input.sheet-subclass1:checked ~ div.sheet-subclass-martial1, .charsheet input.sheet-subclass2:checked ~ div.sheet-subclass-martial2, .charsheet input.sheet-subclass3:checked ~ div.sheet-subclass-mage1, .charsheet input.sheet-subclass4:checked ~ div.sheet-subclass-mage2, .charsheet input.sheet-subclass5:checked ~ div.sheet-subclass-ranger1, .charsheet input.sheet-subclass6:checked ~ div.sheet-subclass-ranger2, .charsheet input.sheet-subclass7:checked ~ div.sheet-subclass-merc1, .charsheet input.sheet-subclass8:checked ~ div.sheet-subclass-merc2, .charsheet input.sheet-subclass9:checked ~ div.sheet-subclass-cleric1, .charsheet input.sheet-subclass10:checked ~ div.sheet-subclass-cleric2 { display: block; } .charsheet input.sheet-race { width: 0px; height: 0px; opacity: 0; display: none; } I've tried searching several posts with similar concepts as well as taking HTML and CSS classes but haven't had any luck. I'm assuming this can be done since the D&amp;D 5E sheet has something similar to it like only certain skills will pop up for a specific class once you level up. Speaking of the 5E sheet, I've spent hours looking through the basic coding for it to see how they did it but again, no luck in finding it. So I'm turning to you fellow coders and scripters for any and all help you can give on this matter. GiGs, I do want to apologize in advance for all these issues you've been helping with thus far.&nbsp;
1601429723
GiGs
Pro
Sheet Author
API Scripter
The first issue I'm seeing are the option values: &lt;option value="@{maritalartist}"&gt;Martial Artist&lt;/option&gt; (Note the typo there, but that's not relevant). Why are this attribute calls, and not simply text: &lt;option value="martialartist"&gt;Martial Artist&lt;/option&gt; You want to use fixed values in these, so you can use the values with CSS and reliably know what those values will contain. Second, I have no idea what's going on in this select: &lt;select class="hiding subclass" name="attr_subclass"&gt; &nbsp;&nbsp;&nbsp;&nbsp;&lt;option value=""&gt;Choose&lt;/option&gt; &lt;option value="@{martialartist}"&gt;Martial1&lt;/option&gt; &lt;option value="@{martialartist}"&gt;Martial2&lt;/option&gt; &lt;option value="@{magician}"&gt;Mage1&lt;/option&gt; &lt;option value="@{magician}"&gt;Mage2&lt;/option&gt; &lt;option value="@{ranger}"&gt;Ranger1&lt;/option&gt; &lt;option value="@{ranger}"&gt;Ranger2&lt;/option&gt; &lt;option value="@{mercenary}"&gt;Merc1&lt;/option&gt; &lt;option value="@{mercenary}"&gt;Merc2&lt;/option&gt; &lt;option value="@{cleric}"&gt;Cleric1&lt;/option&gt; &lt;option value="@{cleric}"&gt;Cleric2&lt;/option&gt; &lt;/select&gt; option values need to be unique. If you select, say, Ranger2, on the next sheet load, it iwll probably show Ranger1 as selected. HTML uses the the option value to identify which thing is selected - the text inside the &lt;option&gt;Label&lt;/option&gt; tags is just a label that is invisible to html. So you need unique values in each select option. On to your specific problem. the CSS is like this: .charsheet input.sheet-subclass1:checked ~ div.sheet-subclass-martial1, For this line to work you need a checkbox with a class="subclass1", and you need a div with the class="subclass-martial1". I dont see either of those in the code youve posted. Anyway, I wouldnt use a checkbox here. With a select, the user can only select one option. So, a hidden input will be better. Set up your selects something like: &lt;input type="hidden" class="class" name="attr_class" value="" /&gt; &lt;select class="hiding class" name="attr_class" style="width: 135px;"&gt; &lt;option value=""&gt;Choose&lt;/option&gt; &lt;option value="maritalartist"&gt;Martial Artist&lt;/option&gt; &lt;option value="magician"&gt;Magician&lt;/option&gt; &lt;option value="ranger"&gt;Ranger&lt;/option&gt; &lt;option value="mercenary"&gt;Mercenary&lt;/option&gt; &lt;option value="cleric"&gt;Cleric&lt;/option&gt; &lt;/select&gt; &lt;span&gt;Subclass&lt;/span&gt; &lt;input type="hidden" class="subclass" name="attr_subclass" value="" /&gt; &lt;select class="hiding subclass" name="attr_subclass"&gt; &lt;option value=""&gt;Choose&lt;/option&gt; &lt;option value="martial1"&gt;Martial 1&lt;/option&gt; &lt;option value="martial2"&gt;Martial 2&lt;/option&gt; &lt;option value="mage1"&gt;Mage 1&lt;/option&gt; &lt;option value="mag2"&gt;Mage 2&lt;/option&gt; &lt;option value="ranger1"&gt;Ranger 1&lt;/option&gt; &lt;option value="ranger2"&gt;Ranger 2&lt;/option&gt; &lt;option value="merc1"&gt;Merc 1&lt;/option&gt; &lt;option value="merc2"&gt;Merc 2&lt;/option&gt; &lt;option value="cleric1"&gt;Cleric 1&lt;/option&gt; &lt;option value="cleric2"&gt;Cleric 2&lt;/option&gt; &lt;/select&gt; Note: I'm a little wary of using a class name of class. I've never done that, and its just a string so it should work, but if it doesnt, its worth changing that class name just to check. Then you can do this for your CSS .charsheet div[class^="sheet-subclass"] { display: none; } .charsheet input.sheet-subclass[value="martial1"] ~ div.sheet-subclass-martial1, .charsheet input.sheet-subclass[value="martial2"] ~ div.sheet-subclass-martial2, .charsheet input.sheet-subclass[value="mage1"] ~ div.sheet-subclass-mage1, .charsheet input.sheet-subclass[value="mage2"] ~ div.sheet-subclass-mage2 { display: block; } I've only done the first 4. The rest would follow the same pattern.