Here's a script that should do what you want. It's set up to look for health in bar 3 and update bar 1 with the fuzzy health. You can edit lines 3-6 to configure it: const HEALTH_BAR = 3; //< Where to find the health of the token
const DISPLAY_BAR = 1; //< Where to put the fuzzy health on the token
const SEGMENTS = 5; //< How many "levels" of fuzzy health
const SEGMENT_GONE_AT_HALF = false; //< Where to consider a segment "gone"
SEGMENTS controls how many subdivisions there are for the fuzzy health bar. If you set this to 100, you'll get a percentage for your fuzzy health bar. SEGMENT_GONE_AT_HALF controls when a segment is removed. Default is when it is all gone, for example: 50/50 hp menas 5/5 segments 44/50 hp means 5/5 segments when false, or 4/5 when true 1/50 hp means 1/5 segments when false, or 0/5 when true On startup it will apply the bars to all tokens in the game that are NPCs, where NPC is defined as a token that can't be controlled by a player (either on the token or the character it represents). NOTE: this will change bar 1 by default on all NPC tokens, so be sure you have configured it for your game before saving it in the API! It supports TokenMod, so if you are changing token health with that, it will update correctly. Here's the code: on('ready',()=>{
const HEALTH_BAR = 3; //< Where to find the health of the token
const DISPLAY_BAR = 1; //< Where to put the fuzzy health on the token
const SEGMENTS = 5; //< How many "levels" of fuzzy health
const SEGMENT_GONE_AT_HALF = false; //< Where to consider a segment "gone"
const bound = (v, max, min) => Math.min(max,Math.max(min,v));
const isNPC = (o) => {
if(undefined !== o && 'function' === typeof o.get){
switch(o.get('type')){
case 'graphic':
if(o.get('represents')){
let c = getObj('character',o.get('represents'));
return isNPC(c);
}
return (0 === o.get('controlledby').length);
case 'character':
return (0 === o.get('controlledby').length);
default:
return; // undefined is indepterminate
}
}
return; // undefined is indepterminate
};
const dress_func = (SEGMENT_GONE_AT_HALF ? Math.round : Math.ceil);
const UpdateTooltipOnToken = (t) => {
let ratio = (parseFloat(t.get(`bar${HEALTH_BAR}_value`))||0)/(parseFloat(t.get(`bar${HEALTH_BAR}_max`))||1);
t.set({
[`bar${DISPLAY_BAR}_value`]: dress_func(bound( (SEGMENTS*ratio), SEGMENTS, 0)),
[`bar${DISPLAY_BAR}_max`]: SEGMENTS,
[`showplayers_bar${DISPLAY_BAR}`]: true,
[`playersedit_bar${DISPLAY_BAR}`]: true
});
};
const changeHandler = (t,p)=>{
if(t.get('represents')!==''){
if(
isNPC(t) &&
((t.get(`bar${HEALTH_BAR}_value`)!== p[`bar${HEALTH_BAR}_value`]) ||
(t.get(`bar${HEALTH_BAR}_max`)!== p[`bar${HEALTH_BAR}_max`]))
){
UpdateTooltipOnToken(t);
}
}
};
const addHandler = (t,p)=>{
if(t.get('represents')!==''){
setTimeout(()=>{
if(isNPC(t)){
UpdateTooltipOnToken(t);
}
},300);
}
};
on('add:graphic',addHandler);
on('change:graphic',changeHandler);
if('undefined' !== typeof TokenMod && TokenMod.ObserveTokenChange){
TokenMod.ObserveTokenChange(changeHandler);
}
const applyToTokens = ()=>{
let tokens = findObjs({type:'graphic'}).filter(t=>t.get('represents')!=='');
const burndown = ()=>{
let t = tokens.shift();
if(t){
if(isNPC(t)){
UpdateTooltipOnToken(t);
}
setTimeout(burndown,0);
} else {
log('All tokens have updated fuzzy health bars.');
}
};
burndown();
}
applyToTokens();
});