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

Help for referencing fields with expression value

Hi guys, I'm trying to do a pretty simple feature on my sheet (for WFRP 1) which consists in having a display for the hero's HPs juxtaposed to its max, and I want the user to change the HP count using 2 simple buttons. Eventually I'll try to have these buttons do more like ask the user for a number of hit point to recover. I'll also use a similar logic to prompt the user to write a quick comment about where the points are going for more critical stuff like "fate" points. For now though, all my buttons do is either add or remove one HP. (sorry for the double pic. I have no idea why it's printing in double) Here the max value you see is read correctly from the character's stats table. It is itself an expression that cumulates the initial value and a bunch of modifiers. Both the current and max HP fields are disabled, so that they may only be modified by using the buttons. Here's the onion. I want to have a simple condition inside my function that limits the HP from going over the max.      on("clicked:heal_hp", function() {         getAttrs(["hidden_present_hp", "Max_HP"], function(v) {             let hidden_hp = parseFloat(v["hidden_present_hp"]);             let max_hp = parseFloat(v["Max_HP"]);             let u = {};             if (hidden_hp < max_hp) {                 u["hidden_present_hp"] = hidden_hp + 1;             }             setAttrs(u);         });     }); simple enough. Here hidden_hp is because I used the trick they give in the doc for modifying a disabled field: """ Note:  If you are trying to update a disabled input field with this method you may run into trouble. One option is to use this  setAttrs  method to set a hidden input, then set the disabled input to the hidden element. In this example we have an attribute named  will , and we want to calculate  judgement  based off 1/2 of the  will  stat, but not to allow it to exceed 90. See below. < label > Judgment < /label > < input type = "hidden" name = "attr_foo_judgment" value = "0" / > < input type = "number" name = "attr_judgment" value = "@{foo_judgment}" disabled = "true" title = "1/2 of will rounded down, 90 max" / > """ Mine looks like this: < td >     < input type = "hidden" name = "attr_hidden_present_hp" value = "0" />     < input type = "number" name = "attr_Present_HP" value = "@{hidden_present_hp}" disabled /> </ td > < td >< input type = "number" name = "attr_Max_HP" value = "@{Current_HP}" disabled /></ td > """ My problem is that my if doesn't work. And if I try to display the value of max_hp, I get either a constant number that seems to come from nowhere, or nothing happens at all. If I don't put an if, and just leave it as simplistic as it can be (the "heal" button will add 1 hp no matter what, even if the value exceeds the displayed max), it works. Unfortunately I haven't found out how to output logs from the character sheet sandbox, so that doesn't help. But here's my guess: the "value" attribute for attr_hidden_present_hp is easily convertible to a numerical value. In fact, I know it has to be converted to a number using parseFloat because if I don't do it, the operation hidden_hp + 1 just ends up appending a "1" character and I end up with "01", "011", "0111", etc. But "@{Current_HP}", not so much. And so it returns nonsense or Null or something like that. So... with all that said, how do I obtain a numerical value from the expression in my "value" attribute?
1655969084

Edited 1655970916
GiGs
Pro
Sheet Author
API Scripter
Why is your Max_HP set to the value of current_HP? Wouldnt that cause max HP to change a lot in play? Oh, since you also have a Present_HP, I'm guessing current_HP is the current HP Max. I suspect the problem here is being caused by using disabled attributes, which don't work with sheet workers (you cant get the value out of a disabled attribute - if you removed the parseFloat, and left it as a string, you'd see its value is " @{Current_HP}" , and not a number). Since you are using sheet workers, your Max_HP should be being calculated using another sheet worker, not a disabled attribute. The solution here is two fold. First, change the way you are handling HP. Change the Current_Hp to HP_max, something like this: <input type="number" name="attr_HP_max" value="0"/> If the value is being calculated somehow using a disabled field, or being set somehwere else and you just want to display a value here, it could instead look like <input type="number" name="attr_HP_max" value="0" readonly/> And, if needed to be calculated, the value would be calculated in a sheet worker, not a disabled field. Then change the inputs above to <td>     <input type="number" name="attr_HP" value="0" readonly /> </td> <td><input type="number" name="attr_HP_max" value="0" readonly /></td> Then have a sheet worker to update HP when HP_max increases. Using HP and HP_max means you can set a token bar value to HP, and it will fill the max in automatically. Then your healing sheet worker becomes on ( "clicked:heal_hp" , function () {   getAttrs ([ "HP" , "HP_max" ], function ( v ) {       let hp = + v . HP ;       let max_hp = + v . HP_max ;       if ( hp < max_hp ) {           hp += 1 ;           setAttrs ({             hp           });       }   }); }); There are a few changes here. You only need to use this syntax v["HP"] if the attribute name isnt a valid javascript variable name - if it has, say, spaces in it. But for simple attribute names using only letters and underscores, you can use the simpler syntax: v.hp. Using +v.HP is exactly the same as parseFloat(v.HP). The + coerces the value into a number, if its a valid number (and since it comes frrom a number input, it should be. I used hp+=1 syntax to add 1 to hp - thats a handy bit of javascript, which means the same as hp = hp +1. I put the setAttrs inside the if statement, since you only want to run it if the if statement is true. And finally I did this: setAttrs({   hp }); If the variable name is the same as attribute name on the sheet, you can do this instead of setAttrs({   hp: hp }); (and attribute names are case insensitive, so here HP and hp are the same).
1655969434

Edited 1655969631
GiGs
Pro
Sheet Author
API Scripter
Mathieu L. said: Unfortunately I haven't found out how to output logs from the character sheet sandbox, so that doesn't help. If you want to output values, use console.log() or console.info(), but the output doesnt appear in the API script log - it appears in the browser console. With your campaign loaded, press F12, and in the new frame that appears, click the console tab. Your logs will appear there. console.info makes them easier to see, I think. Finally, you are using TABLE elements to layout your sheet. That's a really bad apprach for two reasons: 1) its just not as easy to use as CSS Grid, and 2) Roll20 dismisses summarily all sheets using table for layout. So you can never get a sheet using tables accepted by roll20 for sharing with the community. You are better off looking into CSS Grid, and I just made a post about that this week: <a href="https://cybersphere.me/css-grid/" rel="nofollow">https://cybersphere.me/css-grid/</a> .
1655969893

Edited 1655970119
GiGs
Pro
Sheet Author
API Scripter
Mathieu L. said: Eventually I'll try to have these buttons do more like ask the user for a number of hit point to recover. You'll hit a snag here. By default, "roll" buttons can make rolls and ask users for input, and "action" buttons can make changes to character sheets. But you cant combine them - a roll button cant make changes to a sheet, and an action button can't ask users for input. A relatively new feature, Custom Roll Parsing, does combine them and let action buttons do both, but writing CRPs is a bit more complicated than standard action buttons and sheet workers. So when it comes time to add that feature, don';t be shy about asking for help on the forums.
1655970685
GiGs
Pro
Sheet Author
API Scripter
Sorry for the reply spam. I looked for the source of this text: Note: If you are trying to update a disabled input field with this method you may run into trouble. One option is to use this setAttrs method to set a hidden input, then set the disabled input to the hidden element. In this example we have an attribute named will , and we want to calculate judgement based off 1/2 of the will stat, but not to allow it to exceed 90. See below. &lt; label &gt; Judgment &lt; /label &gt; &lt; input type = "hidden" name = "attr_foo_judgment" value = "0" / &gt; &lt; input type = "number" name = "attr_judgment" value = "@{foo_judgment}" disabled = "true" title = "1/2 of will rounded down, 90 max" / &gt; on ( "change:will" , function () { getAttrs ([ "will" ], function (values) { setAttrs ({ foo_judgment: Math. min ( 90 , (values.will/ 2 )) }); }); }); I see it is on the wiki page about sheet workers and setAttrs. Honestly, I'm confused why that's there, when readonly exists. You should never have a reason to interact with disabled attributes using sheet workers, and you should always avoid it. But note this example is one-way: it's talking about setting a disabled attribute, but not reading one. The incompatibility between sheet workers and disabled attributes is primarily about reading them. I suspeect this is the source of your issue - the above work around is for setting values, not reading them. Honestly, I think there's a strong argument for deleting that part of the wiki page, because its existence makes it look like something you should ever do, and you really shouldn't. Adding a note to use readonly on any attributes that are going to interaction with sheet workers is probably better.
Thanks for all of this, this is going to be very useful. To answer your question about using Current_HP to fill in the value for Max_HP, it's all because I'm starting from an existing sheet and the prefix "Current_" is used to designate the character's most recent stats. That is, the initial value + talents + advancements + applicable modifiers. Nothing in the code is responsive to this prefix being present but I kept this notation for consistency. But in this particular case I know it's horribly confusing. I should probably rename it Current_Max_HP for clarity, since what the HP stat really does is set the number of HP the character has when they are fully healed.
1656000595

Edited 1656000682
GiGs &nbsp;said: Mathieu L. &nbsp;said: Eventually I'll try to have these buttons do more like ask the user for a number of hit point to recover. You'll hit a snag here. By default, "roll" buttons can make rolls and ask users for input, and "action" buttons can make changes to character sheets. But you cant combine them - a roll button cant make changes to a sheet, and an action button can't ask users for input. That seems very limiting. One of the things I'm hoping to do by modifying our sheet, beyond just adding the missing stats/removing the useless stuff, is to tie loose ends in the sheet. I want to be done with players having to literally go in the sheet, click on tiny fields and change values as they go, basically using the whole sheet as a glorified txt file. I'd like everything to be done through buttons and, when necessary, have most actions recorded somewhere on the sheet. Doing that for HPs would probably be overkill, but for earning/spending experience, gold, fate points, insanity points, I'd like any change to come with a comment and date tag. It aint even not trusting the players (I'm actually a player, not a DM...), it's about having something solid to keep track of what's going on. We play sporadically, sometimes having as much as a full month between two games. A common situation is the following: It's getting a bit late but we're in a big action sequence (probably a fight). We decide to stretch it a bit to finish that sequence. Once we finish the fight, later than we had planned on finishing the game someone will go "ok guys I really have to go, tty next time". So they leave immediately, before we discuss the loot or exp gain. Then we just keep playing a little bit as a kind of wrap up. Maybe we just describe how we're all back to the camp and now some players are discussing a few things they might want to do that don't really impact the story. Like "oh while I think about it, does the herbalist who helped us sell herb X?" Simple stuff like that. But then, the next game, come the inevitable "did you guys get any experience points last time?" and "did I add it already or not?" and "did we split the gold?" or "can the doc in the village heal me" followed by "I think Mark already treated you for that wound last time you can't receive two heals for that one" and nobody's too sure what's been done, did they forget to remove that fate point last fight? etc. So that's a lot of what I'm trying to fix. I'd like changes in the sheet to be done as much as possible using buttons that enforce good note-taking practices, and possibly have a couple of log tabs at the back where you can go see where your resources go/come from.
1656010816

Edited 1656011844
GiGs
Pro
Sheet Author
API Scripter
Yes, it is limiting, but you have to work within the limits of the tools you have available. You might be trying to push too much automation on roll20. It seems the easiest solution for that situation is either use chat: just type in chat when you spend stuff, and refer back to it later, or have a handout created specifically for that kind of book-keeping. Mathieu L. said: Thanks for all of this, this is going to be very useful. To answer your question about using Current_HP to fill in the value for Max_HP, it's all because I'm starting from an existing sheet and the prefix "Current_" is used to designate the character's most recent stats. That is, the initial value + talents + advancements + applicable modifiers. Nothing in the code is responsive to this prefix being present but I kept this notation for consistency. But in this particular case I know it's horribly confusing. I should probably rename it Current_Max_HP for clarity, since what the HP stat really does is set the number of HP the character has when they are fully healed. No, you should absolutely not rename it to Current_Max_HP, and I'll explain why. Roll20 has a hidden feature: each attribute you create actually has two values, a current and a max value. The current value is the default so you don't have to do anything to access it, but you can set, store, and retreive the other value, the max value, using _max. So If you want to keep your current scheme, you should use Current_HP and Current_HP_max. This gives you the benefit of when you assign current_HP to a token, the max is assigned at the same time, and the token can show the current and max values in one bubble. Edit: I'm assumingnher that you use current_HP for the actually current hp - for instance, it goes down when a character is injured. And use Current_HP_max as the actual current max - when a character gains a permanent increase in HP, add it to this stat (and have a sheet worker that changes current_HP whenever current_HP_max changes).