
Evening, I am using a few scripts, that all work together fairly well. But I would like to add AlterBars script to the mix The scripts I am using that work well are Aura/Tint Health Colors and a Hp Bar Management script (by The Aaron). Those work perfectly together. The problem I am running in to is having the Hp Bar Management script register changes made through the AlterBars script. I am running the Health Colors script and trying to run the AlterBars script, both modified to work with each other. Not being a coder, I don't know what to add to what script to have the Hp Bar Management script watch for changes made by the AlterBars script. Below are the two scripts in question = Hp Bar Management code first (modified for Pathfinder 1e with Mythic Rules)...and the AlterBars script second. Sorry for the long post (due to the scripts, and no "spoiler" to hide them) /* global TokenMod, ChatSetAttr */
on('ready', () => {
// Configuration parameters
const HPBarNum = 1;
const TempHPMarker = 'green';
const DeadMarker = 'dead';
const BloodiedMarker = 'red';
const TempHPAttributeName = 'hp_temp';
const UseBloodied=true;
//New Constants
const DyingAt = 0;
const DisableMarker = 'half-heart';
const DyingMarker = 'skull';
const NegativeDeadAtAttr = 'constitution';
// Cases for Mythic Tier and Greater Tenacity
const MythicYN = 'Mythic-Tier';
const MasterLvl = 'Master-Level';
/////////////////////////////////////////////
const clearURL = /images\/4277467\/iQYjFOsYC5JsuOPUCI9RGA\/.*.png/;
const bar = `bar${HPBarNum}_value`;
const lnk = `bar${HPBarNum}_link`;
const max = `bar${HPBarNum}_max`;
const unpackSM = (stats) => stats.split(/,/).reduce((m,v) => {
let p = v.split(/@/);
let n = parseInt(p[1] || '0', 10);
if(p[0].length) {
m[p[0]] = Math.max(n, m[p[0]] || 0);
}
return m;
},{});
const packSM = (o) => Object.keys(o)
.map(k => ('dead' === k || true === o[k] || o[k]<1 || o[k]>9) ? k : `${k}@${parseInt(o[k])}` ).join(',');
const checkTempHP = (obj) => {
let v = parseFloat(obj.get('current'));
findObjs({
type: 'graphic',
represents: obj.get('characterid')
})
.filter( (t) => t.get(lnk) !== '')
.filter( (t) => !clearURL.test(t.get('imgsrc') ) )
.forEach((g)=>{
let sm = unpackSM(g.get('statusmarkers'));
if(v>0){
sm[TempHPMarker]=v;
} else {
delete sm[TempHPMarker];
}
g.set({
statusmarkers: packSM(sm)
});
});
};
const assureTempHPMarkers = () => {
let queue = findObjs({
type: 'attribute',
name: TempHPAttributeName
});
const burndownQueue = ()=>{
if(queue.length){
let attr = queue.shift();
checkTempHP(attr);
setTimeout(burndownQueue,0);
}
};
burndownQueue();
};
const temporalTempHPCache = {};
const accountForHPBarChange = (obj,prev) => {
// 1. did hp change and is it a scale
const hpMax = parseInt(obj.get(max),10);
let hp = parseInt(obj.get(bar),10);
const diff = hp-parseFloat(prev[bar]);
if( !isNaN(hpMax) && diff !== 0 ) {
let changes = {};
// 2. does it represent a character
// 3. does the hp bar represent an attribute
const character = getObj('character',obj.get('represents'));
if( diff < 0 && character && obj.get(lnk)!=='' ){
// 4. is there temp hp
const temp_hp = findObjs({
type: 'attribute',
characterid: character.id,
name: TempHPAttributeName
})[0];
if( temp_hp ) {
const now = Date.now();
const tempHP = parseFloat(temp_hp.get('current'))||0;
const newTmpHP = Math.max((tempHP+diff),0);
const toHeal = tempHP - newTmpHP;
const hash=`${character.id}:${hp}:${hpMax}:${toHeal}`;
// 5. have we accounted for it.
if( !temporalTempHPCache.hasOwnProperty(hash) || (now-temporalTempHPCache[hash].when)>10000) {
// calculate necessary change
temporalTempHPCache[hash]={
when: now,
toHeal: toHeal
};
temp_hp.set('current', newTmpHP);
checkTempHP(temp_hp);
}
hp += temporalTempHPCache[hash].toHeal;
changes[bar] = hp;
}
}
// NEW
const dying_at = DyingAt;
let dead_at=0;
const mythic_rank = parseFloat((findObjs({
type: 'attribute',
characterid: character.id,
name: MythicYN
})[0]||{get:()=>0}).get('current'));
const lvl_mstr = parseFloat((findObjs({
type: 'attribute',
characterid: character.id,
name: MasterLvl
})[0]||{get:()=>0}).get('current'));
if(NegativeDeadAtAttr.length && character){
// Add case for Mythic dead at double constitution and Master level
if( mythic_rank > 0 ){
if( lvl_mstr > 0 ){
dead_at = -((2 * parseFloat((findObjs({
type: 'attribute',
characterid:character.id,
name: NegativeDeadAtAttr
})[0]||{get:()=>0}).get('current'))) + (parseFloat((findObjs({
type: 'attribute',
characterid:character.id,
name: MasterLvl
})[0]||{get:()=>0}).get('current'))));
} else {
dead_at = -(2 * parseFloat((findObjs({
type: 'attribute',
characterid: character.id,
name: NegativeDeadAtAttr
})[0]||{get:()=>0}).get('current')));
}
} else {
dead_at = -parseFloat((findObjs({
type: 'attribute',
characterid:character.id,
name: NegativeDeadAtAttr
})[0]||{get:()=>0}).get('current'));
}
}
// END NEW
let sm = unpackSM(obj.get('statusmarkers'));
if(hp > hpMax) {
hp = hpMax;
changes[bar] = hp;
delete sm[DisableMarker];
delete sm[DyingMarker];
delete sm[DeadMarker];
if(UseBloodied) {
delete sm[BloodiedMarker];
}
// NEW conditions
} else if(hp===dying_at) {
changes[bar] = hp;
sm[DisableMarker] = true;
delete sm[DyingMarker];
delete sm[DeadMarker];
if(UseBloodied) {
delete sm[BloodiedMarker];
}
} else if( (hp < dying_at) && (hp > dead_at) ) {
changes[bar] = hp;
delete sm[DisableMarker];
sm[DyingMarker] = true;
delete sm[DeadMarker];
if(UseBloodied) {
delete sm[BloodiedMarker];
}
} else if(hp <= dead_at) {
// End new conditions, Below line gets uncommented out if removing dying_at and dead_at. Remove dead_at line below also.
// } else if(hp <= 0) {
// hp=0;
hp=dead_at;
changes[bar] = hp;
delete sm[DisableMarker];
delete sm[DyingMarker];
sm[DeadMarker] = true;
if(UseBloodied) {
delete sm[BloodiedMarker];
}
} else if(UseBloodied && (hp <= (hpMax/2) ) ) {
delete sm[DisableMarker];
delete sm[DyingMarker];
delete sm[DeadMarker];
sm[BloodiedMarker] = true;
} else {
delete sm[DisableMarker];
delete sm[DyingMarker];
delete sm[DeadMarker];
if(UseBloodied) {
delete sm[BloodiedMarker];
}
}
changes.statusmarkers = packSM(sm);
obj.set(changes);
}
};
const onAttributeChange = (obj) => {
if(obj.get('name') === TempHPAttributeName){
checkTempHP(obj);
}
};
on("change:attribute", onAttributeChange);
on("change:token", accountForHPBarChange);
if('undefined' !== typeof TokenMod && TokenMod.ObserveTokenChange){
TokenMod.ObserveTokenChange(accountForHPBarChange);
}
if('undefined' !== typeof ChatSetAttr && ChatSetAttr.registerObserver){
ChatSetAttr.registerObserver('change',onAttributeChange);
}
if('undefined' !== typeof ApplyDamage && ApplyDamage.registerObserver){
ApplyDamage.registerObserver('change',onAttributeChange);
}
assureTempHPMarkers();
}); // 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));
});
// 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 = false;
// STOP AT ZERO - Prevents the current value of the bar from dropping below zero.
var STOP_AT_ZERO = false;
var STOP_AT_DEAD = false;
const DeadValue = 'CON';
// 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;
// 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) {
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);
} 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;
if (STOP_AT_DEAD && (CurrentValue - AlterValue < DeadValue)) AlterValue = DeadValue
Target.set("bar" + Bar + "_value", CurrentValue -= AlterValue);
}
if ('undefined' !== typeof HealthColors && HealthColors.Update) HealthColors.Update(Target, Previous);
});
};