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

Calculating Attributes requiring multiplication

1571346054

Edited 1571346285
Alright, so to preface this: I'm still learning code. Which shouldn't be surprising given the post to follow, and makes it difficult for me to express what I'm looking for in the correct terminology. I've been slowly learning but feel that what I want to accomplish is simple enough that it may be easier to seek advice here. That being said, I know the answer can likely be boiled down to needing further education on the matter. Basically I want to use a number value to calculate another, which is 10% of that first number. So I have a homebrew, and it's a relatively math-heavy system. Players have to balance three sources of energy to survive and perform actions/cast spells: health, magic, and stamina. I have them coded like this: <div class="col"> <h2>Life Stats</h2>         <div class="stats">             <h3>Current</h3><h3>Max</h3>             <label>Health</label><input type="number" name="attr_health" /><input type="number" name="attr_health_max"  />             <label>Magicka</label><input type="number" name="attr_magicka" /><input type="number" name="attr_magicka_max" />             <label>Stamina</label><input type="number" name="attr_stamina"  /><input type="number" name="attr_stamina_max"  />         </div> </div> Next I have the rate at which they recover at the end of each round of combat, determined by the maximum of each value.  <div class="col">         <h2>Regen Rate</h2>         <div class="stats">             <label>HR</label><input type="number" name="attr_healthrate"/>             <label>MR</label><input type="number" name="attr_magickarate"/>             <label>SR</label><input type="number" name="attr_staminarate"/>         </div> </div> Finally, I tried to make a simple sheet worker script which takes the maximum values of each to calculate and input the regen rates. <script type="text/worker"> on(“change:health_max change:magicka_max change:stamina_max sheet:opened”, function() { getAttrs([“health_max”,”magicka_max”,”stamina_max”], function(values) { let health_max = parsInt(values.health_max,10)||0; let magicka_max = parsInt(values.magicka_max,10)||0; let stamina_max = parsInt(values.stamina_max,10)||0; let hr = health_max * 0.1 let mr = magicka_max * 0.1 let sr = stamina_max * 0.1 setAttrs({ hr: healthrate, mr: magickarate, sr: staminarate }); }); }); </script> And it doesn't... really do anything. So far my thoughts are that either I haven't specified an output location or perhaps the way I named each attribute doesn't work. Is this something that can be done with a sheet worker script or do I need some sort of auto-calc script? I've tried looking through other coding examples and am still not sure what I'm missing here. Update: Note sure if this is related to the API issues that are currently outstanding. Looking into it, though.
1571359409
Kraynic
Pro
Sheet Author
Looks like you are missing the ";" on your last 3 "let" lines.  Not sure if anything else is needed, but thought I would throw that out there while you are waiting for someone more knowledgeable.  <script type="text/worker"> on(“change:health_max change:magicka_max change:stamina_max sheet:opened”, function() { getAttrs([“health_max”,”magicka_max”,”stamina_max”], function(values) { let health_max = parsInt(values.health_max,10)||0; let magicka_max = parsInt(values.magicka_max,10)||0; let stamina_max = parsInt(values.stamina_max,10)||0; let hr = health_max * 0.1; let mr = magicka_max * 0.1; let sr = stamina_max * 0.1; setAttrs({ hr: healthrate, mr: magickarate, sr: staminarate }); }); }); </script>
1571359628

Edited 1571359700
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Welcome to sheet coding Dakota! Thanks for all the information about your code; it makes troubleshooting your issue a lot easier. I believe I can see your problem (or at least a problem). You've got some undefined variables in your sheetworker so that you aren't setting what you think you're setting (I've bolded the offending section and added some comments to explain): <script type="text/worker"> on(“change:health_max change:magicka_max change:stamina_max sheet:opened”, function() { getAttrs([“health_max”,”magicka_max”,”stamina_max”], function(values) { let health_max = parsInt(values.health_max,10)||0; let magicka_max = parsInt(values.magicka_max,10)||0; let stamina_max = parsInt(values.stamina_max,10)||0; let hr = health_max * 0.1 //you're defining variables hr, mr, and sr as 10% of the max values. Additionally, you should really use semi colons to end your lines of code let mr = magicka_max * 0.1 let sr = stamina_max * 0.1 setAttrs({//Down here in setAttrs, you are then setting attributes on the sheet named hr, mr, and sr to be equal to the values of the variables healthrate, magickrate, and staminarate. However, you haven't defined these.                         //an object (which is what the curly brackets denote) uses a key:value pair system. The left side is the key, in this case it needs to be the name of an attribute to be be created and/or set on the sheet, while the right side is the value that that key is equal to. hr: healthrate, mr: magickarate, sr: staminarate }); }); }); </script> The fix for the problem is very simple, you just need to swap your keys and values in the setAttrs object. I would also recommend defining the object outside of setAttrs as a variable, and then referencing it in the setAttrs. And finally, do you want the recovery rates to come out as decimals/fractionals? or should they be rounded to the nearest whole number in some way? Here's the code with those changes made (they're in bold): <script type="text/worker"> on(“change:health_max change:magicka_max change:stamina_max sheet:opened”, function() { getAttrs([“health_max”,”magicka_max”,”stamina_max”], function(values) {                  const setObj = {}; //This defines a constant that is set to an empty object. Because it's a constant, we can never assign it to something else, but we can assign new keys to the object and change the values of the keys. let health_max = values.health_max,10*1||0;//I've changed to this method of ensuring the values are used as numbers because it is slightly more performant. let magicka_max = values.magicka_max,10*1||0; let stamina_max = values.stamina_max,10*1||0; let hr = Math.floor( health_max * 0.1 ); //I've added Math.floor here to floor the recovery amount as an example of a rounding method (in this case down) let mr = Math.floor( magicka_max * 0.1 ); let sr = Math.floor( stamina_max * 0.1 );                  setObj.healthrate = hr; //Note that you could actually do the above three lines directly into the rate keys in setObj                  setObj.magickrate = mr;                 setObj.staminarate = sr; setAttrs( setObj,{silent:true} );//I've added silent:true here because doing cascades of setAttrs which then trigger another sheetworker and so on are very poor performance wise }); }); </script> Let me know if any of that wasn't clear, and note that there are some ways to simplify your code through use of things like .each (or _.each). Scott C.
1571359775

Edited 1571359847
vÍnce
Pro
Sheet Author
Hi Dakota, first, there are far more capable individuals here to give sheet worker advice... That said, here's a couple suggestions; In your html make your auto-calculated values "readonly" ie <input type="number" name="attr_healthrate" readonly/> and in your sheet worker; I think you check changes to just the attribute and the max version is automatically included by sheet workers.  So you can simply do change:magika  and it will detect the change of either.  It may function just fine using the "..._max" version, but I though it should be mentioned.  Finally, I think your setAttrs should be reversed ie magickarate : mr instead of mr: magickarate.  Hope this helps EDIT: Sniped by Scott...  Always follow Scott's advice!  ;-P
1571361020
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Vince said: EDIT: Sniped by Scott...  Always follow Scott's advice!  ;-P Hah! Only when I'm right. When I'm not that can lead you deep into the weeds ;)
This is all very helpful, thank y'all so much! I think I understand where I'm going wrong with the errors you pointed out. Unfortunately, I don't believe I have this working quite yet, so I will start looking into some of the other suggestions Scott brought up, i.e. looking into simplifying it with .each. Does the "_max" on the attributes whose change I want to trigger this script mess it up at all? I'm also wondering how to tell that this script is affecting the inputs I set up.
1571369051
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
_max shouldn't cause a problem. As for telling if the script is working, you could use console.log to log setObj to make sure its values are what you want them to be. This is one of the reasons I like assembling the setObj before using it in the setAttrs call. Other than that, the values should just appear in the rate attributes on your sheet.
1571417187

Edited 1571417349
GiGs
Pro
Sheet Author
API Scripter
Dakota S. said: This is all very helpful, thank y'all so much! I think I understand where I'm going wrong with the errors you pointed out. Unfortunately, I don't believe I have this working quite yet, so I will start looking into some of the other suggestions Scott brought up, i.e. looking into simplifying it with .each. Does the "_max" on the attributes whose change I want to trigger this script mess it up at all? I'm also wondering how to tell that this script is affecting the inputs I set up. When Scott said "simplify" he was using it in the programmer sense, where it means, "make more complicated and harder to understand". (I'm both joking and absolutely serious.) Scotts code should work. But I notice the quotes on the first two lines are smart quotes-  I think they've been autocorrected by the program you were working in. Try changing them to on('change:health_max change:magicka_max change:stamina_max sheet:opened', function() { getAttrs(['health_max','magicka_max','stamina_max'], function(values) { Smart quotes ( “, ” ) aren't recognised as quotes by the editor. you have to use one of these three quotes: ', ", or `. And make sure Word or other editors don't 'fix' them by changing them to smart quotes.
1571417522

Edited 1571419786
GiGs
Pro
Sheet Author
API Scripter
The other thing I'd check: are your max values being set properly? If you are defining a health stat for instance, the _max also needs to be created separately, or it will be undefined. Never mind: I see from the post they are defined properly. So the quotes thing in the previous post should fix your issues. Edit:  also your original code included a typo: let health_max = parsInt(values.health_max,10)||0; should have been let health_max = pars e Int(values.health_max,10)||0; for the record, you don't actually need the ,10 part, so you could use let health_max = pars e Int(values.health_max)||0; Scott''s code has an uncaught error: let magicka_max = values.magicka_max,10*1||0; should be let magicka_max = values.magicka_max*1||0; It's easy to see how that happened- switching from parseInt to the x1 method, he just forgot to remove the ,10. So, in summary, either of these methods are fine: let magicka_max = values.magicka_max*1||0; let health_max = pars e Int(values.health_max)||0;
1571420884
GiGs
Pro
Sheet Author
API Scripter
So, if you were to start over, what you should do, is separate the 3 attributes out into their own sheet workers. You have health_max, magicka_max, and stamina_max all being monitored in the same sheet worker. This is a classic mistake made by new coders. Each of these is its own thing, not related to the others, so should be its own sheet worker. For instance, health_max would be on('change:health_max&nbsp;sheet:opened',&nbsp;function()&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;getAttrs(['health_max'],&nbsp;function(values)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;max&nbsp;=&nbsp;parseInt(values.health_max)||0; // or let&nbsp;max&nbsp;=&nbsp;+values.health_max*1||0 // either method is great. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;let&nbsp;rate&nbsp;=&nbsp;max&nbsp;*&nbsp;0.1; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setAttrs({ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;healthrate:&nbsp;rate &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;}); }); I dont think this needs much explaining. Remember that in setAttrs, the attribute you are setting goes on the left. You could create a separate sheet worker for each of the three max attributes, or as Scott suggests, have roll20 create them through a loop. One way to to that would be like this: const ratestats = ['health',&nbsp;'magicka',&nbsp;'stamina']; ratestats.forEach(stat =&gt;&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;on(`change:${stat}_max&nbsp;sheet:opened`,&nbsp;function()&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getAttrs([`${stat}_max`],&nbsp;function(values)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const max&nbsp;=&nbsp;+values[`${stat}_max`] *1||0; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;const rate&nbsp;=&nbsp;max&nbsp;*&nbsp;0.1; &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}rate`]:&nbsp;rate &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}); &nbsp;&nbsp;&nbsp;&nbsp;}); }); This function uses a bunch of arcane techniques, which I've tried to explain over here: <a href="https://wiki.roll20.net/UniversalSheetWorkers" rel="nofollow">https://wiki.roll20.net/UniversalSheetWorkers</a> .&nbsp; For this to work though, you'd need to alter your&nbsp; magickrate&nbsp; attribute name to&nbsp; magickarate . There is a way to do it without changing the name, but makes the code a little more complex - honestly, it's easier keeping your name formats consistent.
GiGs said: When Scott said "simplify" he was using it in the programmer sense, where it means, "make more complicated and harder to understand". (I'm both joking and absolutely serious.) These changes got everything working, thank you! I'm also going to try the loop you suggested, since it seems like it would help clean up the code. I think I was trying to simplify the sheet worker script by having them all together but it makes sense that they would have to be separated otherwise.
1571424251
GiGs
Pro
Sheet Author
API Scripter
Dakota S. said: GiGs said: When Scott said "simplify" he was using it in the programmer sense, where it means, "make more complicated and harder to understand". (I'm both joking and absolutely serious.) These changes got everything working, thank you! I'm also going to try the loop you suggested, since it seems like it would help clean up the code. I think I was trying to simplify the sheet worker script by having them all together but it makes sense that they would have to be separated otherwise. Great :) Just to clarify: they don't have &nbsp;to be separated. It's just good practice, and gives better performance. To explain: if you have them all in one worker, then whenever health_max changes, all 3 stats have to be read, and calculated, and then output, even when the other 2 stats didnt change. But this is 3x as much work is as actually needed.&nbsp;
GiGs said: Dakota S. said: GiGs said: When Scott said "simplify" he was using it in the programmer sense, where it means, "make more complicated and harder to understand". (I'm both joking and absolutely serious.) These changes got everything working, thank you! I'm also going to try the loop you suggested, since it seems like it would help clean up the code. I think I was trying to simplify the sheet worker script by having them all together but it makes sense that they would have to be separated otherwise. Great :) Just to clarify: they don't have &nbsp;to be separated. It's just good practice, and gives better performance. To explain: if you have them all in one worker, then whenever health_max changes, all 3 stats have to be read, and calculated, and then output, even when the other 2 stats didnt change. But this is 3x as much work is as actually needed.&nbsp; That makes total sense! It's basically asking the sheet worker to re-calculate things that aren't going to be changed on that trigger. I just started cleaning up this script.