IQRPG Battle Stats

Tracks various battle stats over multiple battles.

目前为 2021-12-13 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name IQRPG Battle Stats
  3. // @namespace https://www.iqrpg.com/
  4. // @version 0.43
  5. // @description Tracks various battle stats over multiple battles.
  6. // @author Coastis
  7. // @match http://iqrpg.com/game.php
  8. // @match https://iqrpg.com/game.php
  9. // @match http://www.iqrpg.com/game.php
  10. // @match https://www.iqrpg.com/game.php
  11. // @match http://test.iqrpg.com/game.php
  12. // @match https://test.iqrpg.com/game.php
  13. // @require http://code.jquery.com/jquery-latest.js
  14. // @grant GM_addStyle
  15. // ==/UserScript==
  16.  
  17. //TODO add persistant storage between browser sessions
  18. //BUG clan dragons are still being counted!!
  19.  
  20. ///////////////////////////////////
  21. /////////// config ////////////////
  22. ///////////////////////////////////
  23.  
  24. const track_boss_battles = false; // true or false
  25. const track_clan_battles = false; // true or false
  26. const track_abyss_battles = false; // true or false
  27.  
  28. //////////////////////////////////////////////////////////////////
  29. /////////// Don't change anything below this line ////////////////
  30. //////////////////////////////////////////////////////////////////
  31.  
  32. /* globals jQuery, $ */
  33.  
  34. // init
  35. var player_stats = { dmg:0, hits:0, misses:0 }; //TODO update from local storage
  36. var enemy_stats = { dmg:0, hits:0, misses:0, dodges:0 }; //TODO update from local storage
  37. var player_cache = '';
  38. var enemy_cache = '';
  39.  
  40. // run every xxx ms updating stuffs
  41. setInterval(iqrpgbs_loop, 100);
  42.  
  43. // main loop
  44. function iqrpgbs_loop() {
  45.  
  46. var we_battling = false;
  47. var display_needs_update = false;
  48.  
  49. // check we're on the battle page
  50. if(document.getElementsByClassName("battle-container").length > 0) {
  51. we_battling = true;
  52. } else return false;
  53.  
  54. // and not in a boss battle - Boss Tokens
  55. if(track_boss_battles!==true && $("div.game-grid > div.main-game-section > div.main-section__body:contains('Boss Tokens')").length > 0) {
  56. return false;
  57. }
  58.  
  59. // and not in a dungeon - Dungeoneering Exp
  60. if($("div.game-grid > div.main-game-section > div.main-section__body:contains('Dungeoneering Exp')").length > 0) {
  61. return false;
  62. }
  63.  
  64. // and not in a standard clan battle - Clan Exp
  65. if(track_clan_battles!==true && $("div.game-grid > div.main-game-section > div.main-section__body:contains('Clan Exp')").length > 0) {
  66. return false;
  67. }
  68.  
  69. // all is well, so let's get the mob name
  70. var n_obj = $("div.battle-container > div:nth-child(3) > div.participant-name > span");
  71. if(n_obj.length > 0) {
  72. var mob_name = n_obj.prop('innerHTML').trim();
  73. } else {
  74. return false; // couldn't find the mob name, lets skip just in case
  75. }
  76.  
  77. // and not in a clan dragon battle
  78. // exact matches
  79. const clan_dragons = ['Baby Dragon','Young Dragon','Adolescent Dragon','Adult Dragon','Dragon'];
  80. if(track_clan_battles!==true && clan_dragons.indexOf(mob_name) > -1 ) {
  81. console.log('Skipping Clan Dragons');
  82. return false;
  83. }
  84.  
  85. // and not in an abyss battle or clan battle - Abyssal
  86. if(track_abyss_battles!==true && ['Abyssal'].some(term => mob_name.includes(term))) {
  87. return false;
  88. }
  89.  
  90. // all good
  91. if(we_battling === true) {
  92.  
  93. // Add the battle stat panel to dom, if not already there
  94. if(!document.getElementById("iqrpgbs_bs_panel")) {
  95. var iqrpg_body = $( "div.game-grid > div.main-game-section > div.main-section__body" );
  96. iqrpg_body[0].children[0].children[0].insertAdjacentHTML('beforeend', render_battle_stats_panel() );
  97. document.getElementById('igrpg_bs_reset').addEventListener('click', iqrpg_bs_reset_stats, false);
  98. }
  99.  
  100. // get the players stat line & compare it to previous stored stats
  101. var player_sl = $("div.game-grid > div.main-game-section > div.main-section__body > div > div > div > div:nth-child(2)");
  102. if(player_sl.prop('innerHTML') !== player_cache) {
  103. player_cache = player_sl.prop('innerHTML');
  104. parse_player_stat_line(player_sl);
  105. display_needs_update = true;
  106. }
  107.  
  108. // get the mobs stat line & compare it to previous stored stats
  109. var mobs_sl = $("div.game-grid > div.main-game-section > div.main-section__body > div > div > div > div:nth-child(3)");
  110. if(mobs_sl.prop('innerHTML') !== enemy_cache) {
  111. enemy_cache = mobs_sl.prop('innerHTML');
  112. parse_enemy_stat_line(mobs_sl);
  113. display_needs_update = true;
  114. }
  115.  
  116. // update displayed values
  117. if(display_needs_update === true) {
  118. update_display();
  119. }
  120.  
  121. }
  122. }
  123.  
  124. function parse_player_stat_line(player_sl) {
  125. var hits = player_sl.find("p:nth-child(1) > span:nth-child(2)");
  126. if(hits.length > 0) {
  127. var actual_hits = hits.prop('innerHTML').replaceAll(" time(s)", "");
  128. player_stats.hits += parseInt(actual_hits);
  129. }
  130. var dmg = player_sl.find("p:nth-child(1) > span:nth-child(3)");
  131. if(dmg.length > 0 && hits.length > 0) {
  132. var actual_dmg = dmg.prop('innerHTML').replaceAll(" damage", "").replaceAll(",", "");
  133. player_stats.dmg += parseInt(actual_dmg) * parseInt(actual_hits);
  134. }
  135. var misses = player_sl.find("p:nth-child(2) > span:nth-child(1)");
  136. if(misses.length > 0) {
  137. var actual_misses = misses.prop('innerHTML').replaceAll(" time(s)", "");
  138. player_stats.misses += parseInt(actual_misses);
  139. }
  140. }
  141.  
  142. function parse_enemy_stat_line(stat_line) {
  143. var hits = stat_line.find("p:nth-child(1) > span:nth-child(2)");
  144. if(hits.length > 0) {
  145. var actual_hits = hits.prop('innerHTML').replaceAll(" time(s)", "");
  146. enemy_stats.hits += parseInt(actual_hits);
  147. }
  148. var dmg = stat_line.find("p:nth-child(1) > span:nth-child(3)");
  149. if(dmg.length > 0 && hits.length > 0) {
  150. var actual_dmg = dmg.prop('innerHTML').replaceAll(" damage", "").replaceAll(",", "");
  151. enemy_stats.dmg += parseInt(actual_dmg) * parseInt(actual_hits);
  152. }
  153. var misses = stat_line.find("p:nth-child(2) > span:nth-child(2)");
  154. if(misses.length > 0) {
  155. var actual_misses = misses.prop('innerHTML').replaceAll(" time(s)", "");
  156. enemy_stats.misses += parseInt(actual_misses);
  157. }
  158. var dodges = stat_line.find("p:nth-child(2) > span:nth-child(3)");
  159. if(dodges.length > 0) {
  160. var actual_dodges = dodges.prop('innerHTML').replaceAll(" attack(s)", "");
  161. enemy_stats.dodges += parseInt(actual_dodges);
  162. }
  163. }
  164.  
  165. function iqrpg_bs_reset_stats(e) {
  166. e.preventDefault();
  167. player_stats.dmg = 0;
  168. player_stats.hits = 0;
  169. player_stats.misses = 0;
  170. enemy_stats.dmg = 0;
  171. enemy_stats.hits = 0;
  172. enemy_stats.misses = 0;
  173. enemy_stats.dodges = 0;
  174. update_display();
  175. }
  176.  
  177. function update_display() {
  178. // players
  179. $("#iqrpgbs_pl_dmg").html(new Intl.NumberFormat().format(player_stats.dmg));
  180. $("#iqrpgbs_pl_hits").html(new Intl.NumberFormat().format(player_stats.hits));
  181. var avg = Math.round(player_stats.dmg / player_stats.hits) || 0;
  182. $("#iqrpgbs_pl_avg").html(new Intl.NumberFormat().format(avg));
  183. var acc = (player_stats.hits / (player_stats.hits + player_stats.misses))*100 || 0;
  184. $("#iqrpgbs_pl_acc").html(new Intl.NumberFormat().format(acc.toFixed(2)));
  185. // enemy
  186. var enemy_avg = Math.round(enemy_stats.dmg / enemy_stats.hits) || 0;
  187. $("#iqrpgbs_enmy_avg").html(new Intl.NumberFormat().format(enemy_avg));
  188. var enemy_acc = ( (enemy_stats.hits + enemy_stats.dodges) / (enemy_stats.hits + enemy_stats.misses + enemy_stats.dodges))*100 || 0;
  189. $("#iqrpgbs_enmy_acc").html(new Intl.NumberFormat().format(enemy_acc.toFixed(2)));
  190. var enemy_dodges = (enemy_stats.dodges / (enemy_stats.hits /*+ enemy_stats.misses*/ + enemy_stats.dodges))*100 || 0;
  191. $("#iqrpgbs_enmy_dodges").html(new Intl.NumberFormat().format(enemy_dodges.toFixed(2)));
  192. }
  193.  
  194. function render_battle_stats_panel() {
  195. var content = `
  196. <div id="iqrpgbs_bs_panel" class="margin-top-large">
  197. <div>Battle Stats <span>by Coastis</span></div>
  198. <div>You dealt a total of <span id="iqrpgbs_pl_dmg">0</span> damage in <span id="iqrpgbs_pl_hits">0</span> hits,
  199. with an average of <span id="iqrpgbs_pl_avg">0</span> per hit and <span id="iqrpgbs_pl_acc">0</span>% accuracy</div>
  200. <div>Enemy dealt an average of <span id="iqrpgbs_enmy_avg">0</span> per hit
  201. with an accuracy of <span id="iqrpgbs_enmy_acc">0</span>%, and you dodged <span id="iqrpgbs_enmy_dodges">0</span>% of attacks</div>
  202. <div>[<a href="#" id="igrpg_bs_reset">Reset Battle Stats</a>]</div>
  203. </div>
  204. `;
  205. return content;
  206. }
  207.  
  208. GM_addStyle ( `
  209. div#iqrpgbs_bs_panel div { text-align:center;padding:1px;}
  210. div#iqrpgbs_bs_panel div:nth-child(1) { font-weight:bold;}
  211. div#iqrpgbs_bs_panel div:nth-child(1) span { font-size:8px;font-weight:normal;}
  212. ` );
  213.