// ==UserScript==
// @name ET Battle Helper
// @version 0.14
// @description A few interface tweaks to improve your battling experience
// @author jimborino
// @match http*://*.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();
}
})();