ET Battle Helper

A few interface tweaks to improve your battling experience

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         ET Battle Helper
// @version      0.15
// @description  A few interface tweaks to improve your battling experience
// @author       jimborino
// @match        *://*.eternitytower.net/*
// @run-at       document-idle
// @namespace https://greasyfork.org/users/156118
// ==/UserScript==
//


(function() {
    'use strict';

    var storage = window.localStorage;

    var EBHsettings = {
        showEnemyTargets: true,
        showPlayerTargets: true,
        highlightExecuteRange: true,
        showEnemyPercentHealth: true,
        highlightOwnTarget: true,
        loadouts: [
        ]
    };

    if(storage.getItem("ETBattleHelper"))
        EBHsettings = Object.assign({}, EBHsettings, JSON.parse(storage.getItem("ETBattleHelper")));

    $(window).on("beforeunload", function() {
        storage.setItem("ETBattleHelper", JSON.stringify(EBHsettings));
    });

    var ebhStyles = `
        .playerTarget img { border: 1px solid red !important; }

        #target { font-size: 14px; text-transform: capitalize; }

        .battle-unit-container { max-width: 200px; }

        .dropdown-item.ebh-settings {
            cursor: pointer;
        }

        .ebh-settings-modal {
            width: 300px;
            height: 250px;
            position: absolute;
            top: 65px;
            margin-left: auto;
            margin-right: auto;
            left: 0;
            right: 0;
            border: 1px solid black;
            background-color: white;
        }

        .ebh-settings-modal .ebh-close {
            float: right;
            margin-right: 5px;
            margin-top: 5px;
            line-height: 1;
            cursor: pointer;
        }

        .ebh-setting input {
            margin-left: 5px;
        }

        .loadout-container { display: none !important; }
        .loadout-container.active-loadout {
            display: flex !important;
            display: -webkit-flex !important;
            display: -ms-flexbox !important;
        }

        .loadout-container .delete-loadout {
            margin-left: auto;
            cursor: pointer;
            color: red;
            text-decoration: underline;
        }

        .equipment-row .save-loadout {
            float: right;
            cursor: pointer;
            color: green;
            text-decoration: underline;
        }
    `;

    $("<style type='text/css' id='ebh-styles'></style>")
        .text(ebhStyles)
        .appendTo("head");

    setTimeout(initUI, 1000);

    // Prevent typing in chat input box from triggering abilities
    setTimeout(function() { $("input#simple-chat-message").on("keyup", function(e) { e.stopPropagation(); }); }, 2000);

    // Fix hovering for battle log drops
    var obs = new MutationObserver(function(mutations, observer) {
        $(".alert.alert-secondary div.d-flex div.align-items-center img").each(function() {
            $(this).parent().attr("title", $(this).attr("title"));
        });

        mutations.forEach(function(mutation) {
            var addedNodes = mutation.addedNodes;
            if(addedNodes !== null) {
                var nodes = $(addedNodes);
                nodes.each(function() {
                    if($(this).find(".equipment-row").length > 0) {
                        injectLoadoutElements();
                    }
                });
            }
        });
    });
    obs.observe($("body").get(0), { childList: true, subtree: true });


    // Enemy HP % and targets
    Meteor.connection._stream.on("message", function(json) {
        var message = JSON.parse(json);
        if(message.msg == "changed" && message.collection == "redis" &&  message.id.includes("battles-")) {
            var battleState = JSON.parse(message.fields.value);

            battleState.enemies.forEach(function(enemy) {
                var enemyTarget = getUnitById(enemy.target, battleState);
                if (enemyTarget !== undefined) {
                    enemyTarget = enemyTarget.name;
                    var enemyHealth = enemy.stats.health;
                    var enemyHealthMax = enemy.stats.healthMax;
                    var enemyHealthPercent = (enemyHealth / enemyHealthMax) * 100;
                    var enemyImgEl = $("img#" + enemy.id);
                    if(EBHsettings.highlightExecuteRange) {
                        if(enemyHealthPercent < 30) {
                            enemyImgEl.siblings(".progress.health-bar").css("box-shadow", "0 0 0 2px red");
                        } else {
                            enemyImgEl.siblings(".progress.health-bar").css("box-shadow", "none");
                        }
                    }

                    if(EBHsettings.showEnemyPercentHealth) {
                        var enemyPercentEl = enemyImgEl.siblings("div#percent");
                        if(enemyPercentEl.length > 0) {
                            enemyPercentEl.html(enemyHealthPercent.toFixed(2) + "%");
                        } else {
                            enemyPercentEl = $("<div id='percent'>" + enemyHealthPercent.toFixed(2) + "%</div>");
                            enemyPercentEl.insertBefore(enemyImgEl);
                        }
                    }

                    if(EBHsettings.showEnemyTargets) {
                        var enemyTargetEl = enemyImgEl.siblings("div#target");
                        if(enemyTargetEl.length > 0) {
                            enemyTargetEl.html("( " + enemyTarget + " )");
                        } else {
                            enemyTargetEl = $("<div id='target'>( " + enemyTarget + " )</div>");
                            enemyTargetEl.insertBefore(enemyImgEl);
                        }
                    }
                }
            });

            battleState.units.forEach(function(unit) {
                var unitTarget = getUnitById(unit.target, battleState);
                var unitTargetId;
                if(unitTarget === undefined) { // player targets only change when the target is manually changed
                                               // for attacks after an enemy is dead, the target is always the first enemy
                    if(battleState.enemies.length < 1) { // we receive an update when all enemies are dead
                        return;
                    }
                    unitTargetId = battleState.enemies[0].id;
                    unitTarget = battleState.enemies[0].name;
                } else {
                    unitTargetId = unitTarget.id;
                    unitTarget = unitTarget.name;
                }
                var unitImgEl = $("img#" + unit.id);
                if(EBHsettings.showPlayerTargets) {
                    var unitTargetEl = unitImgEl.siblings("div#target");
                    if(unitTargetEl.length > 0) {
                        unitTargetEl.html("( " + unitTarget + " )");
                    } else {
                        unitTargetEl = $("<div id='target'>( " + unitTarget + " )</div>");
                        unitTargetEl.insertBefore(unitImgEl);
                    }
                }



                if(EBHsettings.highlightOwnTarget) {
                    $("img#" + unitTargetId).parent().addClass("playerTarget");
                    $("img#" + unitTargetId).parent()
                                            .parent()
                                            .siblings()
                                            .children()
                                            .removeClass("playerTarget");
                }


            });

        }



        else if(message.msg == "added" && message.collection == "redis" &&  message.id.includes("battles-")) {
            // This fixes abilities triggering after they've recently been switched in
            // Not sure why this works, something to do with recreating the template instance
            var walkTheDOM = function(node, func) {
                func(node);
                node = node.firstChild;
                while (node) {
                    walkTheDOM(node, func);
                    node = node.nextSibling;
                }
            };
            walkTheDOM(document.body, function(node) {
                try{
                    if (Blaze.getView(node).name === "Template.ability"){
                        Blaze.getData(node).templateInstance();
                    }
                } catch(err){
                }
            });
        }

    });

    // Recolor your damage splats
    var oldReceive = Meteor.connection._livedata_data;
    var userId = Meteor.userId();

    Meteor.connection._livedata_data = function() {
        if(arguments[0].msg == "changed" && arguments[0].collection == "redis") {
            var battleState = JSON.parse(arguments[0].fields.value);
            battleState.tickEvents.forEach(function(tickEvent) {
                if(tickEvent.from == userId)
                    tickEvent.customColor = "black";
            });
            arguments[0].fields.value = JSON.stringify(battleState);
        }

        var ret = oldReceive.apply(this, arguments);
        return ret;
    };

    function getUnitById(unitId, battleState) {
        return battleState.units.concat(battleState.enemies).find(function (el) {
            if (el.id == unitId) return true;
        });
    }

    function initUI() {
        // Dropdown menu item
        $("<a></a>")
            .addClass("dropdown-item ebh-settings")
            .text("ET Battle Helper")
            .on("click", function(e) {
                e.preventDefault();
                toggleSettings();
            })
            .appendTo(".navbar div.dropdown-menu");

        // Settings dialog
        var settingsDialog = $("<div></div>")
            .addClass("ebh-settings-modal")
            .css("display", "none")
            .appendTo("body");

        $("<div></div>")
            .text("X")
            .addClass("ebh-close")
            .on("click", function() {
                toggleSettings();
            })
            .appendTo(settingsDialog);

        Object.keys(EBHsettings).forEach(function(key) {
            if(typeof EBHsettings[key] !== "boolean")
                return;
            var settingDiv = $("<div></div>").addClass("ebh-setting");

            $("<label></label>")
                .attr("for", key)
                .text(key)
                .appendTo(settingDiv);

            $("<input type='checkbox'></input>")
                .attr("id", key)
                .prop("checked", EBHsettings[key])
                .on("change", function() {
                    EBHsettings[$(this).attr("id")] = this.checked;
                })
                .appendTo(settingDiv);

            settingDiv.appendTo(settingsDialog);
        });

    }

    function injectLoadoutElements() {
        var loadoutContainer = $("<div></div>")
            .attr("id", "ebh-loadouts-div");

        var loadoutsSelectDiv = $("<div></div>")
            .attr("id", "loadouts-select-div")
            .appendTo(loadoutContainer);

        var loadoutsSelectLabel = $("<div></div>")
            .text("Select Loadout: ")
            .attr("for", "loadouts-select")
            .appendTo(loadoutContainer);

        var loadoutsSelect = $("<select></select>")
            .attr("id", "loadouts-select")
            .on("change", function(e) {
                $(".loadout-container.active-loadout").toggleClass("active-loadout");
                $('.loadout-container[id="' + e.target.value +'"]').toggleClass("active-loadout");
            })
            .appendTo(loadoutContainer);

        var loadoutsDisplay = $("<div></div>")
            .attr("id", "loadouts-display")
            .appendTo(loadoutContainer);

        EBHsettings.loadouts.forEach(function(loadout, index) {
            $("<option></option>")
                .attr("value", loadout.name.replace(" ", "_"))
                .text(loadout.name)
                .appendTo(loadoutsSelect);

            var loadoutContainer = $("<div></div>")
                .addClass("loadout-container d-flex")
                .attr("id", loadout.name.replace(" ", "_"))
                .appendTo(loadoutsDisplay);

            if(index == 0) loadoutContainer.addClass("active-loadout");
            loadout.items.forEach(function(item) {
                var loadoutItem = $("[data-id=" + item + "]")
                    .clone()
                    .addClass("cloned")
                    .on("click", function() {
                        $("[data-id=" + item + "]:not(.cloned)")[0].click();
                    })
                    .appendTo(loadoutContainer);

                // Equipped items have a larger box, make them smaller for consistency
                if(!loadoutItem.hasClass("small")) {
                    loadoutItem.addClass("small");
                }
            });

            var removeLoadout = $("<div></div>")
            .text("Delete Loadout")
            .addClass("delete-loadout")
            .on("click", function() {
                deleteLoadout(loadout.name);
            })
            .appendTo(loadoutContainer);
        });

        $(".equipment-row").append(loadoutContainer);
        $("<div></div>")
            .text("Save Current Loadout")
            .addClass("save-loadout")
            .on("click", function() {
                saveLoadout();
            })
            .prependTo(".equipment-row");

    }

    function saveLoadout() {
        var loadoutName = prompt("Enter a name for your new loadout");
        if(loadoutName === null) return;
        var newLoadout = {name: loadoutName, items: []};
        $(".equipment-row .item-icon-container:not(.cloned)").each(function() {
            newLoadout.items.push($(this).attr("data-id"));
        });

        EBHsettings.loadouts.push(newLoadout);

        $("#ebh-loadouts-div").remove();
        $(".save-loadout").remove();
        injectLoadoutElements();
    }

    function deleteLoadout(loadoutName) {
        EBHsettings.loadouts = EBHsettings.loadouts.filter(function(loadout) {
            return loadout.name !== loadoutName;
        });

       $("#ebh-loadouts-div").remove();
       $(".save-loadout").remove();
       injectLoadoutElements();
    }

    function toggleSettings() {
        $(".ebh-settings-modal").toggle();
    }
})();