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 .
×

[HELP] Automatically increment status marker at specific fractions of hit points

I'm looking for a solution to the following problem: My group plays 5e D&D with a variant hp rule. You have hit points equal to your level plus your Constitution modifier and if you take damage in excess of this, your hit points reset (damage carries over) and you take a level of exhaustion. Functionally, this means that you have max hp = 6 * (LEVEL + CON) since there are five levels of exhaustion. What I'd like is an API script that checks when a token is modified, and appropriately increments the skull token, and corrects the hit points as appropriate. I'm familiar with programming, but Javascript/web development is entirely new to me. I imagine that I would need something along the lines of: on("change:token", function(obj){ // if the target doesn't have their max hp keyed to their bar1 // then don't run this script if(obj.get("bar1_max") === "") return; // if the target's hp falls to zero or below, add a wound counter // and increase their hp by maximum if(obj.get("bar1_value") <= 0) { obj.set("status_skull",obj.get("status_skull")+1); obj.set("bar1_value",obj.get("bar1_value") + obj.get("bar1_max")); } }) or alternatively (using total effective hp) on("change:token", function(obj){ if(obj.get("bar1_max") === "") return; var nWounds = 6 - ceil(6 * obj.get("bar1_value") / obj.get("bar1_max")); obj.set("status_skull",num.toString(nWounds)); }) I would love to just implement Token-Mod and be done with it, but I can't figure out how to do it in a macro (which obviously doesn't run itself). I don't know how to make Token-Mod reference token properties, e.g. !token-mod --set statusmarkers|skull:+1 --set bar1_value|@{target|bar1_value}+@{target|bar1_max} isn't working for me. I also am not clear on how to implement Token-Mod inside of a .js file. I know I'm butchering syntax in at least several places, so maybe this is an easy fix? Any help would be appreciated.
1521630884

Edited 1521631093
GiGs
Pro
Sheet Author
API Scripter
Using obj.get("status_red") will return true, false, or a number as a string. So you cant perform mathematical operations on it without first identifying its value. When you aren't sure where the error is, it's a good idea to extract every value you are using into a variable, so you can print that variable to the log to check where the error is. Does this work? on("change:token", function(obj){ if(isNaN(obj.get("bar1_max")) || isNaN(obj.get("bar1_value")) ) return; // if the target's hp falls to zero or below, add a wound counter // and increase their hp by maximum if(obj.get("bar1_value") <= 0) { let skull = obj.get("status_skull"); let marks; if (skull === false) { marks = true; } else if (skull === true) { marks = "2"; } else { marks = String(parseInt(skull) +1); } obj.set("status_skull",marks); obj.set("bar1_value",obj.get("bar1_value") + obj.get("bar1_max")); } }); I assume obj.set("status_skull",marks); works if marks = true, but if not you might need to change it to if(isNaN(marks)) obj.set("status_skull"); // or if(marks === true) obj.set("status_skull"); else obj.set("status_skull",marks); How do you plan to handle the HP healing? I guess you could use the same process in reverse if HP exceed max bar.
Thanks for the help! On further consideration, I think it's best to instantiate an array of hit point segments and then write special conditionals for the marginal cases. This seems to do what I'm looking for...can't say it's elegant though. on("change:token", function (obj) { // if there is no hp bar defined, return if(isNaN(obj.get("bar1_max")) || isNaN(obj.get("bar1_value")) ) return; // set up an array of hp segments var hp_vec = []; var hp_seg = obj.get("bar1_max") / 6; for (let ii = 0; ii < 6; ii++) { hp_vec.push(ii*hp_seg); } // adjudicate "dead" status markers if (obj.get("bar1_value") <= 0) { obj.set("status_skull","6"); obj.set("status_dead",true); return } else { obj.set("status_dead",false); } // adjudicate "skull" status markers if (obj.get("bar1_value") > hp_vec[6]) { obj.set("status_skull",false); } for (let ii = 0; ii < 6; ii++) { if (obj.get("bar1_value") <= hp_vec[ii]) { let nWounds = 6 - ii; obj.set("status_skull",nWounds.toString()); return; } } })
1521659439

Edited 1521660154
The Aaron
Pro
API Scripter
Nice, I was going to suggest doing ranges but got distracted with other things.  I believe you can actually simplify this a little and get rid of the array and the loops: on("change:token", function (obj) {     let hp_curr = obj.get("bar1_value");     // if there is no hp bar defined, return     if(isNaN(hp_curr) || isNaN(hp_curr) ) { return; }     let hp_seg  = obj.get("bar1_max") / 6;     // adjudicate "dead" status markers     if (hp_curr <= 0) {         obj.set("status_skull","6");         obj.set("status_dead",true);         return;     } else {         obj.set("status_dead",false);     }     // adjudicate "skull" status markers     if (hp_curr > (5 * hp_seg) ) {         obj.set("status_skull",false);     }     obj.set("status_skull", (6-Math.floor(hp_curr/hp_seg)).toString()); }); Also, I think   if (obj.get("bar1_value") > hp_vec[6]) { is an error, index 6 would be undefined, the array was being defined from 0 to 5 by the loop.
1521659556

Edited 1521659596
GiGs
Pro
Sheet Author
API Scripter
Great!  Shouldnt this if (obj.get("bar1_value") > hp_vec[6]) { be if (obj.get("bar1_value") > hp_vec[5]) { ? Edit: Ninja'd!