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

Checking for an attribute above 3

1585078822

Edited 1585080128
I have a value that adds a specific attribute to it, but only if that attribute is at 3 or above. Is there an easy way to check for this when auto-calculating a field? In this case, the attribute is called Reflexes. If it is 3 or above, its value should then be added to a character's Defense, otherwise it shouldn't. I don't see any way to use if statements in the sheet builder, though.
1585088330
Ziechael
Forum Champion
Sheet Author
API Scripter
You should be able to use a success check for this: defence + [[ [[ {1d1*@{selected|attribute}}>3 ]]*@{selected|attribute} ]] That is the basic syntax which essentially checks if the attribute is greater than or equal to 3 (resulting in 1 if so and 0 if not and needing a 'roll' to be valid) and multiplying the result by the attribute to add to the defence. Fun eh!
So, I tried to do that and it appears not to work. My full formula is: <input type="text" name="attr_Defense" value="(10-@{Size}) + [[ [[ {1d1*@{Reflexes}}>3 ]]*(2*@{Reflexes} ]]" disabled="true"/> When I put this in, the formula reads: (10-3) + [[ [[ {1d1*2}>3 ]]*(2*2) ]] However, when opening the character sheet itself and making it calculate, the result it returns is 1019. This should be 7. If I change Reflexes to a higher number, though, this value goes up a lot - at Reflexes 3, for example, the value is then 2185. If I remove the 1d1 part and just add @{Reflexes} it calculates correctly, but obviously then it adds even when the Reflexes score is less than 3. Any idea what I'm missing here?
1585168802

Edited 1585169169
Ziechael
Forum Champion
Sheet Author
API Scripter
You appear to have an extra 2* in there, you also seem to be missing a closing bracket at the end. The inline portion might also be causing problems since this is for a character sheet? I'll have a play... You also have it set to "text" rather than "number"
1585169386

Edited 1585169590
Ziechael
Forum Champion
Sheet Author
API Scripter
So this gives the right output when calling the defense stat but displays wrong on the sheet?! <input type="number" name="attr_Defense" value="(10-@{Size}) + (({1d1*@{Reflexes}}>3)*@{Reflexes})" disabled="true"/> I reckon it's a formatting thing somewhere in there... will keep playing
1585170697

Edited 1585173162
Ziechael
Forum Champion
Sheet Author
API Scripter
Apparently it might be because disabled fields can't/won't process 'rolls', so even though it is needed to make the comparison work  it might be breaking the display (which is the next thing to test again I guess)! edit: Turns out you don't need a roll in the > portion, just something to compare against so we can drop the roll and change it to: <input type="number" name="attr_Defense" value="((10-@{Size}) + (({0,@{Reflexes}}>3)*@{Reflexes}))" disabled="true"/> Having it as type="text" will work as it turns out but will display the full formula... this is very confusing lol
When I put that formula in as you entered it, the actual character sheet returns a blank field. When I change the 'number' to 'text' it shows the formula, rather than calculating it. Why would it not display the value at all when changing it to 'Number'?
1585220980
Ziechael
Forum Champion
Sheet Author
API Scripter
I genuinely have no idea... it's an oddity for sure, probably something to do with autocalc fields and the use of the > syntax or something like that. It's illogical for sure. An alternative might be to look into using a sheet worker to set the value instead of using a calculation in the field itself?
1585234281

Edited 1585235383
So, being new to this, I had to spend some time figuring out Sheet Workers. I've created the script below, but my Defense value still doesn't actually show anything. Should this work? <script type="text/worker"> on("sheet:opened change:reflexes", function() { getAttrs(["Reflexes","Size"], function(value)) { let Ref = parseInt(value.Reflexes,0)||0; let Siz = parseInt(value.Size,0)||4; if (Ref > 2) { let DefRating = 2*(10-Siz) } else { let DefRating = 2*((10-Siz)+Ref) } setAttrs({ Defense: DefRating }) } }) </script>
1585235480

Edited 1585235736
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Hi Corin, Did you remove the disabled="true"  from the input element for totdef? Sheetworkers can't modify autocalc fields. Something else to keep in mind is that sheetworkers primarily work with attributes in lowercase. This is why best practice for attribute naming is to use all lowercase with underscores signifying spaces/word breaks instead of using camelcase. Naming your attributes this way also makes it easier for a majority of screen readers to properly read the attribute name. I also see some errors in your code. you have if(Reff > 2){ which doesn't exist in your code. I think that's probably just a typo. More seriously, you define DefRating inside of the if/else expressions. Because of scoping, DefRating is undefined when you get to your setAttrs expression. If you open your developer console, you'll probably see an error that looks like this: ReferenceError: insideVal is not defined ReferenceError: insideVal is not defined ...Lots of stack tracing information follows... Scoping is a complex topic, but the quick way to think of it is that let (and const) only define variables for everything within the same curly bracket set they are in. So, try this: <script type="text/worker">     on("sheet:opened change:reflexes", function() {         getAttrs(["reflexes","size"], function(value)) {             let ref = parseInt(value.reflexes,0)||0,                 siz = parseInt(value.size,0)||4,                 defRating;             if (ref > 2) {                 defRating = 2*(10-siz)             }             else {                 defRating = 2*((10-siz)+ref)             }             setAttrs({                 totDef: defRating             });         }     }) </script>
1585236673

Edited 1585236706
So, I had the field set to disabled, but it is now readonly instead. I took your advice and made those changes (including removing all capitals from variables throughout my sheet using Notepad++ and the Replace feature). My script now looks as follows, with defense being the name of the attribute that should be displayed. <script type="text/worker">     on("sheet:opened change:reflexes", function() {         getAttrs(["reflexes","size"], function(value)) {             let ref = parseInt(value.reflexes,0)||0;             let siz = parseInt(value.size,0)||4;             defrating;             if (ref < 3) {                 let defrating = 2*(10-siz)             }             else {                 let defrating = 2*((10-siz)+ref)             }             setAttrs({                 defense: defrating             })         }     }) </script> This still doesn't seem to work correctly, though, and I cannot figure out why. My code for the actual display fields, and what's coming up on the character sheet itself when I open the game it's tied to, are as follows. <td> <h3 style="padding:0 10px;text-align: right; font-size:16px;" data-i18n="Defense-u">Defense:</h3> </td> <td> <input type="number" name="curr_Defense" /> <input type="number" name="attr_defense" readonly /> </td>
1585239100
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Ok, so, there's something I see in your html that won't work the way you think it will. All input elements (including textareas) must be named, otherwise their data is not saved to firebase and will disappear as soon as you close the sheet or exit the game (might just be exit the game, can't remember for sure). This name must be in the format attr_attribute_name_here . Your curr_Defense  input does not have a valid name and so doesn't really do anything. If you need to differentiate between a current value and a max value, just append _max  to the end of the attribute name (e.g. attr_defense  and attr_defense_max ) For what's going on in the script, this is where logging comes in. You want to log at each important point in your script to figure out what is going on. Something like this: <script type="text/worker">     on("sheet:opened change:reflexes", function() {         getAttrs(["reflexes","size"], function(value)) {             console.log(`======== getAttrs entered and value returned ===========`);             console.log(value);             let ref = parseInt(value.reflexes,0)||0,                 siz = parseInt(value.size,0)||4,                 defrating; //Note that I removed the multiple let declarations here, you can chain them together with commas, and you had forgotten to add a let to defrating, which was then throwing the same error as last time because it wasn't defined.             console.log(`ref=${ref} | siz=${siz}`);             if (ref < 3) {                 let defrating = 2*(10-siz)             }             else {                 let defrating = 2*((10-siz)+ref)             }             console.log(`defrating=${defrating}`);             setAttrs({                 defense: defrating             })         }     }) </script> You can see the logged results in the developer console of your browser (ctrl-shift-i in chrome pc). The reason I added all those equal signs bracketing the first log was to make it easier to find in the very crowded developer console.
Does it matter in some way where I put the script? I've got it at the top of my HTML file, right below some buttons and things that allow people to see different sheets. Even after adding the console log stuff you've suggested, the Chrome console doesn't actually show any of those values when loading the sheet or making changes to the reflexes attribute.
1585240837
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
No, where you put the script element doesn't matter, but the convention is to put it at the end of the sheet. Technically, having it at the start of the sheet can slow the loading of the sheet's elements and styling.
1585240958
Scott C.
Forum Champion
Sheet Author
API Scripter
Compendium Curator
Ah, and I see a problem with your code, you're missing an ending parentheses for your getAttrs block. I'd recommend running your code through the google closure compiler to look for syntax errors like that (you should also have a large syntax error in your developer console.
Well, I got it working with your help. Some of the issue came from the fact that I had a parentheses in the wrong place, and then I had to remove some 'let' statements. The final, functional code is: <script type="text/worker">     on("sheet:opened change:reflexes", function() {     getAttrs(["reflexes","size"], function(value) {         console.log(`======== getAttrs entered and value returned ===========`);         console.log(value);         let ref = parseInt(value.reflexes,0)||0,             siz = parseInt(value.size,0)||4,             defrating;         console.log(`ref=${ref} | siz=${siz}`);         if (ref < 3) {             defrating = 2*(10-siz)         }         else {             defrating = 2*((10-siz)+ref)         }         console.log(`defrating=${defrating}`);         setAttrs({             defense: defrating             })         })     }) </script> Thanks everyone for all the help with this!