ET Battle CD Tracker

Displays each player's cooldowns during battle. Settings available in console via window.et_battleCd

  1. // ==UserScript==
  2. // @name ET Battle CD Tracker
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.9
  5. // @description Displays each player's cooldowns during battle. Settings available in console via window.et_battleCd
  6. // @author Aes Sedai
  7. // @match http*://*.eternitytower.net/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13. $("<style type='text/css'> .cd-container > div { display: flex; align-items: center; } .cd-container > div > p { padding-left: 8px; margin-bottom: 0;} .room-number-container > p {font-size: 16px; line-height: 16px; margin-bottom: 0; text-align: center;} </style>").appendTo("head");
  14.  
  15. // Default settings, use window.et_battleCd in console to change settings
  16. // hideOwnCds: BOOLEAN, default: true; if true, your own cooldowns are always hidden
  17. // hideReadyCds: BOOLEAN, default: true; if true, only skills that are on cooldown will be shown and ready-to-use skills will be hidden
  18. // ignoreAbilityCds: ARRAY, default: []; contains a comma-delimited list of strings of ability names to ignore
  19. // showRoomNumber: BOOLEAN, default: true; if true, shows the floor number in the buff bar at the top
  20. window.et_battleCd = {
  21. hideOwnCds: true,
  22. hideReadyCds: true,
  23. ignoreAbilityCds: [],
  24. showRoomNumber: true
  25. };
  26.  
  27. if(localStorage.getItem('et_battleCd')) window.et_battleCd = Object.assign({}, window.et_battleCd, JSON.parse(localStorage.getItem('et_battleCd')));
  28.  
  29. $(window).on("beforeunload", function() {
  30. localStorage.setItem('et_battleCd', JSON.stringify(window.et_battleCd));
  31. });
  32.  
  33. var userId = Meteor.userId();
  34.  
  35. function getAbilityName(ability) {
  36. if(ability.id === "attack_up") return "attack";
  37. if(ability.id === "accuracy_up") return "accuracy";
  38. if(ability.id === "defense_up") return "defense";
  39. if(ability.id === "health_up") return "health";
  40. return ability.id.split("_").map(function(s, idx) {
  41. if(idx > 0) {
  42. return s.charAt(0).toUpperCase() + s.slice(1);
  43. } else return s;
  44. }).join('');
  45. }
  46.  
  47. function isPassiveAbility(ability) {
  48. if(["attack_up", "accuracy_up", "defense_up", "health_up", "poisoned_blade", "thirsty_fangs", "phantom_strikes"].some(function(name) { return ability.id === name; })) return true;
  49. return false;
  50. }
  51.  
  52. function addAbilityContainer(unit, ability) {
  53. if (window.et_battleCd.ignoreAbilityCds.some(function(name) {return ability.id === name;})) return;
  54. if($("[data-"+ability.id+"='"+unit.id+"']").length === 0) {
  55. var abilityContainer = document.createElement("div");
  56. abilityContainer.dataset[ability.id] = unit.id;
  57. var abilityImg = document.createElement("img");
  58. abilityImg.src = "/icons/"+getAbilityName(ability)+".svg";
  59. abilityImg.className = "small-icon icon-box";
  60. abilityContainer.append(abilityImg);
  61. abilityContainer.append(document.createElement("p"));
  62. var cdContainer = $("[data-cd-container="+unit.id+"]");
  63. cdContainer.append(abilityContainer);
  64. }
  65. }
  66.  
  67. function addUnitCds(unit) {
  68. if(unit.id === userId && window.et_battleCd.hideOwnCds) return;
  69.  
  70. // ensure unit exists first
  71. var interval = setInterval(function() {
  72. if ($('img#' + unit.id).length > 0) {
  73. var unitElem = $('img#' + unit.id);
  74. if($("[data-cd-container='"+ unit.id +"']").length === 0) {
  75. var cdContainer = document.createElement("div");
  76. cdContainer.className = "cd-container";
  77. cdContainer.dataset.cdContainer = unit.id;
  78. var parentElem = unitElem.closest(".flex-column").parent();
  79. parentElem.append(cdContainer);
  80. unit.abilities.forEach(function(ability) {
  81. if(!isPassiveAbility(ability)) addAbilityContainer(unit, ability);
  82. });
  83. }
  84. clearInterval(interval); // here interval is undefined, but when we call this function it will be defined in this context
  85. }
  86. }, 50);
  87. }
  88.  
  89. function updateUnitCds(unit) {
  90. if(unit.id === userId && window.et_battleCd.hideOwnCds) return;
  91.  
  92. unit.abilities.forEach(function(ability) {
  93. var abilityContainer = $("[data-"+ability.id+"='"+unit.id+"']");
  94. if(abilityContainer.length > 0) {
  95. if(window.et_battleCd.hideReadyCds && ability.currentCooldown <= 0) {
  96. abilityContainer.remove();
  97. } else {
  98. var abilityCd = abilityContainer.find("p");
  99. abilityCd.html(ability.currentCooldown < 0 ? "0 s" : ability.currentCooldown.toString().split('.')[0] + " s");
  100. }
  101. } else {
  102. if(ability.currentCooldown > 0 && !isPassiveAbility(ability)) addAbilityContainer(unit, ability);
  103. }
  104. });
  105. }
  106.  
  107. function removeUnitCds(unit) {
  108. var cdContainer = $("[data-cd-container='"+unit.id+"']");
  109. cdContainer.remove();
  110. }
  111.  
  112. function getRoomNumber(battleState) {
  113. if (battleState.hasOwnProperty("floor")) return "F" + battleState.floor + "\nR" + battleState.room;
  114. if (battleState.hasOwnProperty("level")) return "L" + battleState.level + "\nW" + battleState.wave;
  115. }
  116.  
  117. function addRoomNumber(battleState) {
  118. if($(".room-number-container").length > 0) {
  119. var roomNumberContainer = $(".room-number-container");
  120. roomNumberContainer.find("p").html(getRoomNumber(battleState));
  121. } else {
  122. var interval = setInterval(function() {
  123. if($(".room-number-container").length > 0) {
  124. clearInterval(interval); // here interval is undefined, but when we call this function it will be defined in this context
  125. } else if ($("#content > div.d-sm-flex.flex-grow > div > div:nth-child(1) > div > div.d-flex.my-1").length > 0) {
  126. var topBar = $("#content > div.d-sm-flex.flex-grow > div > div:nth-child(1) > div > div.d-flex.my-1");
  127. var roomNumberContainer = document.createElement("div");
  128. roomNumberContainer.className = "room-number-container buff-icon-container icon-box medium-icon";
  129. var roomNumber = document.createElement("p");
  130. roomNumber.innerHTML = getRoomNumber(battleState);
  131. roomNumberContainer.append(roomNumber);
  132. topBar.append(roomNumberContainer);
  133. clearInterval(interval); // here interval is undefined, but when we call this function it will be defined in this context
  134. }
  135. }, 50);
  136. }
  137. }
  138.  
  139. Meteor.connection._stream.on("message", function(json) {
  140. var message = JSON.parse(json);
  141. if(message.msg == "changed" && message.collection == "redis" && message.id.includes("battles-")) {
  142. // per-tick updates
  143. var battleState = JSON.parse(message.fields.value);
  144. battleState.units.forEach(function(unit) {
  145. addUnitCds(unit);
  146. updateUnitCds(unit);
  147. });
  148. battleState.deadUnits.forEach(function(unit) {
  149. removeUnitCds(unit);
  150. });
  151. if (window.et_battleCd.showRoomNumber) addRoomNumber(battleState);
  152. } else if(message.msg == "removed" && message.collection == "redis" && message.id.includes("battles-")) {
  153. // cleanup
  154. $(".cd-container").remove();
  155. $(".room-number-container").remove();
  156. }
  157. });
  158. })();