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 .
×

Trying to select and set skill using 'IF'

In my quest to remove self-calculating fields from my character sheet, I'm trying to come up with a script that can assign the rank values for several different skills. Each of these skills has a name field, rank field, and rank bonus field. I want the script to get the rank number, compare it to the chart, and assign the proper rank bonus to the rank bonus field. I have something similar to this already working for some stat bonuses, but this one doesn't seem to want to work for me.  const int = score => parseInt(score, 10) || 0; const stats = ["appraisal", "armorevaluation", "metalevaluation", "stoneevaluation", "weaponevaluation"]; stats.forEach(stat => { on(`change:${stat}ranks`, () => { getAttrs([${stat}ranks], values => { const stat_base = int(values[`${stat}ranks`]); console.log(stat_base); let rankbonus = 0; if (stat_base >= 31) rankbonus = 80.5; else if (stat_base >= 30) rankbonus = 80; else if (stat_base >= 29) rankbonus = 79; else if (stat_base >= 28) rankbonus = 78; else if (stat_base >= 27) rankbonus = 77; else if (stat_base >= 26) rankbonus = 76; else if (stat_base >= 25) rankbonus = 75; else if (stat_base >= 24) rankbonus = 74; else if (stat_base >= 23) rankbonus = 73; else if (stat_base >= 22) rankbonus = 72; else if (stat_base >= 21) rankbonus = 71; else if (stat_base >= 20) rankbonus = 70; else if (stat_base >= 19) rankbonus = 68; else if (stat_base >= 18) rankbonus = 66; else if (stat_base >= 17) rankbonus = 64; else if (stat_base >= 16) rankbonus = 62; else if (stat_base >= 15) rankbonus = 60; else if (stat_base >= 14) rankbonus = 58; else if (stat_base >= 13) rankbonus = 56; else if (stat_base >= 12) rankbonus = 54; else if (stat_base >= 11) rankbonus = 52; else if (stat_base >= 10) rankbonus = 50; else if (stat_base >= 9) rankbonus = 45; else if (stat_base >= 8) rankbonus = 40; else if (stat_base >= 7) rankbonus = 35; else if (stat_base >= 6) rankbonus = 30; else if (stat_base >= 5) rankbonus = 25; else if (stat_base >= 4) rankbonus = 20; else if (stat_base >= 3) rankbonus = 15; else if (stat_base >= 2) rankbonus = 10; else if (stat_base >= 1) rankbonus = 5; else rankbonus = -25; setAttrs({ [`${stat}rankbonus`]: rankbonus }); }); }); });
1592677881
GiGs
Pro
Sheet Author
API Scripter
On the getAttrs line you have forgotten the quotes (backtikcs0: getAttrs([`${stat}ranks`], values => { Your if statement can be significantly simplified. Identify the patterns - when you have multiple steps separated by the same increment you can substitute a formula.     if (stat_base >= 31) rankbonus = 80.5;     else if (stat_base >= 20) rankbonus = stat_base + 50;     else if (stat_base >= 10) rankbonus = stat_base * 2 +30;     else if (stat_base >= 1) rankbonus = stat_base * 5;     else rankbonus = -25; If this is old RM/SM progression, you can change the top line to     if (stat_base >= 31) rankbonus = 65+ stat_base/2;
1592679432

Edited 1592679527
GiGs
Pro
Sheet Author
API Scripter
Also, I'd recommend changing this const stat_base = int(values[`${stat}ranks`]); to const stat_base = int(values[`${stat}ranks`]) ||0; I cant see your html, but if the statranks input can be empty, or allows player input, this stops the worker from crashing when it gets bad input. Haha, never mind, you're using the int function I posted to the boards. That above isnt necessary.
Fixing the backtics there definitely fixed it. It's works now, which is awesome. Now to figure out how to get the stat bonuses for each skill without having to dismantle my sheet. :)
My skill stat bonus script may be completely off-base. I think because it's checking the skill stats field to determine which stats the skill relies on, and then trying to find the appropriate row. I don't want to have to create a set of fields for every skill that calls the stat, considering there are over 300 skills, and each can have up to 3 stats associated. Would be nice to iterate through each one, check the specific combo, and just pull it from a list, like below Each skill has a field with a stat value, like "AG" or "AG/AG/ST". There's only about 50 combos, so it could have them all figured out here, and just populate them into the field if it could identify the right combo. I think the problem is that it's not a number in that field, and I'm not sure how to pull or read the right values. I have a feeling this one is totally wrong. What do you think? const int = score => parseInt(score, 10) || 0; const stats = ["1handedcrushweapons", "1handededgedweapons"]; stats.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`]; console.log(skill_stats); let stat_bonus = 0; let ag = parseInt(value.ag_bonus)||0; let co = parseInt(value.co_bonus)||0; let sd = parseInt(value.sd_bonus)||0; let me = parseInt(value.me_bonus)||0; let re = parseInt(value.re_bonus)||0; let st = parseInt(value.st_bonus)||0; let qu = parseInt(value.qu_bonus)||0; let pr = parseInt(value.pr_bonus)||0; let em = parseInt(value.em_bonus)||0; let in = parseInt(value.in_bonus)||0; if (skill_stats = "AG") stat_bonus = ag; else if (skill_stats = "AG/AG/ST") stat_bonus = (ag + ag + ag)/3; else stat_bonus = 0; setAttrs({ [`${stat}statbonus`]: stat_bonus }); }); }); });
1592689550
GiGs
Pro
Sheet Author
API Scripter
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.
1592690591

Edited 1592698633
GiGs
Pro
Sheet Author
API Scripter
Okay, so it looks like you might be using stat bonuses a lot. So its a good idea to make some utility functions. Put these at the start of your script block along with int, like so. I'll explain the benefit afterwards. const int = score => parseInt(score) || 0; const stats = ['co', 'ag', 'sd', 'me', 're', 'st', 'qu', 'pr', 'em', 'in']; const statAttrs = stats.map(stat => `${stat}_bonus`); const statChanges = stats.map(stat => `change:${stat}_bonus`).join(' '); const getStatBonus = (values, stat_names) => stat_names.reduce((total, name) => total += int(values[`${name.toLowerCase()}_bonus`])); So the stats are defined as an array here. Then statAttrs converts that into an array like ['co_bonus', 'ag_bonus',] etc statChanges converts the stats array to a string that looks like this: 'change:co_bonus change:ag_bonus   change:sd_bonus...' You can use them in later sheet workers like this: const weaponskills = ['1handedcrushweapons', '1handededgedweapons']; weaponskills.forEach(stat => {     on(`change:${stat}ranks ${statChanges}`, () => {         getAttrs([`${stat}stats`, `${stat}statbonus`, ...statAttrs], values => { It makes it simpler to use them in sheet workers. You just need to add ${statChanges} to the change line, and ...statAttrs  to the getAttrs line to automatically include them, without having to type them all out each time. The last one is this: const getStatBonus = (values, stat_names) => stat_names.reduce((total, name) => total += int(values[`${name.toLowerCase()}_bonus`])); This uses the reduce function, which is complicated to explain: just think it of it as aforEach loop on steroids. It calculates the stat bonus, and you use it as shown below - see the line I've made bold: const weaponskills = ['1handedcrushweapons', '1handededgedweapons']; weaponskills.forEach(stat => {     on(`change:${stat}ranks ${statChanges}`, () => {         getAttrs([`${stat}stats`, `${stat}statbonus`, ...statAttrs], values => {             const skill_stats = [`${stat}stats`];             let stat_names = skill_stats.split('/');             let stat_bonus = getStatBonus(values, stat_names);             setAttrs({                 [`${stat}statbonus`]: stat_bonus             });         });     }); }); So if there are any other functions you need to calculate a bonus from oe or more stats, you can use this as long as you supply the values variable , and an array of stat names.  Hope this helps!
Amazing info! I'll try all this out and let you know how it goes. Thanks!
1592693956
GiGs
Pro
Sheet Author
API Scripter
If you post the sheet in an external file, like pastebin or gist.github.com, I might be able to suggest some optimisations. I've been helping someone else with a RM sheet so I'm getting familiar with it. I think its likely you can benefit from combining several workers into fewer workers, but would need to see you existing code to make suggestions.
Ooh, that makes me nervous. It's like showing off my macaroni art to a professional painter. I created an account and uploaded my html and css there.&nbsp; <a href="https://github.com/madcatprimary/Spacemaster" rel="nofollow">https://github.com/madcatprimary/Spacemaster</a>
I haven't been able to get your last suggestion working yet. It's in the code, titled as //Skill Stat Bonus Calculator WIP on line 6625. I didn't see any math in there for averaging the stat bonuses (which I didn't explicitly mention, but my snip did have a (ag + ag + ag)/3 line. I should have been more explicit on that count.&nbsp; Also, it looks like you may have refered to getStatBonus as getSkillBonus after the initial definition of the function, but I could be wrong. I tried to correct for that, and I didn't find any calls for the weaponskills function beyond the start of the forEach, though the 'stat' and 'statbonus' are subsets of the skill.&nbsp; The code I uploaded obviously has some sloppy worksites in it where I've disabled my self-calculating fields to replace them with these scripts. The skill I was actually testing was 'Gymnastic Events' because it only calls for 1 skill, so that's one where there's no formula in the fields.&nbsp; I'm looking to replace all the stat bonus calculations, as well as skill bonus total calculations. This stat bonus lookup and calc is probably the most complicated thing left on the sheet, though, and once it's in place I think the other calcs will go in pretty easy.&nbsp;
1592699042
GiGs
Pro
Sheet Author
API Scripter
You're right, that should have been getStatBonus not getSkillBonus. I noticed something odd. Your function starts like this: on(`change:${stat}ranks`,&nbsp;()&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getAttrs([`${stat}stats`,&nbsp;`${stat}statbonus`,&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;skill_stats&nbsp;=&nbsp;[`${stat}stats`]; I've snipped off the end of the getAttrs line as its not relevant. The attribute listed in change is not the stat listed in getAttrs. Which one is correct? To divide by number of stats, just change getStatBonus as follows const&nbsp;getStatBonus&nbsp;=&nbsp;(values,&nbsp;stat_names)&nbsp;=&gt;&nbsp;Math.round(stat_names.reduce((total,&nbsp;name)&nbsp;=&gt;&nbsp;total&nbsp;+=&nbsp;int(values[`${name.toLowerCase()}_bonus`]))/stat_names.length);
1592700984
GiGs
Pro
Sheet Author
API Scripter
I found the issue. You replaced the wrong variable names. Here's your code from the html file: weaponskills.forEach(stat&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;on(`change:${weaponskills}ranks&nbsp;${statChanges}`,&nbsp;()&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getAttrs([`${weaponskills}stats`,&nbsp;`${weaponskills}statbonus`,&nbsp;...statAttrs],&nbsp;values&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;skill_stats&nbsp;=&nbsp;[`${weaponskills}stats`]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;stat_names&nbsp;=&nbsp;skill_stats.split('/'); &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;stat_bonus&nbsp;=&nbsp;getStatBonus(values,&nbsp;stat_names); &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setAttrs({ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[`${weaponskills}statbonus`]:&nbsp;stat_bonus &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;catch(e)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;console.log(err.message); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;}); }); You changed each stat &nbsp;inside the function to weaponskills . Let me refer you to my earlier comment: &nbsp;I'd change this to&nbsp; weapon_skills . You dont need to change anything inside the function - just the name of the variable, and the start of the forEach. In the above function weaponskills &nbsp;is the array of skills. When you do weaponskills . forEach ( stat &nbsp; =&gt; &nbsp;{ you are creating a loop. It goes through every skill in weaponskills, and the stat &nbsp;variable is the one it is working on at the time.&nbsp; So on the first run through the loop, stat becomes "1handedcrushweapons". When that loop is completed, stat &nbsp;gets reassigned to "whandededgedweapons" and the loop is done again.&nbsp; So the above should be weaponskills.forEach(stat&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;on(`change:${stat}ranks&nbsp;${statChanges}`,&nbsp;()&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getAttrs([`${stat}stats`,&nbsp;`${stat}statbonus`,&nbsp;...statAttrs],&nbsp;values&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;skill_stats&nbsp;=&nbsp;[`${stat}stats`]; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;stat_names&nbsp;=&nbsp;skill_stats.split('/'); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;stat_bonus&nbsp;=&nbsp;getStatBonus(values,&nbsp;stat_names); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setAttrs({ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[`${stat}statbonus`]:&nbsp;stat_bonus &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;}); });
1592701577
GiGs
Pro
Sheet Author
API Scripter
Speaking more generally about the code, it looks like you've put every sheet worker in its own script block. You shouldnt do this. You cant use common functions, when you do this. You should just have one script block, and all sheet workers go inside that. In this way you dont have to keep declaring things like this const&nbsp;int&nbsp;=&nbsp;score&nbsp;=&gt;&nbsp;parseInt(score)&nbsp;||&nbsp;0; You just place that once at the start of the script block along with any other common functions, and all your sheet workers can use it. When doing this, you do need to be careful about naming your variable.s You use stats &nbsp; as the name of all your arrays. You'd need to tweak them - name them for what they actually represent. Remember you dont need to change anything inside the affected function - just the bit below: const ARRAYNAME = ['some stuff']; ARRAYNAME.forEach(stat =&gt; { In each case, just change the ARRAYNAME part so they are all unique. Or if you have genuinely identical arrays, you can delete the second one. You dont have to declare it again. The second issue is one of efficiency. You have several workers that trigger on the same attributes. Like all the profession sheet workers - they should all be combined into one sheet worker.
1592706868
GiGs
Pro
Sheet Author
API Scripter
Here's a quick clean up of your sheet workers, combining them into one script block, and reorganise some of the forEach to combine them. The biggest headache was combining all the profession devcosts into one worker. <a href="https://gist.github.com/G-G-G/c4a4e0fe5b77aa46181efa2f54ab0193" rel="nofollow">https://gist.github.com/G-G-G/c4a4e0fe5b77aa46181efa2f54ab0193</a>
I replaced my script section with the one you cleaned up, and added some notation. I removed a couple of sections that were in test still and weren't working yet. I'll start those again when I've got all the skill components calculating correctly, as I think it was all the self-calculating fields that were stopping those experimental sections from working. That's what really got me digging more into this. I've updated the HTML and uploaded it to the github site. Testing now, but so far the 'weaponskill' script still isn't updating the stat bonus for the skill I have listed in the array. I picked 'gymnasticevents' because it's only got the one bonus, "AG", and I'll have to go through all the skills once it's working to switch the fields from 'disabled' to 'readonly', which is going to be fun. Just want to get it working for one of them before I do that.&nbsp;
1592715372
GiGs
Pro
Sheet Author
API Scripter
You have two problems. Remember I said earluer about the missing values ? its still missing. this const&nbsp;skill_stats&nbsp;=&nbsp;[`${stat}stats`]; should be const&nbsp;skill_stats&nbsp;=&nbsp;values[`${stat}stats`]; The second problem is your rank bonus attributes are disabled, so the sheet worker cant update them. You can do a global find and replace: Find:&nbsp; disabled class="sheet-skillstats" Replace:&nbsp; readonly class="sheet-skillstats" That'll clean up those specific attributes and get the sheet worker working.
Ah, I did see you caught that before, and I guess I was just taking in too much at once. This has been a very productive exchange for me. Adding 'values' did get it to start populating a value into the field, but the value is the stat name in instead of the stat number value. I'm going to change that variable from 'weaponskills' to 'skillnames', since it'll be for all the skills when it's working. Just need to figure out why it went for the 'AG' instead of the Ag bonus of 20 (in this instance).&nbsp; &lt;tr&gt; &lt;td&gt;&lt;button type='roll' name='roll_gymnasticevents' value='&amp;{template:default} {{name=@{character_name}}} {{skillname="Gymnastic Events"}} {{result=[[({1d100!!&gt;95cf&lt;5}) + @{gymnasticevents}]]}}'&gt;&lt;/button&gt;&lt;/td&gt; &lt;td&gt;&lt;input type="text" name="attr_gymnasticevents_name" value="Gymnastic Events" disabled class="sheet-skillname" /&gt;&lt;/td&gt; &lt;td&gt;&lt;input type="text" name="attr_gymnasticevents" value="@{gymnasticeventsrankbonus}+(@{gymnasticeventslevelbonus}*@{level})+@{gymnasticeventsstatbonus}+@{gymnasticeventsitembonus}+@{gymnasticeventsmiscbonus}" disabled class="sheet-skillranks"/&gt;&lt;/td&gt; &lt;td&gt;&lt;input type="text" name="attr_gymnasticeventsdevcost" value="0" class="sheet-skillranks" /&gt;&lt;/td&gt; &lt;td&gt;&lt;input type="text" name="attr_gymnasticeventsdevranks" value="0" class="sheet-skillranks" /&gt;&lt;/td&gt; &lt;td&gt;&lt;input type="text" name="attr_gymnasticeventsranks" value="0" class="sheet-skillranks" /&gt;&lt;/td&gt; &lt;td&gt;&lt;input type="text" name="attr_gymnasticeventsrankbonus" value="-25" class="sheet-skillranks" /&gt;&lt;/td&gt; &lt;td&gt;&lt;input type="text" name="attr_gymnasticeventslevelbonus" value="0" class="sheet-skillranks" /&gt;&lt;/td&gt; &lt;td&gt;&lt;input type="text" name="attr_gymnasticeventsstats" value="AG" readonly class="sheet-skillstats" /&gt;&lt;/td&gt; &lt;td&gt;&lt;input type="text" name="attr_gymnasticeventsstatbonus" value="0" readonly class="sheet-skillstats" /&gt;&lt;/td&gt; &lt;td&gt;&lt;input type="text" name="attr_gymnasticeventsitembonus" value="0" class="sheet-skillranks" /&gt;&lt;/td&gt; &lt;td&gt;&lt;input type="text" name="attr_gymnasticeventsmiscbonus" value="0" class="sheet-skillranks" /&gt;&lt;/td&gt; &lt;/tr&gt; // Common Functions const int = score =&gt; parseInt(score) || 0; const float = score =&gt; parseFloat(score) || 0; const stats = ['co', 'ag', 'sd', 'me', 're', 'st', 'qu', 'pr', 'em', 'in']; const statAttrs = stats.map(stat =&gt; `${stat}_bonus`); const statChanges = stats.map(stat =&gt; `change:${stat}_bonus`).join(' '); const getStatBonus = (values, stat_names) =&gt; stat_names.reduce((total, name) =&gt; total += int(values[`${name.toLowerCase()}_bonus`])); const getRankBonus = level =&gt; level &gt; 30 ? 65 + level / 2 : (level &gt; 20 ? level + 50 : (level &gt; 10 ? (level * 2) + 30 : (level &gt; 0 ? level * 5 : -25 ))); const skillnames = ['gymnasticevents']; skillnames.forEach(stat =&gt; { on(`change:${stat}ranks ${statChanges}`, () =&gt; { getAttrs([`${stat}stats`, `${stat}statbonus`, ...statAttrs], values =&gt; { const skill_stats = values[`${stat}stats`]; let stat_names = skill_stats.split('/'); let stat_bonus = getStatBonus(values, stat_names); setAttrs({ [`${stat}statbonus`]: stat_bonus }); }); }); });
1592717378
GiGs
Pro
Sheet Author
API Scripter
The previous post gets the levels working. For the stat bonuses, you need to do the following: Change the getStatBonus function to this const&nbsp;getStatBonus&nbsp;=&nbsp;(values,&nbsp;stat_names)&nbsp;=&gt;&nbsp;Math.round(stat_names.reduce((total,&nbsp;name)&nbsp;=&gt;&nbsp;total&nbsp;+=&nbsp;int(values[`${name.toLowerCase()}_bonus`]),&nbsp;0)/stat_names.length); Change your skills.forEach to this &nbsp;&nbsp;&nbsp;&nbsp;skills.forEach(stat&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Skill&nbsp;Ranks&nbsp;Bonus&nbsp;Calculator &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;on(`change:${stat}ranks`,&nbsp;()&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getAttrs([`${stat}ranks`],&nbsp;values&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;stat_base&nbsp;=&nbsp;int(values[`${stat}ranks`]); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;rankbonus&nbsp;=&nbsp;getRankBonus(stat_base); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setAttrs({ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[`${stat}rankbonus`]:&nbsp;rankbonus &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//Skill&nbsp;Stat&nbsp;Bonus&nbsp;Calculator&nbsp;WIP &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;on(`${statChanges}&nbsp;sheet:opened`,&nbsp;()&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getAttrs([`${stat}stats`,&nbsp;...statAttrs],&nbsp;values&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const&nbsp;skill_stats&nbsp;=&nbsp;values[`${stat}stats`] || ''; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;stat_names&nbsp;=&nbsp;skill_stats.split('/'); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;stat_bonus&nbsp;=&nbsp;getStatBonus(values,&nbsp;stat_names); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setAttrs({ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[`${stat}statbonus`]:&nbsp;stat_bonus &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;}); Figuring this out also revealed two skills have typos in them.&nbsp; Find: &nbsp;attr_xenoloretat Replace: &nbsp;attr_xenolore s tat Find: &nbsp;attr_ medicalsciencetat Replace: &nbsp;attr_ medicalscience s tat And that gets the stat bonuses working properly.
1592717493
GiGs
Pro
Sheet Author
API Scripter
Sean R. said: Ah, I did see you caught that before, and I guess I was just taking in too much at once. This has been a very productive exchange for me. Adding 'values' did get it to start populating a value into the field, but the value is the stat name in instead of the stat number value. I'm going to change that variable from 'weaponskills' to 'skillnames', since it'll be for all the skills when it's working. Just need to figure out why it went for the 'AG' instead of the Ag bonus of 20 (in this instance).&nbsp; That issue was my fault. the getRankBonus function was not calculating the first attribute in a set properly - it was treating the operation as a string concatenation (adding letters to letters), instead of an arithmetic one. I've fixed that in the post above.
1592717985
GiGs
Pro
Sheet Author
API Scripter
I keep forgetting&nbsp; to ask this: Are you planning on submitting this sheet for community use? If so, you should be aware that they reject any sheets using tables for layout. Your sheet is laid out with tables, so you'd need to replace that method of layout with a CSS-based one.
1592731285
Andreas J.
Forum Champion
Sheet Author
Translator
GiGs said: Are you planning on submitting this sheet for community use? If so, you should be aware that they reject any sheets using tables for layout. Your sheet is laid out with tables, so you'd need to replace that method of layout with a CSS-based one. People submitting sheets made with HTML table layout is the most common reason their sheets get rejected/held up. Surprisingly, doing something wrong with git/github or having a faulty `sheet.json` isn't as common.
It is working now, with the last set of changes you posted.&nbsp; It wasn't on my list of goals initially to publish this, because I never thought I would get this far. I'll have to figure that out, not that I have such a functional sheet.
1592775904
GiGs
Pro
Sheet Author
API Scripter
Great! I noticed a few errors in the browser console about invalid autocalc fields. They seem to slow the loading of the sheet a bit, so it'll be a good idea to find and correct them. It looks like there are five faulty ones, where the formulas contain bad attribute references.