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

DropAtZeroHealth script with ApplyDamage

I found a script from The Aaron in an old thread which applies the dead statusmarker on non-PC tokens when HP drops to 0. It also removes them from the initiative turn order.  Exactly what I wanted!  And it works great when manually adjusting health.  BUT, I'm trying to figure out how to update the ApplyDamage script so that the dead statusmarker is applied even when the damage is applied from the GroupStatus/ApplyDamage script.   I see this additional comment from Aaron which I think is what is needed: and adding this after line 91 of ApplyChange: if('undefined' !== typeof DropAtZeroHealth && DropAtZeroHealth.OnChangeGraphic) {   DropAtZeroHealth.OnChangeGraphic(token, prev); } So, I need to add these lines into the ApplyDamage script after line 91?  Like this?  Or did I completely misunderstand how to get these two scripts working together?     finalApply = (results, dmg, type, bar, status) => {       const barCur = `bar${bar}_value`,         barMax = `bar${bar}_max`;       Object.entries(results).forEach(([id, saved]) => {         const token = getObj("graphic", id),           prev = JSON.parse(JSON.stringify(token || {}));         let newValue;         if (token && !saved) {           if (boundedBar) {             newValue = Math.min(Math.max(parseInt(token.get(barCur)) - dmg, 0), parseInt(token.get(barMax)));           } else {             newValue = parseInt(token.get(barCur)) - dmg;           }           if (status) token.set(`status_${status}`, true);         }         else if (token && type === "half") {           if (boundedBar) {             newValue = Math.min(Math.max(parseInt(token.get(barCur)) - Math.floor(dmg / 2), 0), parseInt(token.get(barMax)));           } else {             newValue = parseInt(token.get(barCur)) - Math.floor(dmg / 2);           }           if('undefined' !== typeof DropAtZeroHealth && DropAtZeroHealth.OnChangeGraphic) {             DropAtZeroHealth.OnChangeGraphic(token, prev);             }         }         if (!_.isUndefined(newValue)) {           if (Number.isNaN(newValue)) newValue = token.get(barCur);           token.set(barCur, newValue);           notifyObservers("change", token, prev);         }       });     },
1625514171

Edited 1625537675
The Aaron
Roll20 Production Team
API Scripter
I'd go the other direction and change the DropAtZeroHealth script: /* global TokenMod, ApplyDamage, AlterScript */ on('ready', ()=>{ const threshold = 0; const bar = 3; const isPlayerToken = (obj) => { let players = obj.get('controlledby') .split(/,/) .filter(s=>s.length); if( players.includes('all') || players.filter((p)=>!playerIsGM(p)).length ) { return true; } if('' !== obj.get('represents') ) { players = (getObj('character',obj.get('represents')) || {get: function(){return '';} } ) .get('controlledby').split(/,/) .filter(s=>s.length); return players.includes('all') || players.filter((p)=>!playerIsGM(p)).length ; } return false; }; const onChangeGraphic = (obj,prev) => { const healthBar = `bar${bar}_value`; const val = parseInt(obj.get(healthBar)); if(!Number.isNaN(val) && val !== parseInt(prev[healthBar]) && val <= threshold ){ if(!isPlayerToken(obj)){ let turnorder = Campaign().get('turnorder'); turnorder = ('' === turnorder) ? [] : JSON.parse(turnorder); turnorder = turnorder.filter((i)=>i.id !== obj.id); Campaign().set('turnorder',JSON.stringify(turnorder)); } obj.set('statusmarkers','dead'); } }; const registerEvents = () => { on('change:graphic', onChangeGraphic); if('undefined' !== typeof TokenMod && TokenMod.ObserveTokenChange){ TokenMod.ObserveTokenChange(onChangeGraphic); } if('undefined' !== typeof ApplyDamage && ApplyDamage.registerObserver){ ApplyDamage.registerObserver('change',onChangeGraphic); } if('undefined' !== typeof AlterScript && AlterScript.observeTokenChange){ AlterScript.observeTokenChange(onChangeGraphic); } }; registerEvents(); }); I also added support for TokenMod, in case you're changing the hp with that.
Thanks Aaron!  This works great.  And I do use Tokenmod for some manipulation of healthbars, so thanks for adding that as well.  I really appreciate what you do for the community here.  
Does this work with alterbars? Also does health/hp have to be a specific bar?
Peacekeeper B said: Does this work with alterbars? Also does health/hp have to be a specific bar? I’m not sure about alterbars.  But you can set which bar you use for hp in line 4. I changed to bar 1 for me. 
1625537714
The Aaron
Roll20 Production Team
API Scripter
Peacekeeper B said: Does this work with alterbars? Also does health/hp have to be a specific bar? If you use this version of AlterBars and the edited version of DropFromInitAtZero above, it will work: // VERSION INFO var AlterBars_Author = "SkyCaptainXIII"; var AlterBars_Version = "2.0.5"; var AlterBars_LastUpdated = 1494507176; // FUNCTION DECLARATION var AlterScript = AlterScript || {}; on("chat:message", function (msg) { if (msg.type != "api") return; if (msg.content.split(" ", 1)[0] === "!alter") { msg.who = msg.who.replace(" (GM)", ""); msg.content = msg.content.replace(/<br\/>\n/g, ' ').replace(/({{(.*?)}})/g, " $2 "); if (msg.content.indexOf("--target") == -1) { sendChat("ERROR", "/w " + msg.who + " Your macro does not have a target specified."); return; } AlterScript.Process(msg); } }); on("ready", function () { log("-=> AlterBars v" + AlterBars_Version + " <=- [" + (new Date(AlterBars_LastUpdated * 1000)) + "]"); //log (Date.now().toString().substr(0, 10)); }); AlterScript.observers = { tokenChange: [] }; AlterScript.observeTokenChange = function(handler){ if(handler && _.isFunction(handler)){ AlterScript.observers.tokenChange.push(handler); } }; // FUNCTIONS AlterScript.Process = function (msg) { // BAR CONFIGURATION - These are used to identify which bar to adjust. You can // also use any lowercase letters as well such as 'h' or 'hp' for hit points/health. var Bar1Key = "1"; var Bar2Key = "2"; var Bar3Key = "3"; var BarGain = ["gains", "point", "points"]; var BarLoss = ["loses", "point", "points"]; // OUTPUT FORMAT - The following variables are used to define how AlterBar // sends the output to chat. The first item is the verb, the second is the // singular name of the 'bar' and the third item is the plural version. // Example: Paladin heals 1 hit point. // Example: Paladin heals 8 hit points. // Example: Mage spends 3 mana. var Bar1Gain = ["heals", "hit point", "hit points"]; var Bar1Loss = ["takes", "damage", "damage"]; var Bar2Gain = ["recovers", "mana", "mana"] var Bar2Loss = ["spends", "mana", "mana"] var Bar3Gain = ["heals", "hit point", "hit points"]; var Bar3Loss = ["takes", "damage", "damage"]; // ALTCOLORS - This uses a dark green or dark red emote for gain / loss instead // of the default orange for both. var ALT_COLORS = true; // PREVENT OVERMAX - Set this variable to true to prevent the current value of // the bar from being greater than its max value. If there is no max value set, // it will not stop the current bar value from increasing. var PREVENT_OVERMAX = true; // STOP AT ZERO - Prevents the current value of the bar from dropping below zero. var STOP_AT_ZERO = true; // ANNOUNCE CHANGE IN CHAT - Set to true to send a message to the chat window // showing which token gained or lost points and how much. var ANNOUNCE_CHANGE = true; // SEND TO GM - Set to true to send the results to the GM. This will also trigger // if a hidden change is sent. var ALERT_GM = false; // Variables stuff... var n = msg.content.split(/\s+--/); var who = msg.who; var Target = ""; var Bar = 0; var AlterValue = 0; const simpleObject = (o) => JSON.parse(JSON.stringify(o)); const notifyObservers = function(event,obj,prev){ _.each(AlterScript.observers[event],function(handler){ handler(obj,prev); }); }; // Pull variables from n... n.shift(); _.each(n, function (a) { Tag = a.substring(0, a.indexOf("|")).trim(); Content = a.substring(a.indexOf("|") + 1).trim(); if (Tag.toLowerCase() == "target") Target = getObj("graphic", Content); if (Tag.toLowerCase() == "bar") Bar = Content; if (Tag.toLowerCase() == "amount") AlterValue = Content; if (Tag.toLowerCase() == "show" && Content.toLowerCase() == "gm") { ANNOUNCE_CHANGE = false; ALERT_GM = true; } if (Tag.toLowerCase() == "show" && Content.toLowerCase() == "none") { ANNOUNCE_CHANGE = false; ALERT_GM = false; } }); if(!Target) { sendChat("ERROR", `/w "${who}" Not a valid target id.`); return; } if (Bar == 1 || Bar == Bar1Key) { BarGain = Bar1Gain; BarLoss = Bar1Loss; } else if (Bar == 2 || Bar == Bar2Key) { BarGain = Bar2Gain; BarLoss = Bar2Loss; } else if (Bar == 3 || Bar == Bar3Key) { BarGain = Bar3Gain; BarLoss = Bar3Loss; } else { sendChat("ERROR", "/w " + who + " That is not a valid bar."); return; } // Get current and max values from the token... var CurrentValue = parseInt(Target.get("bar" + Bar + "_value")); var MaxValue = parseInt(Target.get("bar" + Bar + "_max")); // Set current values as previous values for transit to Tint/Aura HealthColors... var Previous = JSON.parse(JSON.stringify(Target)); // Check for a + or - sign... var Operand1 = AlterValue.charAt(0); var Operand2 = AlterValue.charAt(1); if (Operand2 === "+" || Operand2 === "-") AlterValue = AlterValue.substring(2); else if (Operand1 === "+" || Operand1 === "-") AlterValue = AlterValue.substring(1); // Save the value for the tooltip... var Expression = AlterValue; // Define CSS... var AlertGainStyle = "max-height: 20px; width: 100%; padding: 3px 40px 6px 0px; border: 1px solid #000; border-radius: 4px; background-color: " + + "; background-image: linear-gradient(rgba(255, 255, 255, .3), rgba(255, 255, 255, 0)); font-family: Candal; font-style: normal; font-size: 1.05em; line-height: 1em; color: #FFF; font-weight: normal; text-align: left; text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;"; var AlertLossStyle = "max-height: 20px; width: 100%; padding: 3px 0px 6px 0px; border: 1px solid #000; border-radius: 4px; background-color: " + ((!ALT_COLORS) ? "#BF5700" : "#440000") + "; background-image: linear-gradient(rgba(255, 255, 255, .3), rgba(255, 255, 255, 0)); font-family: Candal; font-style: normal; font-size: 1.05em; line-height: 1em; color: #FFF; font-weight: normal; text-align: left; text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;"; var AlertOuterStyle = "max-height: 40px; width: 100%; margin: 10px 0px 5px -7px; line-height: 40px;"; var AlertImageStyle = "height: 40px; width: 40px; float: right; margin: -11px 5px 0px 0px;"; // Main process... sendChat("", "/r " + AlterValue, function (outs) { let prev = simpleObject(Target); AlterValue = parseInt(JSON.parse(outs[0].content).total); var Tooltip = "Rolling " + Expression + " = " + AlterValue + "' class='a inlinerollresult showtip tipsy-n'"; var TargetName = (Target.get("name") == "" || Target.get("showplayers_name") == false && !ALERT_GM) ? "NPC" : Target.get("name"); if (Operand1 != "-") { // Add to bar... if (PREVENT_OVERMAX) AlterValue = (AlterValue + CurrentValue > MaxValue) ? MaxValue - CurrentValue : AlterValue; var AlertGain = "" + "<div style='display: block; margin-left: -7px; margin-right: 2px; padding: 2px 0px;'>" + "<div style='position: relative; border: 1px solid #000; border-radius: 5px; background-color: " + ((!ALT_COLORS) ? "#BF5700" : "#004400") + "; background-image: linear-gradient(rgba(255, 255, 255, .3), rgba(255, 255, 255, 0)); margin-right: -2px; padding: 2px 5px 5px 50px;'>" + "<div style='position: absolute; top: -10px; left: 5px; height: 40px; width: 40px;'>" + "<img src='" + Target.get("imgsrc") + "' style='height: 40px; width: 40px;'></img>" + "</div>" + "<div style='font-family: Candal; font-size: 13px; line-height: 15px; color: #FFF; font-weight: normal; text-align: center; text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;'>" + TargetName + " " + BarGain[0] + " " + AlterValue + " " + ((AlterValue == 1) ? BarGain[1] : BarGain[2]) + "." + "</div>" + "</div>" + "</div>"; if (ANNOUNCE_CHANGE) sendChat("", "/desc " + AlertGain); if (ALERT_GM) sendChat(who, "/w GM " + AlertGain); Target.set("bar" + Bar + "_value", CurrentValue += AlterValue); notifyObservers('tokenChange',Target,prev); } else { // Subtract from bar... var AlertLoss = "" + "<div style='display: block; margin-left: -7px; margin-right: 2px; padding: 2px 0px;'>" + "<div style='position: relative; border: 1px solid #000; border-radius: 5px; background-color: " + ((!ALT_COLORS) ? "#BF5700" : "#440000") + "; background-image: linear-gradient(rgba(255, 255, 255, .3), rgba(255, 255, 255, 0)); margin-right: -2px; padding: 2px 5px 5px 50px;'>" + "<div style='position: absolute; top: -10px; left: 5px; height: 40px; width: 40px;'>" + "<img src='" + Target.get("imgsrc") + "' style='height: 40px; width: 40px;'></img>" + "</div>" + "<div style='font-family: Candal; font-size: 13px; line-height: 15px; color: #FFF; font-weight: normal; text-align: center; text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;'>" + TargetName + " " + BarLoss[0] + " " + AlterValue + " " + ((AlterValue == 1) ? BarLoss[1] : BarLoss[2]) + "." + "</div>" + "</div>" + "</div>"; if (ANNOUNCE_CHANGE) sendChat("", "/desc " + AlertLoss); if (ALERT_GM) sendChat(who, "/w GM " + AlertLoss); if (STOP_AT_ZERO && (CurrentValue - AlterValue < 0)) AlterValue = CurrentValue; Target.set("bar" + Bar + "_value", CurrentValue -= AlterValue); notifyObservers('tokenChange',Target,prev); } if ('undefined' !== typeof HealthColors && HealthColors.Update) HealthColors.Update(Target, Previous); }); };