Eternity Tower Heal Buttons

Adds healing buttons to players in battle

13.08.2018 itibariyledir. En son verisyonu görün.

// ==UserScript==
// @name          Eternity Tower Heal Buttons
// @icon          https://www.eternitytower.net/favicon.png
// @namespace     http://mean.cloud/
// @version       0.01
// @description   Adds healing buttons to players in battle
// @match         http*://*.eternitytower.net/*
// @copyright     2018, MeanCloud
// @run-at        document-end
// ==/UserScript==


////////////////////////////////////////////////////////////////
////////////// ** SCRIPT GLOBAL INITIALIZATION ** //////////////
function startup() { ET_PlayerHealButtonsMod(); }
////////////////////////////////////////////////////////////////


ET_PlayerHealButtonsMod = function()
{
    ET.MCMF.Ready(function()
    {
        jQ("head").append
        (
            "<style type=\"text/css\">\r\n" +
            ".MCETMod_PlayerHeal_btndisabled {\r\n" +
            "    width: 96px; height: 30px; font-size: 10pt; font-weight: normal;\r\n" +
            "}\r\n" +
            ".MCETMod_PlayerHeal_btnenabled {\r\n" +
            "    width: 96px; height: 30px; font-size: 10pt; font-weight: bold;\r\n" +
            "}\r\n" +
			".MCETMod_PlayerHeal_btnenabled:hover {\r\n" +
			"    color: #fff;\r\n" +
			"    background-color: #f0ad4e;\r\n" +
			"    border-color: #f0ad4e;\r\n" +
			"}\r\n"
        );

        Meteor.connection._stream.on('message', function(sMeteorRawData)
        {
            try
            {
                var oMeteorData = JSON.parse(sMeteorRawData);

				if ((oMeteorData.id !== undefined) && (oMeteorData.collection === "battlesList") && (oMeteorData.msg === "added"))
                    MCETMod_CurrentBattleListID = oMeteorData.id;

                if (oMeteorData.id.toString().startsWith("battles-"))
                {
                    battleData = JSON.parse(oMeteorData.fields.value);

                    var PlayerSpell_WaterDart = { present: false, data: undefined, html: "" };
                    var PlayerSpell_WaterBall = { present: false, data: undefined, html: "" };
                    var PlayerSpell_Mending   = { present: false, data: undefined, html: "" };
                    
                    var sHTML_AbilityMissing = "<div class=\"item-icon-container item tiny icon-box disabled\"><img class=\"item-icon\" style=\"cursor: not-allowed;\" ";
                    var sHTML_AbilityOnCD = "<div class=\"item-icon-container item tiny icon-box disabled\"><img class=\"item-icon\" style=\"cursor: wait;\" ";
                    var sHTML_AbilityReady = "<div class=\"item-icon-container item tiny icon-box\"><img class=\"item-icon\" style=\"cursor: pointer;\" ";
                    
                    var oThePlayer = undefined;
                    
                    jQ.makeArray(battleData.units).forEach(function(oCurrentPlayer, index, array)
                    {
						if (oCurrentPlayer.name === ET.MCMF.UserName)
                        {
                            oThePlayer = oCurrentPlayer;
                            
                            jQ.makeArray(oThePlayer.abilities).forEach(function(oCurrentAbility, index, array)
                            {
                                if (oCurrentAbility.id === "water_dart")
                                {
                                    PlayerSpell_WaterDart.present = true;
                                    PlayerSpell_WaterDart.data = oCurrentAbility;
                                }
                                else if (oCurrentAbility.id === "water_ball")
                                {
                                    PlayerSpell_WaterBall.present = true;
                                    PlayerSpell_WaterBall.data = oCurrentAbility;
                                }
                                else if (oCurrentAbility.id === "mending_water")
                                {
                                    PlayerSpell_Mending.present = true;
                                    PlayerSpell_Mending.data = oCurrentAbility;
                                }
                            });
                        }
                    });
                    
                    if (PlayerSpell_WaterDart.present)
                    {
                        if (CDbl(PlayerSpell_WaterDart.data.currentCooldown) > 0.1)
                            PlayerSpell_WaterDart.html = sHTML_AbilityOnCD;
                        else
                            PlayerSpell_WaterDart.html = sHTML_AbilityReady;
                    }
                    else
                    {
                        PlayerSpell_WaterDart.html = sHTML_AbilityMissing;
                        PlayerSpell_WaterDart.data = { id: '' };
                    }
                    
                    if (PlayerSpell_WaterBall.present)
                    {
                        if (CDbl(PlayerSpell_WaterBall.data.currentCooldown) > 0.1)
                            PlayerSpell_WaterBall.html = sHTML_AbilityOnCD;
                        else
                            PlayerSpell_WaterBall.html = sHTML_AbilityReady;
                    }
                    else
                    {
                        PlayerSpell_WaterBall.html = sHTML_AbilityMissing;
                        PlayerSpell_WaterBall.data = { id: '' };
                    }
                    
                    if (PlayerSpell_Mending.present)
                    {
                        if (CDbl(PlayerSpell_Mending.data.currentCooldown) > 0.1)
                            PlayerSpell_Mending.html = sHTML_AbilityOnCD;
                        else
                            PlayerSpell_Mending.html = sHTML_AbilityReady;
                    }
                    else
                    {
                        PlayerSpell_Mending.html = sHTML_AbilityMissing;
                        PlayerSpell_Mending.data = { id: '' };
                    }
                    
                    if (PlayerSpell_WaterDart.present || PlayerSpell_WaterBall.present || PlayerSpell_Mending.present)
                    {
                        jQ.makeArray(battleData.units).forEach(function(oCurrentPlayer, index, array)
                        {
                            jQ("div.battle-unit-container").each(function()
                            {
                                try
                                {
                                    if (jQ(this).find("img#" + oCurrentPlayer.id.toString()).length === 0)
                                        return;

                                    var iHeal_WaterDart = ET_PlayerHealButtonsMod_WaterDartHealing(oThePlayer, oCurrentPlayer);
                                    var iHeal_WaterBall = ET_PlayerHealButtonsMod_WaterBallHealing(oThePlayer, oCurrentPlayer);
                                    var iHeal_Mending   = ET_PlayerHealButtonsMod_MendingHealing  (oThePlayer, oCurrentPlayer);
                                    
                                    jQ(this).parent().find(".MCETMod_PlayerHeal").remove();

                                    jQ(this).parent().find("img#" + oCurrentPlayer.id.toString()).after(
                                        "<div class=\"MCETMod_PlayerHeal\">" +
                                        PlayerSpell_WaterDart.html + "src=\"/icons/waterDart.svg\"    onclick=\"javascript:ET_PlayerHealButtonsMod_CastAtTarget('" + PlayerSpell_WaterDart.data.id + "', '" + oCurrentPlayer.id.toString() + "');\"></div>" +
                                        PlayerSpell_WaterBall.html + "src=\"/icons/waterBall.svg\"    onclick=\"javascript:ET_PlayerHealButtonsMod_CastAtTarget('" + PlayerSpell_WaterBall.data.id + "', '" + oCurrentPlayer.id.toString() + "');\"></div>" +
                                        PlayerSpell_Mending.html   + "src=\"/icons/mendingWater.svg\" onclick=\"javascript:ET_PlayerHealButtonsMod_CastAtTarget('" + PlayerSpell_Mending.data.id   + "', '" + oCurrentPlayer.id.toString() + "');\"></div>" +
                                        "<br />" + 
                                        //"<div class=\"buff-icon-container icon-box small-icon drop-target" + ((PlayerSpell_WaterDart.present) ? ((PlayerSpell_WaterDart.data.currentCooldown > 0.0) ? "" : " disabled") : " disabled") + "\"><span class=\"cooldown-text\" style=\"font-size: 16px; font-weight: bold;\">" + ((PlayerSpell_WaterDart.present) ? ((PlayerSpell_WaterDart.data.currentCooldown > 0.0) ? Math.ceil(PlayerSpell_WaterDart.data.currentCooldown).toFixed(0) : "&nbsp;") : "&nbsp;") + "</span></div>" + 
                                        //"<div class=\"buff-icon-container icon-box small-icon drop-target" + ((PlayerSpell_WaterBall.present) ? ((PlayerSpell_WaterBall.data.currentCooldown > 0.0) ? "" : " disabled") : " disabled") + "\"><span class=\"cooldown-text\" style=\"font-size: 16px; font-weight: bold;\">" + ((PlayerSpell_WaterBall.present) ? ((PlayerSpell_WaterBall.data.currentCooldown > 0.0) ? Math.ceil(PlayerSpell_WaterBall.data.currentCooldown).toFixed(0) : "&nbsp;") : "&nbsp;") + "</span></div>" + 
                                        //"<div class=\"buff-icon-container icon-box small-icon drop-target" + ((PlayerSpell_Mending  .present) ? ((PlayerSpell_Mending  .data.currentCooldown > 0.0) ? "" : " disabled") : " disabled") + "\"><span class=\"cooldown-text\" style=\"font-size: 16px; font-weight: bold;\">" + ((PlayerSpell_Mending  .present) ? ((PlayerSpell_Mending  .data.currentCooldown > 0.0) ? Math.ceil(PlayerSpell_Mending  .data.currentCooldown).toFixed(0) : "&nbsp;") : "&nbsp;") + "</span></div>" + 
                                        "<div class=\"buff-icon-container icon-box small-icon drop-target" + ((PlayerSpell_WaterDart.present) ? ((PlayerSpell_WaterDart.data.currentCooldown > 0.0) ? "" : " disabled") : " disabled") + "\"><span class=\"cooldown-text\" style=\"font-size: 16px; font-weight: bold;\">" + ((PlayerSpell_WaterDart.present) ? iHeal_WaterDart.actualHealed.toFixed(0) : "&nbsp;") + "</span></div>" + 
                                        "<div class=\"buff-icon-container icon-box small-icon drop-target" + ((PlayerSpell_WaterBall.present) ? ((PlayerSpell_WaterBall.data.currentCooldown > 0.0) ? "" : " disabled") : " disabled") + "\"><span class=\"cooldown-text\" style=\"font-size: 16px; font-weight: bold;\">" + ((PlayerSpell_WaterBall.present) ? iHeal_WaterBall.actualHealed.toFixed(0) : "&nbsp;") + "</span></div>" + 
                                        "<div class=\"buff-icon-container icon-box small-icon drop-target" + ((PlayerSpell_Mending  .present) ? ((PlayerSpell_Mending  .data.currentCooldown > 0.0) ? "" : " disabled") : " disabled") + "\"><span class=\"cooldown-text\" style=\"font-size: 16px; font-weight: bold;\">" + ((PlayerSpell_Mending  .present) ? iHeal_Mending  .actualHealed.toFixed(0) : "&nbsp;") + "</span></div>" + 
                                        "</div>");
                                }
                                catch (err) { }
                            });
                        });
                    }
                    else
                        jQ(".MCETMod_PlayerHeal").remove(); // not able to heal, remove all heal buttons
                }
            }
            catch (err) { }
        });
    });
};

ET_PlayerHealButtonsMod_WaterDartHealing = function(oHeroDetails, oTargetDetails)
{
    try
    {
        var dHealAmount = 3.0 + oHeroDetails.stats.magicPower;
        dHealAmount *= (1.0 + (oHeroDetails.stats.healingPower / 100.0));
        var dUncappedHealAmount = dHealAmount;

        var dHPCost = 5.0 + (oHeroDetails.stats.magicPower * 0.15);

        var dHPCeiling = oTargetDetails.stats.healthMax;
        
        if (oHeroDetails.id === oTargetDetails.id)
            dHPCeiling -= dHPCost;

        if (dHealAmount + oTargetDetails.stats.health > dHPCeiling)
            dHealAmount = dHPCeiling - oTargetDetails.stats.health;
        
        return { actualHealed: Math.floor(dHealAmount), uncappedHeal: Math.floor(dUncappedHealAmount) };
    }
    catch (err) { }
    
    return { actualHealed: -1, uncappedHeal: -1 };
};

ET_PlayerHealButtonsMod_WaterBallHealing = function(oHeroDetails, oTargetDetails)
{
    try
    {
        var dHealAmount = 10.0 + (oHeroDetails.stats.magicPower * 1.25);
        dHealAmount *= (1.0 + (oHeroDetails.stats.healingPower / 100.0));
        var dUncappedHealAmount = dHealAmount;

        var dHPCost = 10.0 + (oHeroDetails.stats.magicPower * 0.13);

        var dHPCeiling = oTargetDetails.stats.healthMax;
        
        if (oHeroDetails.id === oTargetDetails.id)
            dHPCeiling -= dHPCost;

        if (dHealAmount + oTargetDetails.stats.health > dHPCeiling)
            dHealAmount = dHPCeiling - oTargetDetails.stats.health;
        
        return { actualHealed: Math.floor(dHealAmount), uncappedHeal: Math.floor(dUncappedHealAmount) };
    }
    catch (err) { }
    
    return { actualHealed: -1, uncappedHeal: -1 };
};

ET_PlayerHealButtonsMod_MendingHealing = function(oHeroDetails, oTargetDetails)
{
    try
    {
        var dHealAmount = 2.0 + (oHeroDetails.stats.magicPower * 0.50);
        dHealAmount *= (1.0 + (oHeroDetails.stats.healingPower / 100.0));
        dHealAmount *= 5; // five ticks per cast
        var dUncappedHealAmount = dHealAmount;

        var dHPCost = 25.0 + (oHeroDetails.stats.magicPower * 0.30);

        var dHPCeiling = oTargetDetails.stats.healthMax;
        
        if (oHeroDetails.id === oTargetDetails.id)
            dHPCeiling -= dHPCost;

        if (dHealAmount + oTargetDetails.stats.health > dHPCeiling)
            dHealAmount = dHPCeiling - oTargetDetails.stats.health;
        
        return { actualHealed: Math.floor(dHealAmount), uncappedHeal: Math.floor(dUncappedHealAmount) };
    }
    catch (err) { }
    
    return { actualHealed: -1, uncappedHeal: -1 };
};

ET_PlayerHealButtonsMod_CastAtTarget = function(spell_name, which_id)
{
    Meteor.connection._send({"msg":"method","method":"battles.castAbility","params":[MCETMod_CurrentBattleListID,spell_name,{"targets":[which_id],"caster":ET.MCMF.UserID}],"id":"MCETModHealButtons"});
};

ET_PlayerHealButtonsMod_HealPlayer_WithMending = function(which_id)
{
    ET_PlayerHealButtonsMod_CastAtTarget("mending_water", which_id);
};

ET_PlayerHealButtonsMod_TargetCreature = function(which_id)
{
    ET_PlayerHealButtonsMod_CastAtTarget("changeTarget", which_id);
};


////////////////////////////////////////////////////////////////
/////////////// ** common.js -- DO NOT MODIFY ** ///////////////
time_val = function()
{
    return CDbl(Math.floor(Date.now() / 1000));
};

IsValid = function(oObject)
{
    if (oObject === undefined) return false;
    if (oObject === null) return false;
    return true;
};

Random = function(iMin, iMax)
{
    return parseInt(iMin + Math.floor(Math.random() * iMax));
};

ShiftClick = function(oEl)
{
    if (oEl === undefined)
    {
        var shiftclick = jQ.Event("click");
        shiftclick.shiftKey = true;

        var shiftclickOrig = jQ.Event("click");
        shiftclickOrig.shiftKey = true;

        shiftclick.originalEvent = shiftclick;
        return shiftclick;
    }

    jQ(oEl).trigger(ShiftClick());
};

if (!String.prototype.replaceAll)
    String.prototype.replaceAll = function(search, replace) { return ((replace === undefined) ? this.toString() : this.replace(new RegExp('[' + search + ']', 'g'), replace)); };

if (!String.prototype.startsWith)
    String.prototype.startsWith = function(search, pos) { return this.substr(((!pos) || (pos < 0)) ? 0 : +pos, search.length) === search; };

CInt = function(v)
{
	try
	{
		if (!isNaN(v)) return Math.floor(v);
		if (typeof v === 'undefined') return parseInt(0);
		if (v === null) return parseInt(0);
		var t = parseInt(v);
		if (isNaN(t)) return parseInt(0);
		return Math.floor(t);
	}
	catch (err) { }

	return parseInt(0);
};

CDbl = function(v)
{
	try
	{
		if (!isNaN(v)) return parseFloat(v);
		if (typeof v === 'undefined') return parseFloat(0.0);
		if (v === null) return parseFloat(0.0);
		var t = parseFloat(v);
		if (isNaN(t)) return parseFloat(0.0);
		return t;
	}
	catch (err) { }

	return parseFloat(0.0);
};

// dup of String.prototype.startsWith, but uses indexOf() instead of substr()
startsWith = function (haystack, needle) { return (needle === "") || (haystack.indexOf(needle) === 0); };
endsWith   = function (haystack, needle) { return (needle === "") || (haystack.substring(haystack.length - needle.length) === needle); };

ChopperBlank = function (sText, sSearch, sEnd)
{
	var sIntermediate = "";

	if (sSearch === "")
		sIntermediate = sText.substring(0, sText.length);
	else
	{
		var iIndexStart = sText.indexOf(sSearch);
		if (iIndexStart === -1)
			return "";

		sIntermediate = sText.substring(iIndexStart + sSearch.length);
	}

	if (sEnd === "")
		return sIntermediate;

	var iIndexEnd = sIntermediate.indexOf(sEnd);

	return (iIndexEnd === -1) ? sIntermediate : sIntermediate.substring(0, iIndexEnd);
};

CondenseSpacing = function(text)
{
	while (text.indexOf("  ") !== -1)
		text = text.replace("  ", " ");
	return text;
};

pad = function(sText, iWidth, sChar)
{
    sChar = ((sChar !== undefined) ? sChar : ('0'));
    sText = sText.toString();
    return ((sText.length >= iWidth) ? (sText) : (new Array(iWidth - sText.length + 1).join(sChar) + sText));
};

is_visible = (function () {
    var x = window.pageXOffset ? window.pageXOffset + window.innerWidth - 1 : 0,
        y = window.pageYOffset ? window.pageYOffset + window.innerHeight - 1 : 0,
        relative = !!((!x && !y) || !elementFromPoint(x, y));
        function inside(child, parent) {
            while(child){
                if (child === parent) return true;
                child = child.parentNode;
            }
        return false;
    };
    return function (elem) {
        if (
            hidden ||
            elem.offsetWidth==0 ||
            elem.offsetHeight==0 ||
            elem.style.visibility=='hidden' ||
            elem.style.display=='none' ||
            elem.style.opacity===0
        ) return false;
        var rect = elem.getBoundingClientRect();
        if (relative) {
            if (!inside(elementFromPoint(rect.left + elem.offsetWidth/2, rect.top + elem.offsetHeight/2),elem)) return false;
        } else if (
            !inside(elementFromPoint(rect.left + elem.offsetWidth/2 + window.pageXOffset, rect.top + elem.offsetHeight/2 + window.pageYOffset), elem) ||
            (
                rect.top + elem.offsetHeight/2 < 0 ||
                rect.left + elem.offsetWidth/2 < 0 ||
                rect.bottom - elem.offsetHeight/2 > (window.innerHeight || documentElement.clientHeight) ||
                rect.right - elem.offsetWidth/2 > (window.innerWidth || documentElement.clientWidth)
            )
        ) return false;
        if (window.getComputedStyle || elem.currentStyle) {
            var el = elem,
                comp = null;
            while (el) {
                if (el === document) {break;} else if(!el.parentNode) return false;
                comp = window.getComputedStyle ? window.getComputedStyle(el, null) : el.currentStyle;
                if (comp && (comp.visibility=='hidden' || comp.display == 'none' || (typeof comp.opacity !=='undefined' && comp.opacity != 1))) return false;
                el = el.parentNode;
            }
        }
        return true;
    };
})();
////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////
////////////// ** common_ET.js -- DO NOT MODIFY ** /////////////

if (window.ET === undefined) window.ET = { };
if (window.ET.MCMF === undefined) // MeanCloud mod framework
{
    window.ET.MCMF =
    {
        TryingToLoad: false,
        WantDebug: false,
        WantFasterAbilityCDs: false,

        InBattle: false,
        FinishedLoading: false,
        Initialized: false,
        AbilitiesReady: false,
        InitialAbilityCheck: true,
        TimeLeftOnCD: 9999,
        TimeLastFight: 0,

        CombatID: undefined, // technically not required

        ToastMessageSuccess: function(msg)
        {
            toastr.success(msg);
        },

        ToastMessageWarning: function(msg)
        {
            toastr.warning(msg);
        },

        EventSubscribe: function(sEventName, fnCallback, sNote)
        {
            if (window.ET.MCMF.EventSubscribe_events === undefined)
                window.ET.MCMF.EventSubscribe_events = [];

            var newEvtData = {};
                newEvtData.name = ((!sEventName.startsWith("ET:")) ? ("ET:" + sEventName) : (sEventName));
                newEvtData.callback = fnCallback;
                newEvtData.note = sNote;

            window.ET.MCMF.EventSubscribe_events.push(newEvtData);

            /*
            jQ("div#ET_meancloud_bootstrap").off("ET:" + sEventName.trim()).on("ET:" + sEventName.trim(), function()
            {
                window.ET.MCMF.EventSubscribe_events.forEach(function(oThisEvent, index, array)
                {
                    if (sEventName === oThisEvent.name)
                    {
                        if (window.ET.MCMF.WantDebug) console.log("FIRING '" + oThisEvent.name + "'!" + ((oThisEvent.note === undefined) ? "" : " (" + oThisEvent.note + ")"));
                        oThisEvent.callback();
                    }
                });
            });
            */

            if (window.ET.MCMF.WantDebug) console.log("Added event subscription '" + sEventName + "'!" + ((sNote === undefined) ? "" : " (" + sNote + ")"));
        },

        EventTrigger: function(sEventName)
        {
            //jQ("div#ET_meancloud_bootstrap").trigger(sEventName);

            if (window.ET.MCMF.EventSubscribe_events === undefined) return;

            window.ET.MCMF.EventSubscribe_events.forEach(function(oThisEvent, index, array)
            {
                if (sEventName === oThisEvent.name)
                {
                    if (window.ET.MCMF.WantDebug) console.log("FIRING '" + oThisEvent.name + "'!" + ((oThisEvent.note === undefined) ? "" : " (" + oThisEvent.note + ")"));
                    try { oThisEvent.callback(); } catch (err) { if (window.ET.MCMF.WantDebug) console.log("Exception: " + err); }
                }
            });
        },

        MeteorCall: function(sMethod, oParam1, oParam2, sMsgSuccess, sMsgFailure)
        {
            Package.meteor.Meteor.call("crafting.craftItem", sRecipeID, iBatchAmt, function(errResp)
            {
                if (errResp)
                    window.ET.MCMF.ToastMessageWarning(sMsgFailure);
                else
                    window.ET.MCMF.ToastMessageSuccess(sMsgSuccess);
            });
        },

        FasterAbilityUpdates: function()
        {
            try
            {
                if ((window.ET.MCMF.WantFasterAbilityCDs) && (window.ET.MCMF.FinishedLoading) && (!window.ET.MCMF.InBattle) && (!window.ET.MCMF.AbilitiesReady))
                    Meteor.call("abilities.gameUpdate", function(e, t) { });
            }
            catch (err) { }

            setTimeout(window.ET.MCMF.FasterAbilityUpdates, 2000);
        },

        AbilityCDTrigger: function()
        {
            try
            {
                bStillInCombat = window.ET.MCMF.InBattle || ((time_val() - window.ET.MCMF.TimeLastFight) < 3);

                if ((window.ET.MCMF.FinishedLoading) && (!bStillInCombat))
                {
                    iTotalCD = 0;
                    iTotalCDTest = 0;
                    iHighestCD = 0;

                    window.ET.MCMF.GetAbilities().forEach(function(oThisAbility, index, array)
                    {
                        if (oThisAbility.equipped)
                        {
                            if (parseInt(oThisAbility.currentCooldown) > 0)
                            {
                                iTotalCD += parseInt(oThisAbility.currentCooldown);
                                if (iHighestCD < parseInt(oThisAbility.currentCooldown))
                                    iHighestCD = parseInt(oThisAbility.currentCooldown);
                            }
                        }

                        iTotalCDTest += parseInt(oThisAbility.cooldown);
                    });

                    if ((iTotalCDTest > 0) && (iTotalCD === 0))
                    {
                        if (!window.ET.MCMF.AbilitiesReady)
                        {
                            if (!window.ET.MCMF.InitialAbilityCheck)
                            {
                                if (window.ET.MCMF.WantDebug) console.log("<-- triggering ET:abilitiesReady -->");
                                window.ET.MCMF.EventTrigger("ET:abilitiesReady");
                                //jQ("div#ET_meancloud_bootstrap").trigger("ET:abilitiesReady");
                            }
                        }

                        window.ET.MCMF.AbilitiesReady = true;
                        window.ET.MCMF.TimeLeftOnCD = 0;
                    }
                    else
                    {
                        window.ET.MCMF.AbilitiesReady = false;
                        window.ET.MCMF.TimeLeftOnCD = iHighestCD;
                    }

                    window.ET.MCMF.InitialAbilityCheck = false;
                }
                else
                {
                    window.ET.MCMF.AbilitiesReady = false;
                    window.ET.MCMF.TimeLeftOnCD = 9999;
                }
            }
            catch (err) { }

            setTimeout(window.ET.MCMF.AbilityCDTrigger, 500);
        },

        InitMeteorTriggers: function()
        {
            if ((Package.meteor.Meteor === undefined) || (Package.meteor.Meteor.connection === undefined) || (Package.meteor.Meteor.connection._stream === undefined))
            {
                setTimeout(window.ET.MCMF.InitMeteorTriggers, 100);
                return;
            }

            Package.meteor.Meteor.connection._stream.on('message', function(sMeteorRawData)
            {
                if (window.ET.MCMF.CombatID === undefined)
                {
                    try
                    {
                        oDataTemp = Package.meteor.global.Accounts.connection._stores.combat._getCollection()._collection._docs._map;
                        window.ET.MCMF.CombatID = oDataTemp[Object.keys(oDataTemp)[0]]._id;
                    }
                    catch (err) { }
                }

                try
                {
                    oMeteorData = JSON.parse(sMeteorRawData);

                    /////////////////////////////////////////////////////////////////////////////////////////////////////////
                    //
                    //  BACKUP TO RETRIEVE USER AND COMBAT IDS
                    //
                    if (oMeteorData.collection === "users")
                        if ((window.ET.MCMF.UserID === undefined) || (window.ET.MCMF.UserID.length !== 17))
                            window.ET.MCMF.UserID = oMeteorData.id;

                    if (oMeteorData.collection === "combat")
                        if ((window.ET.MCMF.T_CombatID === undefined) || (window.ET.MCMF.CombatID.length !== 17))
                            if (oMeteorData.fields.owner === window.ET.MCMF.UserID)
                                window.ET.MCMF.CombatID = oMeteorData.id;
                    //
                    /////////////////////////////////////////////////////////////////////////////////////////////////////////

                    if (window.ET.MCMF.FinishedLoading)
                    {
                        if (oMeteorData.collection === "battlesList")
                        {
                            window.ET.MCMF.IsDemon = false;
                            window.ET.MCMF.AbilitiesReady = false;

                            if ((oMeteorData.msg === "added") || (oMeteorData.msg === "removed"))
                            {
                                window.ET.MCMF.InBattle = (oMeteorData.msg === "added");
                                if (window.ET.MCMF.WantDebug) console.log("<-- triggering ET:combat" + (((oMeteorData.msg === "added")) ? ("Start") : ("End")) + " -->");
                                window.ET.MCMF.EventTrigger("ET:combat" + (((oMeteorData.msg === "added")) ? ("Start") : ("End")));
                                //jQ("div#ET_meancloud_bootstrap").trigger("ET:combat" + (((oMeteorData.msg === "added")) ? ("Start") : ("End")));
                            }
                        }

                        if ((oMeteorData.collection === "battles") && (oMeteorData.msg === "added"))
                        {
                            if (oMeteorData.fields.finished)
                            {
                                window.ET.MCMF.WonLast = oMeteorData.fields.win;
                                window.ET.MCMF.TimeLastFight = time_val();

                                if (!oMeteorData.fields.win)
                                    window.ET.MCMF.HP = 0;

                                if (window.ET.MCMF.WantDebug) console.log("<-- triggering ET:combat" + ((oMeteorData.fields.win) ? ("Won") : ("Lost")) + " -->");
                                window.ET.MCMF.EventTrigger("ET:combat" + ((oMeteorData.fields.win) ? ("Won") : ("Lost")));
                                //jQ("div#ET_meancloud_bootstrap").trigger("ET:combat" + ((oMeteorData.fields.win) ? ("Won") : ("Lost")));
                            }
                        }
                    }

                    try
                    {
                        if (window.ET.MCMF.FinishedLoading)
                        {
                            if (oMeteorData.id)
                            {
                                if (oMeteorData.id.startsWith("battles-"))
                                {
                                    if (oMeteorData.msg !== "removed")
                                    {
                                        battleData = JSON.parse(oMeteorData.fields.value);

                                        jQ.makeArray(battleData.units).forEach(function(currentPlayer, index, array)
                                        {
                                            try
                                            {
                                                if (currentPlayer.name === window.ET.MCMF.UserName)
                                                {
                                                    jQ.makeArray(currentPlayer.buffs).forEach(function(currentBuff, index2, array2)
                                                     {
                                                        try
                                                        {
                                                            if (currentBuff.id === "demons_heart")
                                                            {
                                                                if (currentBuff.data.active)
                                                                {
                                                                    if (!window.ET.MCMF.IsDemon)
                                                                    {
                                                                        window.ET.MCMF.IsDemon = true;

                                                                        if (window.ET.MCMF.WantDebug) console.log("<-- triggering ET:combat:buffDemon -->");
                                                                        window.ET.MCMF.EventTrigger("ET:combat:buffDemon");
                                                                        //jQ("div#ET_meancloud_bootstrap").trigger("ET:combat:buffDemon");
                                                                    }
                                                                }
                                                            }
                                                        }
                                                        catch (err) { }
                                                    });

                                                    return true; // break out of forEach()
                                                }
                                            }
                                            catch (err) { }
                                        });
                                    }
                                }
                            }
                        }
                    }
                    catch (err) { }
                }
                catch (err) { }

                try
                {
                    //todo: use life data from Meteor vs captured meteor response data
                    oMeteorData = JSON.parse(sMeteorRawData);

                    if (oMeteorData.collection === "combat")
                    {
                        if ((oMeteorData.fields.owner === window.ET.MCMF.UserID) || (oMeteorData.id === window.ET.MCMF.CombatID))
                        {
                            window.ET.MCMF.HP  = oMeteorData.fields.stats.health;
                            window.ET.MCMF.NRG = oMeteorData.fields.stats.energy;
                        }
                    }
                }
                catch (err) { }
            });
        },

        AbilityCDCalc: function()
        {
            iTotalCD = 0;
            iTotalCDTest = 0;
            iHighestCD = 0;

            window.ET.MCMF.GetAbilities().forEach(function(oThisAbility, index, array)
            {
                if (oThisAbility.equipped)
                {
                    if (parseInt(oThisAbility.currentCooldown) > 0)
                    {
                        iTotalCD += parseInt(oThisAbility.currentCooldown);
                        if (iHighestCD < parseInt(oThisAbility.currentCooldown))
                            iHighestCD = parseInt(oThisAbility.currentCooldown);
                    }
                }

                iTotalCDTest += parseInt(oThisAbility.cooldown);
            });

            if ((iTotalCDTest > 0) && (iTotalCD === 0))
            {
                if (!window.ET.MCMF.AbilitiesReady)
                {
                    if (!window.ET.MCMF.InitialAbilityCheck)
                    {
                        if (window.ET.MCMF.WantDebug) console.log("<-- triggering ET:abilitiesReady -->");
                        window.ET.MCMF.EventTrigger("ET:abilitiesReady");
                        //jQ("div#ET_meancloud_bootstrap").trigger("ET:abilitiesReady");
                    }
                }

                window.ET.MCMF.AbilitiesReady = true;
                window.ET.MCMF.TimeLeftOnCD = 0;
            }
            else
            {
                window.ET.MCMF.AbilitiesReady = false;
                window.ET.MCMF.TimeLeftOnCD = iHighestCD;
            }

            window.ET.MCMF.InitialAbilityCheck = false;
        },

        GetAbilities: function()
        {
            return Object.keys(window.ET.MCMF.AbilityManager._collection._docs._map).map(key => window.ET.MCMF.AbilityManager._collection._docs._map[key])[0].learntAbilities;
        },

        GetAdventures: function()
        {
            return Object.keys(window.ET.MCMF.AdventureManager._collection._docs._map).map(key => window.ET.MCMF.AdventureManager._collection._docs._map[key])[0].adventures;
        },

        GetChats: function()
        {
            return Object.keys(window.ET.MCMF.ChatManager._collection._docs._map).map(key => window.ET.MCMF.ChatManager._collection._docs._map[key]);
        },

        GetItems: function()
        {
            return Object.keys(window.ET.MCMF.ItemManager._collection._docs._map).map(key => window.ET.MCMF.ItemManager._collection._docs._map[key]);
        },

        // need a better way to check if the game has loaded basic data, but this is fine for now
        Setup: function()
        {
            if ((!window.ET.MCMF.TryingToLoad) && (!window.ET.MCMF.FinishedLoading))
            {
                // use whatever version of jQuery available to us
                $("body").append("<div id=\"ET_meancloud_bootstrap\" style=\"visibility: hidden; display: none;\"></div>");
                window.ET.MCMF.TryingToLoad = true;
                window.ET.MCMF.Setup_Initializer();
            }
        },

        Setup_Initializer: function()
        {
            // wait for Meteor availability
            if ((Package === undefined) || (Package.meteor === undefined) || (Package.meteor.Meteor === undefined) || (Package.meteor.Meteor.connection === undefined) || (Package.meteor.Meteor.connection._stream === undefined))
            {
                setTimeout(window.ET.MCMF.Setup_Initializer, 10);
                return;
            }

            if (!window.ET.MCMF.Initialized)
            {
                window.ET.MCMF.Initialized = true;
                window.ET.MCMF.Setup_SendDelayedInitializer();
                window.ET.MCMF.InitMeteorTriggers();
                window.ET.MCMF.Setup_remaining();
            }
        },

        Setup_SendDelayedInitializer: function()
        {
            try
            {
                jQ("div#ET_meancloud_bootstrap").trigger("ET:initialized");
                window.ET.MCMF.EventTrigger("ET:initialized");
                //if (window.ET.MCMF.WantDebug) console.log("<-- triggering ET:initialized -->");
            }
            catch (err)
            {
                setTimeout(window.ET.MCMF.Setup_SendDelayedInitializer, 100);
            }
        },

        Setup_remaining: function()
        {
            try
            {
                window.ET.MCMF.UserID = Package.meteor.global.Accounts.connection._userId;
                window.ET.MCMF.UserName = Package.meteor.global.Accounts.connection._stores.users._getCollection()._collection._docs._map[Package.meteor.global.Accounts.connection._userId].username;
                try
                {
                    oDataTemp = Package.meteor.global.Accounts.connection._stores.combat._getCollection()._collection._docs._map;
                    window.ET.MCMF.CombatID = oDataTemp[Object.keys(oDataTemp)[0]]._id;
                }
                catch (err) { }

                window.ET.MCMF.AbilityManager = Package.meteor.global.Accounts.connection._stores.abilities._getCollection();
                window.ET.MCMF.AdventureManager = Package.meteor.global.Accounts.connection._stores.adventures._getCollection();
                window.ET.MCMF.ChatManager = Package.meteor.global.Accounts.connection._stores.simpleChats._getCollection();
                window.ET.MCMF.ItemManager = Package.meteor.global.Accounts.connection._stores.items._getCollection();

                if (window.ET.MCMF.GetAbilities().length < 0) throw "Not loaded yet: no abilities";
                if (window.ET.MCMF.GetItems().length < 0) throw "Not loaded yet: no items";
                if (window.ET.MCMF.GetChats().length < 0) throw "Not loaded yet: no chats";

                // if the above is all good, then this should be no problem:

                window.ET.MCMF.AbilityCDTrigger();     // set up ability CD trigger
                window.ET.MCMF.AbilityCDCalc();
                window.ET.MCMF.FasterAbilityUpdates(); // set up faster ability updates (do not disable, this is controlled via configurable setting)

                // trigger finished-loading event
                if (!window.ET.MCMF.FinishedLoading)
                {
                    if (window.ET.MCMF.WantDebug) console.log("<-- triggering ET:loaded -->");
                    window.ET.MCMF.EventTrigger("ET:loaded");
                    //jQ("div#ET_meancloud_bootstrap").trigger("ET:loaded");
                    window.ET.MCMF.FinishedLoading = true;
                }
            }
            catch (err)
            {
                // any errors and we retry setup
                setTimeout(window.ET.MCMF.Setup_remaining, 500);
            }
        },

        Loaded: function(fnCallback, sNote)
        {
            if (!window.ET.MCMF.FinishedLoading)
                window.ET.MCMF.EventSubscribe("loaded", fnCallback, sNote);
            else
                fnCallback();
        },

        Ready: function(fnCallback, sNote)
        {
            if (!window.ET.MCMF.Initialized)
                window.ET.MCMF.EventSubscribe("initialized", fnCallback, sNote);
            else
                fnCallback();
        }
    };

    window.ET.MCMF.Setup();
}
////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////
////////// ** CORE SCRIPT STARTUP -- DO NOT MODIFY ** //////////
function LoadJQ(callback) {
    if (window.jQ === undefined) { var script=document.createElement("script");script.setAttribute("src","//ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js");script.addEventListener('load',function() {
        var subscript=document.createElement("script");subscript.textContent="window.jQ=jQuery.noConflict(true);("+callback.toString()+")();";document.body.appendChild(subscript); },
    !1);document.body.appendChild(script); } else callback(); } LoadJQ(startup);
////////////////////////////////////////////////////////////////