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

Baby's First Java Script - needs help

Hi, I'm modifying the 5e Darker Dungeons sheet and am struggling to get a handle on the most basic Javascript function that's within it. The Goal is to create an on change event for the "level" field that performs the following calculation: update hit_dice_max = level + 5 Can anyone with a basic understanding of javascript tell me where I'm going wrong ? (I'm certain it'll be something simple) on("change:level", function(eventinfo) { if (eventinfo.sourceType && eventinfo.sourceType === "sheetworker") { return; } update_hit_dice_max(); }); var update_hit_dice_max = function(attr) { getAttrs(["level"], function(v) { var lvl = parseInt(v["level"], 10) : 0; var hdmax = lvl + 5; var update = {}; update["hit_dice_max"] = hdmax; setAttrs(update); }); };
1640966977

Edited 1640967661
David
Sheet Author
The :0 is your issue are you trying to default 0?  My Javascript is limited but you would probably achieve it with || 0  lvl = parseInt(v["level"], 10) : 0; You also should not need the sourceType checking
1640968893
Kraynic
Pro
Sheet Author
I think it could be something like this: on("change:level", function () { getAttrs(['level'], function (values) { const level = parseInt(values['level'])||0; const hdmax = level+5; setAttrs({ hit_dice_max: hdmax }); }); });
1640992595
GiGs
Pro
Sheet Author
API Scripter
Kraynic's code looks good to me. One thing I'd recommend when doing character sheet code is to avoid programming practices that would be standard elsewhere like this: on("change:level", function(eventinfo) { if (eventinfo.sourceType && eventinfo.sourceType === "sheetworker") { return; } update_hit_dice_max(); }); When I see a structure like this in sheet code, its usually accompanied by a lot more functions like that, sometimes several of them being called in the same change event. That leads to a single change event leading to multiple setAttrs events, and that is something you really want to avoid. SetAttrs is a slow function, and multiple calls can cause a sheet to lag. It's better to have your function calls return an object containing the changed attributes and then have a single setAttrs set them all. I cant type too well at the moment, so I can't give a code example of what I mean, but ideally you want to have a single setAttrs, and ideally a single getAttrs (though they aren't as bad as setAttrs).
Kraynic said: I think it could be something like this: on("change:level", function () { getAttrs(['level'], function (values) { const level = parseInt(values['level'])||0; const hdmax = level+5; setAttrs({ hit_dice_max: hdmax }); }); }); Thank you thank you thank you, this worked perfectly and now I have a basic structure to use moving forward.
GiGs said: Kraynic's code looks good to me. One thing I'd recommend when doing character sheet code is to avoid programming practices that would be standard elsewhere like this: on("change:level", function(eventinfo) { if (eventinfo.sourceType && eventinfo.sourceType === "sheetworker") { return; } update_hit_dice_max(); }); When I see a structure like this in sheet code, its usually accompanied by a lot more functions like that, sometimes several of them being called in the same change event. That leads to a single change event leading to multiple setAttrs events, and that is something you really want to avoid. SetAttrs is a slow function, and multiple calls can cause a sheet to lag. It's better to have your function calls return an object containing the changed attributes and then have a single setAttrs set them all. I cant type too well at the moment, so I can't give a code example of what I mean, but ideally you want to have a single setAttrs, and ideally a single getAttrs (though they aren't as bad as setAttrs). not sure I understand what you are saying (pretend you are talking to a goldfish that trying to code without any prior coding experience outside fo Excel ).... as there are indeed bunches of Sheetworkers on Giffy's Darker Dungeons sheet (which is what I'm using as a base for modifications) but it looks like this is just the method the original coder was used to doing as he seems to call 1x sheetworker for 1x function with 1x setAttr and getAttr in it....  However there are a few that are attached to multiple functions.... e.g. on("change:dtype", function(eventinfo) { if (eventinfo.sourceType && eventinfo.sourceType === "sheetworker") { return; } update_attacks("all"); update_npc_action("all"); }); on("change:jack_of_all_trades", function(eventinfo) { if (eventinfo.sourceType && eventinfo.sourceType === "sheetworker") { return; } update_jack_attr(); update_all_ability_checks(); });
1641001477

Edited 1641001869
GiGs
Pro
Sheet Author
API Scripter
Thats exactly the kind of thing I was warning against. In traditional computing its normal to separate bits of code into separate functions, but thats a bad practice to use with network-based code like roll20. Every one of those functions has its own getAttrs and setAttrs function within it - and the problem is, each of those functions has to send code across the internet to contact roll20's servers, and wait for roll20 to send information back. Aside from the variability of the internet, that puts a load on roll20's own servers - they might have hundreds or thousands of such requests coming in from other games, and your game was to wait in line till roll20 has handled all those. That can be a slow process, especially if you have multiple such commands, and can lag your sheet while its waiting to be updated. its better to structure your code so that you perform only one setAttrs and on getAttrs (though the setAttrs is more important). See the version Kyarnic wrote which didnt have a separate functioncall, so you can see exactly what's going on in that change event. The fact that those change events all have this at the start: if (eventinfo.sourceType && eventinfo.sourceType === "sheetworker") { return; } is worrying too. That suggests the writer is trying to keep lag down via a brute force approach, but not really addressing what is causing the lag to happen. That code should not be necessary in normal sheet workers (there are places you'd include something like that, but it should not be a universal thing). If you really needed to put code into functions, imagine instead you had something like:                     on("change:jack_of_all_trades", function(eventinfo) {                         // call functions which don't have setAttrs commands within them                         // instead they create an object variable containing the updated attributes and values, and return that         var jack = update_jack_attr(); var abilities = update_all_ability_checks();                          // now merge those objects into a single variable. You can do this with any number of objects                          var output = {...jack, ...abilities};                          // finally use a single setAttrs to update the sheet.                          setAttrs(output, {silent:true});                          // silent:true here does the same work as that code at the start of the worker, blocking sheet workers from continuing. }); Ideally you'd remove the getAttrs from the functions as well and have a single getAttrs in this code, and pass any attributes needed into the function. But that's not as big a deal as cutting down on the setAttrs functions. The best approach though is to do what kraynic has done, and dispense with the separate function  entirely, and copy out the code that is needed from them and up it in the on(change) block - you might need to be thoughtful about combining code if you have two or more functions as above. The only reason to use separate functions is to handle repeated code - if you are doing the same calculation in multiple different workers. And even there, there are often alternative approaches (like the Universal Sheet Worker approach i have on the wiki, or using your function just to perform calculations and return their results: dont set values on the sheet). The conclusion: roll20 character sheets run in a network environment, so code that is good when run on your home computer can be bad for roll20. You haveto think about how to approach that to make your sheet more optimal.
1641007074
Kraynic
Pro
Sheet Author
DM Dogchain said: not sure I understand what you are saying (pretend you are talking to a goldfish that trying to code without any prior coding experience outside fo Excel ).... GiGs explanations can melt my brain sometimes as well.  That isn't a commentary on the explanations GiGs gives, but just the magnitude of my ignorance.  That snippet I provided was something from one of my sheets altered to use the attribute names you used.  GiGs said it looked ok, but it was probably a sheetworker that GiGs wrote years ago when I was first asking for help cobbling a sheet together. Basically, don't be too concerned about your lack of experience.  I had no experience with any bit of coding any part of a sheet before deciding no one else was going to make a sheet for a game that was 25 years out of print.  Sometimes a bunch of stubborn (and some help on the forums) is all it takes to get you by...  :p
Hi Gigi, That all makes sense and I couldn't figure out why the sheetworkers had to be separate from the function in the first place. I be using that inline syntax Kraynic suggested (as it works exactly as I had hoped) but I doubt I'm the right guy to retrofit the original coding (considering I couldn't work out X + 5) Thanks again for the prompt help and advise, may the homebrew character sheet continue.