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

November 21 (3 years ago)

Edited January 17 (3 years ago)
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.
 * http://www.gnu.org/licenses/gpl.html
 * 
 * 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(">> 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(">> Health Status: OFF");
                    sendChat('HS','/w gm OFF');
                    resetAllTokens();
    
                } else { 
                    state.healthstat.run_state = true;
                    log(">> Health Status: ON");
                    sendChat('HS','/w gm ON');
                }
            }
        }
 
	},

	handleToken = function(obj) {
        if(
               'graphic' == obj.get('type') 
            && 'token'   == obj.get('subtype') 
            && ''        != obj.get('bar1_max')
        ) 
        {
            if(state.healthstat.run_state && !(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(">> Health Status: "+token_name+" - at "+token_hp_percent+"%");
                
                resetStatus(obj); // Clear all unused status markers
                
                if(token_hp_percent >= 100) {
                    // Remove Icon at 100% health
                     resetStatus(obj);
                } else if (token_hp_percent >= 75 && token_hp_percent <= 99) {
                    // Green Health
                    obj.set("status_green",true);
                }else if (token_hp_percent >= 50 && token_hp_percent <= 74) {
                    // Yellow Health
                    obj.set("status_yellow",true);
                } else if (token_hp_percent >= 25 && token_hp_percent <= 49) {
                    // Brown Health
                    obj.set("status_brown",true);
                } else if (token_hp_percent >= 1 && token_hp_percent <= 24) {
                    // Red Health
                    obj.set("status_red",true);
                } else if (token_hp_percent <= 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 => {
              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("-=> Health State Script v"+state.healthstat.version+" Initialized <=-")
	},

    	
	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();
});
January 14 (3 years ago)

Edited January 14 (3 years ago)

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. 

   if(token_hp_percent >= 100) {

                    // Green Health

                    obj.set("status_green",true);

                } else if (token_hp_percent >= 70 && token_hp_percent <= 99) {

                    // Yellow Health

                    obj.set("status_yellow",true);

                } else if (token_hp_percent >= 35 && token_hp_percent <= 69) {

                    // Brown Health

                    obj.set("status_brown",true);

                } else if (token_hp_percent >= 1 && token_hp_percent <= 34) {

                    // Red Health

                    obj.set("status_red",true);

                } else if (token_hp_percent <= 0 ) {

                    // Dead

                    obj.set("status_dead",true);

January 16 (3 years ago)

This is great.  I wanted something a simpler than DeathTracker so I could customize the thresholds and this fits the bill.  I do have one weird situation though.  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.  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 > and <.  For example:

} else if (token_hp_percent > 50 && token_hp_percent < 75) {
                    // Yellow Health
                    obj.set("status_yellow",true);

Otherwise you'll get some limbo hp totals that don't assign a status marker.

January 16 (3 years ago)

Edited January 17 (3 years ago)
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);
January 17 (3 years ago)

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.  Roll20 doesn't round this so it falls between your thresholds for brown and yellow and thus doesn't match either's criteria.  I propose you change it to:

                if(token_hp_percent >= 75) {
// Green Health
obj.set("status_green",true);
} else if (token_hp_percent >= 50 && token_hp_percent < 75) {
// Yellow Health
obj.set("status_yellow",true);
} else if (token_hp_percent >= 25 && token_hp_percent < 50) {
// Brown Health
obj.set("status_brown",true);
} else if (token_hp_percent > 0 && token_hp_percent < 25) {
// Red Health
obj.set("status_red",true);


January 17 (3 years ago)

Edited January 17 (3 years ago)
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.

January 17 (3 years ago)

Edited January 17 (3 years ago)
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 & 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.

January 18 (3 years ago)
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. 

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.


January 18 (3 years ago)
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.

January 19 (3 years ago)
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: https://app.roll20.net/forum/post/10616314/attempting-an-api-to-change-the-bars-color-by-hp-percent/?pageforid=10616314#post-10616314

Thanks again for the inspiration, and a starting point to build my own API. Great Work!