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

[Script] Automatic Temp HP Management

Always forgetting to remove those temp HP first? This script allows you to configure a primary HP bar and a temp HP bar. When you subtract HP from the primary bar the script will check the temp HP bar and remove the appropriate number of temp HP and adjust the primary HP to compensate. <a href="https://gist.github.com/edalquist/5488951" rel="nofollow">https://gist.github.com/edalquist/5488951</a>
So I hadn't realised this was here and set out to writes something similar, myself. Mine is slightly more compact, but should accomplish the same goal. I am, however, having an odd problem: obj.set does not actually change the HPBar or the THPBar, no matter what I do. &nbsp;I've tried yours and mine, Eric, and neither of them change the bars, even though both are reporting the right values to change to. <a href="https://gist.github.com/kkragenbrink/5490886" rel="nofollow">https://gist.github.com/kkragenbrink/5490886</a>
1367347239
Alex L.
Pro
Sheet Author
This works for changing the bar for me: on("ready", function() { &nbsp; object = findObjs({_type: "graphic", name: "testy"})[0]; &nbsp; if(object &amp;&amp; "get" in object) { &nbsp; log("found"); &nbsp; object.set("bar1_value", 8); &nbsp; } &nbsp; else log("not found"); }); should set the value of the hp bar to 8 on a token called testy
The problem is this line: obj.set({HPVALUE : hp.target, THPVALUE : thptarget}); In Javascript, it doesn't translate the "HPVALUE" variable into "bar1_value" before issuing the set command when you write it that way. So you're literally trying to write to the "HPVALUE" property on the object, which doesn't exist. Instead, do: var newattrs = {}; newattrs[HPVALUE] = hp.target; newattrs[THPVALUE] = thptarget; obj.set(newattrs); And it should work.
Oh, hey, I knew that. Thanks, Riley.
Huh, I'll give it another test today to make sure I didn't miss something in a copy/paste
So it's still not behaving as expected. &nbsp;I have a token with 6 in Bar3 and 34 in Bar1. &nbsp; When I do -5 on Bar1, my log of the target object says: &nbsp;{"bar3_value":1,"bar1_value":34}, which is what I use in obj.set(). That's as expected. &nbsp;However, the map still shows Bar1 is 29 and Bar3 is 6. &nbsp; For an extra dose of weird: if I then do a -1 to Bar1, the log reports&nbsp;{"bar3_value":0,"bar1_value":29}, even though the map shows Bar3 is 6 and Bar1 is 28. So the object is updating, but the map is not, and when the next change happens, the map and object are out of sync with one another.
I'd like a version of this that uses an attribute on the token instead of a bar and give players a way to type something like: !thp 5 or !thp -5 to adjust their attribute.
1367403155
Alex L.
Pro
Sheet Author
If you edit the bar it edits the linked atrib as far as i know.
I don't want to use a bar for temp hp.
1367403585
Alex L.
Pro
Sheet Author
HoneyBadger said: I don't want to use a bar for temp hp. In that case you would need to find the attrib obj you want to use, using its name and the id of the character. You could get the attrib linked to the hp bar then use that to get the id of the character. @Riley: It would be a lot easyer if you exposed the id of the character link to the token.
Alex L. said: HoneyBadger said: I don't want to use a bar for temp hp. In that case you would need to find the attrib obj you want to use, using its name and the id of the character. You could get the attrib linked to the hp bar then use that to get the id of the character. @Riley: It would be a lot easyer if you exposed the id of the character link to the token. Oh, whoops. Oversight. I'll add that in the next update. Kevin K. said: So it's still not behaving as expected. &nbsp;I have a token with 6 in Bar3 and 34 in Bar1. &nbsp; When I do -5 on Bar1, my log of the target object says: &nbsp;{"bar3_value":1,"bar1_value":34}, which is what I use in obj.set(). That's as expected. &nbsp;However, the map still shows Bar1 is 29 and Bar3 is 6. &nbsp; For an extra dose of weird: if I then do a -1 to Bar1, the log reports&nbsp;{"bar3_value":0,"bar1_value":29}, even though the map shows Bar3 is 6 and Bar1 is 28. So the object is updating, but the map is not, and when the next change happens, the map and object are out of sync with one another. Alex L. said: If you edit the bar it edits the linked atrib as far as i know. Ah, this may not be the case in the API. I think the way I'm doing it in Roll20 is when I display that values in the bar, if the bar is linked to an attribute, I'm actually ignoring the bar value and just show the current value of the attribute. So currently the correct way to do it in the API would be to check and see if a Bar is linked to an Attribute, and if it is you need to modify the Attribute's value, not the bar's value.
1367415234
Alex L.
Pro
Sheet Author
Riley D. said: Alex L. said: HoneyBadger said: I don't want to use a bar for temp hp. In that case you would need to find the attrib obj you want to use, using its name and the id of the character. You could get the attrib linked to the hp bar then use that to get the id of the character. @Riley: It would be a lot easyer if you exposed the id of the character link to the token. Oh, whoops. Oversight. I'll add that in the next update. Thanks, I could to with controling player as well although that will default to the character when one is linked. Would it be hard to have the controlledby property as read and write? so you can do things like disable/enable changing of tokens by players. Kevin K. said: So it's still not behaving as expected. &nbsp;I have a token with 6 in Bar3 and 34 in Bar1. &nbsp; When I do -5 on Bar1, my log of the target object says: &nbsp;{"bar3_value":1,"bar1_value":34}, which is what I use in obj.set(). That's as expected. &nbsp;However, the map still shows Bar1 is 29 and Bar3 is 6. &nbsp; For an extra dose of weird: if I then do a -1 to Bar1, the log reports&nbsp;{"bar3_value":0,"bar1_value":29}, even though the map shows Bar3 is 6 and Bar1 is 28. So the object is updating, but the map is not, and when the next change happens, the map and object are out of sync with one another. Alex L. said: If you edit the bar it edits the linked atrib as far as i know. Ah, this may not be the case in the API. I think the way I'm doing it in Roll20 is when I display that values in the bar, if the bar is linked to an attribute, I'm actually ignoring the bar value and just show the current value of the attribute. So currently the correct way to do it in the API would be to check and see if a Bar is linked to an Attribute, and if it is you need to modify the Attribute's value, not the bar's value. Ok i will take that into acoount going forward thanks.
Good to know, I'll try to update my script soon with attribute support
1367422006
Alex L.
Pro
Sheet Author
For anyone who wants it a function to set a bars value or the attrib its linked to: function setBar(token, bar, val) { &nbsp; &nbsp;var aid = token.get("_bar" + bar + "_link"); &nbsp; &nbsp;if(aid != "") { &nbsp; &nbsp; &nbsp;var attribs = findObjs({_type: "attribute", _id: aid}); &nbsp; &nbsp; &nbsp;if(attribs.length &gt; 0) { &nbsp; &nbsp; &nbsp; &nbsp;var attrib = attribs[0]; &nbsp; &nbsp; &nbsp; &nbsp;if(attrib &amp;&amp; "set" in attrib) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;attrib.set("current", val); &nbsp; &nbsp; &nbsp; &nbsp;} &nbsp; &nbsp; &nbsp;} &nbsp; &nbsp;} else { &nbsp; &nbsp; &nbsp;token.set("bar" + bar + "_value", val); &nbsp; &nbsp;} }
Apparently Alex and I were working on this at the same time; however, I'm getting a very odd error in my code when I try mine. &nbsp;The error is: Error: Firebase.update failed: First argument contains undefined in property 'current' at Error (&lt;anonymous&gt;) at Aa (/home/symbly/www/d20-api-server/node_modules/firebase/lib/firebase-node.js:9:49) at Aa (/home/symbly/www/d20-api-server/node_modules/firebase/lib/firebase-node.js:10:196) at za (/home/symbly/www/d20-api-server/node_modules/firebase/lib/firebase-node.js:8:468) at Da (/home/symbly/www/d20-api-server/node_modules/firebase/lib/firebase-node.js:10:392) at G.W.update (/home/symbly/www/d20-api-server/node_modules/firebase/lib/firebase-node.js:128:318) at TrackedObj.set ( The code is at: <a href="https://gist.github.com/kkragenbrink/5490886" rel="nofollow">https://gist.github.com/kkragenbrink/5490886</a>
1367424681
Alex L.
Pro
Sheet Author
TrackedObj.set errors tend to be problems with a obj.set normaly a bad value.
Your code is incorrect at: setBar(obj, HPBAR, target.hp); setBar(obj, THPBAR, target.thp); You probably meant setBar(obj, HPBAR, target[HPVALUE]);
1367425057
Alex L.
Pro
Sheet Author
Riley D. said: Your code is incorrect at: setBar(obj, HPBAR, target.hp); setBar(obj, THPBAR, target.thp); You probably meant setBar(obj, HPBAR, target[HPVALUE]); Did the preview show him using that code for you? For me it shows: obj.set(target);
You're right, I did, and I'm about 95% working, now. &nbsp;I had to change setBar to update both the bar AND the attribute, if an attribute is present. &nbsp;Now, if I remove enough THP to reach 0 THP, it works flawlessly. &nbsp;If I SET the value (for example, if I have 34 HP, 6 THP, and I set HP to 29), it will successfully change everything to how it should be. &nbsp;However, if I try to SUBTRACT a value (if I have 34 hp, 6 THP, and I set HP to -5), it will update THP successfully, but also update HP. &nbsp;I'm going to see if there is a race condition in a minute, but I thought I'd keep giving updates to the quirks as I find them.
Update: There is definitely a racecondition. &nbsp;A settimeout of even 1 millisecond resolves the issue described in my last post.
I'm not totally sure I follow. If you could break it down into a more simple example/test case that causes the race condition without the extra stuff that would be helpful.
Uhhh. Not sure I can. &nbsp;I'll try and explain it, though, in a straightforward manner: When you click a token, the bars fly out as easy-to-modify little circles. &nbsp;Click a circle, get an input box. &nbsp;If you enter a value into the input box (like "34") it will set the bar to the value you have specified. &nbsp;If you enter a delta into the input box (-6, +5), it will adjust &nbsp;the value of the bar by the amount you have specified. &nbsp;I'll use the terms set &nbsp;and adjust &nbsp;as I progress in this explanation to describe the difference between these two types of changing a bar. When you set &nbsp;or adjust , it triggers a token change, as used in my script. &nbsp; Assume our token begins with 30 in Bar 1, and 5 in Bar 2, and my script is used assuming Bar 1 is Hit Points and Bar 2 is Temporary Hit Points. If you set &nbsp;Bar 1 to 25, the result will be: Bar 1 has 30, Bar 2 has 0. &nbsp;This is exactly as desired. If you adjust &nbsp;Bar 1 by -5, the result will be: Bar 1 has 25, Bar 2 has 0. &nbsp;Bar 2 is exactly as desired; obviously, Bar 1 is not. If you add a setTimeout (e.g. setTimeout(setBar.bind(this, obj, HPBAR, target[HPVALUE]), 10)) to each call of setBar, then the adjust &nbsp;example will work exactly as expected. &nbsp;The race condition is specifically to trying to adjust &nbsp;a bar which is also going to be manipulated by the API. I have tested values as low as 1 millisecond for the setTimout; 1 millisecond works most of the time. 10 millisecond works all the time, and is &nbsp;virtually transparent to the user. For now, I am using 10 milliseconds in my code.&nbsp;
Okay, but what I'm wondering is, where exactly is the race condition? I assume it's someplace in all the calculations you're doing for the temp HP stuff? What I need is like "if you respond to a change event for a bar1_value by setting the bar1_value, then it doesn't work"...which I think is what you're saying? The reason why I'm also confused is because whether you do a straight "set value to 50" or type "-5" should be immaterial to the API. All of that is handled client-side before any save() event is sent to the server. So there shouldn't be any difference there. The token that you're doing this test on, are any of the bars linked to Character attributes?
Also looking at my code it seems that I was wrong about the attribute and the bar1_value being explicitly linked. Rather it seems I was just setting them both manually. So I'm going to have to re-think how I'm handling this throughout Roll20 and then I'll update the API docs accordingly.
Yes, the bars are both linked to attributes. &nbsp;And yes, if you respond to a change event for bar1_value by setting bar1_value and the attribute (as you pointed out, and I discovered on my own: you have to do both), and if the change event for bar1_value is an adjustment (-5) instead of a set (30), the API seems to race with the client to determine what will show in the bar value. &nbsp;I realise that it SHOULD be immaterial to the API, but my tests have shown it making a difference. If you'd like, I can give you access to the campaign where I am testing this and you can see what I'm talking about.
Okay, I've figured out what's going on here, bear with me. First off, currently if a bar is linked to an attribute, the attribute's value is what matters for what shows up in the bubble. So keep that in mind. What's happening is this: 1) You are modifying the value of the bubble. This is actually doing two things. First, it sets the value of the bar (bar1_value). 2) The real-time sync server and the API receive the first update for bar1_value. This is what triggers your current script, which *correctly* sets the bar1_value, but since the attribute value is what's being shown in the bubble, that doesn't matter. In addition, your script *attempts* to set the current property of the linked attribute. So at this point we send the set() event to the real-time server. HOWEVER, 3) ...back in Step 1), after the bar1_value is set client-side, the client then also (wanting to be a good, responsible client and keep them in sync) updates the current property of the linked attribute. The thing is, a lot of the time this second client-side event (which again is happening because you modified the bubble) is reaching the server/API *after* you've already tried to set the new current property from step 2).&nbsp; So if I were looking at this from the DB perspective, we see: bar1_value changed (from modifying bubble) bar1_value changed + current changed by API script current changed (from modifying bubble) Because, again, the attribute value is what's shown in the bubble, you're not seeing any change, although if you could see the whole picture you'd see that bar1_value is indeed getting set correctly. So basically the problem is you're modifying the current value of the attribute in response to bar1_value changing, not knowing that there's going to be a separate current change event coming your way like 2ms later. Which is why when you put in the timeout it works, because you're basically causing the script to modify the values after it hears both events from the client. The reason it's variable (sometimes a 1ms timeout works, sometimes it needs to be 10ms) is because that's your client's latency to the real-time server, but you would find (I think) that if you had someone with a really slow connection, they would need you to set a much longer timeout, as their second event would take longer to reach the server/the API. Another interesting thing about this is that the API server has almost no latency (something like 1ms) to the real-time server, whereas any client is going to have at least 30ms if not more. So the API server is able to hear the first event, and set both new values (bar1_value and current) to the real-time server before your client even has time to send the second event for the current attribute property. I'm still not totally sure how I want to handle fixing this, but I thought I would let you know what's going on. In the mean time, your timeout is fine, although as I said if you have any slower connected players they may have issues.
Yeah, that's about what I figured was happening, but ... as you discovered trying to describe it yourself from a position of KNOWING what's going on, it's not exactly easy to describe. Glad to know it is as expected, though! &nbsp;I will play with the timing on this a little; at worst, even a 1000ms delay is not going to kill my game, and my players will accept it and understand it if I explain it. &nbsp;Thanks for taking the time to look into it, Riley! &nbsp;Even with this race condition, the code is working well, and I'm on to writing a full on 4e framework, now.
I'm trying to make a script like this that works using an attribute for temphp instead of a bar, but I have no idea how to alter the attribute on the correct character sheet (journal) linked to the token.
1367498994
Alex L.
Pro
Sheet Author
At the moment unless the token has an attribute linked to one of its bars its impossible to know what token belongs to what char without user input. It will be fixed in the next version.
Just fyi... you might want to add:&nbsp; if (obj.get("isdrawing") == true) { return; } to your token status scripts. I moved one of my pointer tokens around that is toggled to a drawing instead of a token and the token status script immediately fired and added the red aura and red x even though the tokens "hit points" were empty strings with no numbers. I'm sure there's other ways to do this as well, but&nbsp; if (obj.get("isdrawing") == true) { return; } is how I handled it in my script.