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] HealthState

1637520588

Edited 1642405901
Finds
API Scripter
HealthState This is a simple script I wrote for my campaign because I didn't like showing actual health-bars with numbers so instead as a tokens HP changes it will add a colored status marker to the token indicating it's general health; starting with green down to red and when they drop below 1 it marks them with the death X marker. Commands !hs - when used without a selection it will toggle the mod functionality ON or OFF and display this as a whisper in chat. If you have tokens selected when using this command it will cycle through them all and update them with the current color status (useful if you have a whole page of tokens not yet marked). Tokens will update when you change their HP value automatically. Sidenotes: If you have bar1 visible to players the token will not update with a status marker, if you hide bar1 from players it will then update status markers. Tokens at 100% or greater health will not display any health status marker When you toggle the mod OFF it will remove all colored status markers on tokens on the Active Player page only, not specifically the page your currently looking at as a GM. (click to view full animated GIF) Updated to (0.5.2) /** * healthstate.js * * * Copyright 2018: Ben F. * Licensed under the GPL Version 3 license. * <a href="http://www.gnu.org/licenses/gpl.html" rel="nofollow">http://www.gnu.org/licenses/gpl.html</a> * * This script is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This script is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * To use type !hs to enable/disable the script, when enabled... * - A token with a health and max health will be flagged with * a color status indicating health state, using green,yellow, * brown,red and finally X (dead). * - Hiding/Showing bar1 to players will enable/disable this effect * on tokens. If players can see bar1 they won't see a status color. * */ var healthstat = healthstat || (function(){ 'use strict'; var handleInput = function(msg) { if ( "api" !== msg.type ) { return; } // Disable/Enable the script activity with !hs if(msg.content.indexOf("!hs") !== -1 ) { if(msg.selected){ log("&gt;&gt; Health Status: Update Selected Tokens"); sendChat('HS','/w gm Updating Selected'); _.each(msg.selected, function(obj) { var token = getObj(obj._type, obj._id); if(state.healthstat.run_state) { handleToken(token); } else { resetStatus(token); } }); } else { if(state.healthstat.run_state) { state.healthstat.run_state = false; log("&gt;&gt; Health Status: OFF"); sendChat('HS','/w gm OFF'); resetAllTokens(); } else { state.healthstat.run_state = true; log("&gt;&gt; Health Status: ON"); sendChat('HS','/w gm ON'); } } } }, handleToken = function(obj) { if( 'graphic' == obj.get('type') &amp;&amp; 'token' == obj.get('subtype') &amp;&amp; '' != obj.get('bar1_max') ) { if(state.healthstat.run_state &amp;&amp; !(obj.get('showplayers_bar1'))) { // If a token with a bar1_max value, show bar1 to players is false, // and the state.run_state is true continue. var token_name = obj.get('name') || ''; var token_hp = (obj.get('bar1_value')) || 0; var token_hp_max = (obj.get('bar1_max')) || 0; // Exit if one of the variables is missing if(!(token_hp_max)) { return; } // Calculate the % of health remaining var token_hp_percent = Math.ceil(((token_hp/token_hp_max)*100)); //log("&gt;&gt; Health Status: "+token_name+" - at "+token_hp_percent+"%"); resetStatus(obj); // Clear all unused status markers if(token_hp_percent &gt;= 100) { // Remove Icon at 100% health resetStatus(obj); } else if (token_hp_percent &gt;= 75 &amp;&amp; token_hp_percent &lt;= 99) { // Green Health obj.set("status_green",true); }else if (token_hp_percent &gt;= 50 &amp;&amp; token_hp_percent &lt;= 74) { // Yellow Health obj.set("status_yellow",true); } else if (token_hp_percent &gt;= 25 &amp;&amp; token_hp_percent &lt;= 49) { // Brown Health obj.set("status_brown",true); } else if (token_hp_percent &gt;= 1 &amp;&amp; token_hp_percent &lt;= 24) { // Red Health obj.set("status_red",true); } else if (token_hp_percent &lt;= 0 ) { // Dead obj.set("status_dead",true); } } else { resetStatus(obj); // Clear all unused status markers } } }, resetStatus = function(obj) { obj.set("status_dead",false); obj.set("status_red",false); obj.set("status_brown",false); obj.set("status_yellow",false); obj.set("status_green",false); }, getAllTokens = function(pageID) { // Function to get all tokens let token_objects = findObjs({ _pageid: pageID, _type: 'graphic', _subtype: 'token', }); return token_objects; }, resetAllTokens = function() { var resetAll = getAllTokens(Campaign().get('playerpageid')) .forEach(token =&gt; { resetStatus(token); }); }, checkInstall = function() { var script_version = "0.5.2"; if( ! state.healthstat ) { state.healthstat = { version: script_version, run_state: true, }; } if (state.healthstat.version != script_version) state.healthstat.version = script_version; log("-=&gt; Health State Script v"+state.healthstat.version+" Initialized &lt;=-") }, registerEventHandlers = function() { on('chat:message', handleInput); on('change:graphic:bar1_value', handleToken); on('change:graphic:showplayers_bar1', handleToken); }; return { CheckInstall: checkInstall, RegisterEventHandlers: registerEventHandlers }; }()); on("ready", function() { 'use strict'; healthstat.CheckInstall(); healthstat.RegisterEventHandlers(); });
1642128461

Edited 1642128538
Thanks for writing this. I am using it and made a slight modification so that green only shows if the token has 100% HP. I adjusted the other thresholds accordingly.&nbsp; &nbsp; &nbsp;if(token_hp_percent &gt;= 100) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Green Health &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; obj.set("status_green",true); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else if (token_hp_percent &gt;= 70 &amp;&amp; token_hp_percent &lt;= 99) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Yellow Health &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; obj.set("status_yellow",true); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else if (token_hp_percent &gt;= 35 &amp;&amp; token_hp_percent &lt;= 69) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Brown Health &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; obj.set("status_brown",true); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else if (token_hp_percent &gt;= 1 &amp;&amp; token_hp_percent &lt;= 34) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Red Health &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; obj.set("status_red",true); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else if (token_hp_percent &lt;= 0 ) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Dead &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; obj.set("status_dead",true);
This is great.&nbsp; I wanted something a simpler than DeathTracker so I could customize the thresholds and this fits the bill.&nbsp; I do have one weird situation though.&nbsp; When reducing a token's hp from one to zero by typing "-1" directly into the bar1 bubble, it does not trigger the status_dead condition, however dropping below zero and/or typing a direct value of "0" into the bar1 bubble does trigger the condition.&nbsp; Any idea what's going on there? Also, for characters with more than 100 hp, you'll want to change the logic for each level to use &gt; and &lt;.&nbsp; For example: } else if (token_hp_percent &gt; 50 &amp;&amp; token_hp_percent &lt; 75) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Yellow Health &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; obj.set("status_yellow",true); Otherwise you'll get some limbo hp totals that don't assign a status marker.
1642368614

Edited 1642382097
Finds
API Scripter
So this line (below) which was safeguarding against missing values was exiting the function because (token_hp) health was 0 and it saw 0 as a false, I removed the check for the token_hp and that fixed the issue just checking for a max_hp. I updated the above script to 0.5.1. // Exit if one of the variables is missing if(!(token_hp) || !(token_hp_max)) { return; } Not sure what you mean by changing the logic, the logic in the script works for the values there even with values of HP over 100 as it's based off calculated percent between hp and hp_max. // Calculate the % of health remaining var token_hp_percent = ((token_hp/token_hp_max)*100);
Thank you, that changed worked perfectly! Regarding my limbo example, if a creature has 200hp, and takes 101 damage, then token_hp_percent = 49.5.&nbsp; Roll20 doesn't round this so it falls between your thresholds for brown and yellow and thus doesn't match either's criteria.&nbsp; I propose you change it to: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(token_hp_percent &gt;= 75) { // Green Health obj.set("status_green",true); } else if (token_hp_percent &gt;= 50 &amp;&amp; token_hp_percent &lt; 75) { // Yellow Health obj.set("status_yellow",true); } else if (token_hp_percent &gt;= 25 &amp;&amp; token_hp_percent &lt; 50) { // Brown Health obj.set("status_brown",true); } else if (token_hp_percent &gt; 0 &amp;&amp; token_hp_percent &lt; 25) { // Red Health obj.set("status_red",true);
1642391120

Edited 1642391317
Finds
API Scripter
Ahh I see what you mean hadn't noticed that, good catch! I just updated the percent calculation to round up as a quick edit. (code above updated) Thanks for pointing that out. // Calculate the % of health remaining var token_hp_percent = Math.ceil(((token_hp/token_hp_max)*100)); (note) I added a tweak that removes the health icon if the token health is at 100% or greater, I found it distracting to have everything update with a green dot.
1642459268

Edited 1642462482
MAMS Gaming
Pro
Sheet Author
Would it be possible to use this script to create one that changes the color of the bar instead of the statusmarkers? If I knew the code to use the token to access the stats on the sheet, and the code to associate/disassociate those values with each bar, we could set health &amp; health_max to bar1 when "green", bar2 when "yellow", and bar3 when "red". That way, only 1 bar would show up at a time. Sure this only works if the game only uses 1 bar, but it would be super cool for my SCRPG game, where it actually matters if you are in green, yellow, or red.
1642472556
Finds
API Scripter
You could do something like that but as you say you would lose access to your 2nd and 3rd bar. This would mean adding triggers for the other bars, ensuring all bars are updated when one of their values change (for tokens not associated with a char. sheet). You would probably also need to set it up to remove the max_hp value from bars not in use otherwise you'll see all 3 bars as a GM on each token which probably won't look pretty.&nbsp; Before we follow this train of though too far, does it have to be a bar coloration? Tinting the token or putting a short aura affect around it with a color would be much easier to code and still get some neat looking effects that would still move with the token.
1642478594
MAMS Gaming
Pro
Sheet Author
SCRPG doesn't use a battlemap, and the tokens are stationary. Our game uses 2x3 rectangles. Either Circles or Squares looks awkward. We tested your status markers tonight, and the group liked them, so if it'll be too tricky to pull off changing bar colors, it's not necessary. It would be super cool if we can pull it off though.
1642630736
MAMS Gaming
Pro
Sheet Author
Well, I spent the past 2 days tinkering, and came up with a script that works as long I don't try to link it to the sheet. It's nice, but I would have to keep reminding the players to change the bars instead of their sheets. I don't want to clog this thread with my project, so I branched to new thread:&nbsp; <a href="https://app.roll20.net/forum/post/10616314/attempting-an-api-to-change-the-bars-color-by-hp-percent/?pageforid=10616314#post-10616314" rel="nofollow">https://app.roll20.net/forum/post/10616314/attempting-an-api-to-change-the-bars-color-by-hp-percent/?pageforid=10616314#post-10616314</a> Thanks again for the inspiration, and a starting point to build my own API. Great Work!