您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Tracks all possible in game drops per hour/day, as well as various battle stats over multiple battles.
当前为
// ==UserScript== // @name IQRPG Stats // @namespace https://www.iqrpg.com/ // @version 0.48 // @description Tracks all possible in game drops per hour/day, as well as various battle stats over multiple battles. // @author Coastis // @match http://iqrpg.com/game.html // @match https://iqrpg.com/game.html // @match http://www.iqrpg.com/game.html // @match https://www.iqrpg.com/game.html // @match http://test.iqrpg.com/game.html // @match https://test.iqrpg.com/game.html // @require http://code.jquery.com/jquery-latest.js // @grant GM_addStyle // ==/UserScript== //TODO add persistant storage between browser sessions /////////////////////////////////// /////////// config //////////////// /////////////////////////////////// const track_boss_battles = false; // true or false const track_clan_battles = false; // true or false const track_abyss_battles = false; // true or false const track_min_health_perc = true; // true or false const track_stats_n_drops = true; ////////////////////////////////////////////////////////////////// /////////// Don't change anything below this line //////////////// ////////////////////////////////////////////////////////////////// /* globals jQuery, $ */ // init var player_stats = { dmg:0, hits:0, misses:0, hp_perc:100 }; //TODO update from local storage var enemy_stats = { dmg:0, hits:0, misses:0, dodges:0 }; //TODO update from local storage var player_cache = ''; var enemy_cache = ''; var action_timer_cache = ''; var dropalyse_cache = ''; var dropalyse_store = {}; var dropalyse_rendered = false; var dropalyse_start_datum = Date.now(); var dropalyse_format = 'hour'; // hour/day/total // run every xxx ms updating stuffs // NOTE we set a delay of 500ms before starting to help slow rendering cpu's, otherwise there is the possibility of the very 1st drop not being counted setTimeout(function() { setInterval(iqrpgbs_loop, 100); setInterval(iqrpgbs_timer_loop, 1000); }, 500); // timer loop in it's own interval to optimise cpu usage. function iqrpgbs_timer_loop() { $('div.game-grid > div > div#iqrpgbs_dropalyse_container > div.main-section__body > div > div#iqrpgbs_dropalyse_timer').html( dropalyse_render_nice_timer() ); } // main loop function iqrpgbs_loop() { // first run? if(dropalyse_cache === '') { if( $( 'div#log-div > div' ).length > 0 ) { dropalyse_cache = dropalyse_clean_entry( $( 'div#log-div > div:first' ) ); } else { dropalyse_cache = '123 Temporary'; } } // insert p/h panel if(dropalyse_rendered === false) { dropalyse_insert_log(); dropalyse_rendered = true; } // NEW drop log parsing if( $("div.fixed-top > div.section-2 > div.action-timer > div.action-timer__text").length > 0 ) { let action_data = $("div.fixed-top > div.section-2 > div.action-timer > div.action-timer__text").prop('innerHTML').trim(); if(track_stats_n_drops === true && action_data !== action_timer_cache) { action_timer_cache = action_data; parse_drop_log(); // parse it dropalyse_render_content(); // update p/h } } else { return false; // skip as autos hasnt rendered yet } // battle stats var we_battling = false; var display_needs_update = false; // check we're on the battle page if(document.getElementsByClassName("battle-container").length > 0) { we_battling = true; } else return false; // and not in a boss battle - Boss Tokens if(track_boss_battles!==true && $("div.game-grid > div.main-game-section > div.main-section__body:contains('Boss Tokens')").length > 0) { return false; } // and not in a dungeon - Dungeoneering Exp if($("div.game-grid > div.main-game-section > div.main-section__body:contains('Dungeoneering Exp')").length > 0) { return false; } // and not in a standard clan battle - Clan Exp if(track_clan_battles!==true && $("div.game-grid > div.main-game-section > div.main-section__body:contains('Clan Exp')").length > 0) { return false; } // all is well, so let's get the mob name var n_obj = $("div.battle-container > div:nth-child(3) > div.participant-name > span"); if(n_obj.length > 0) { var mob_name = n_obj.prop('innerHTML').trim(); } else { return false; // couldn't find the mob name, lets skip just in case } // and not in a clan dragon battle // exact matches const clan_dragons = ['Baby Dragon','Young Dragon','Adolescent Dragon','Adult Dragon','Dragon']; if(track_clan_battles!==true && clan_dragons.indexOf(mob_name) > -1 ) { //console.log('Skipping Clan Dragons'); return false; } // and not in an abyss battle or clan battle - Abyssal if(track_abyss_battles!==true && ['Abyssal'].some(term => mob_name.includes(term))) { return false; } // all good if(we_battling === true) { // Add the battle stat panel to dom, if not already there if(!document.getElementById("iqrpgbs_bs_panel")) { var iqrpg_body = $( "div.game-grid > div.main-game-section > div.main-section__body" ); iqrpg_body[0].children[0].children[0].insertAdjacentHTML('beforeend', render_battle_stats_panel() ); document.getElementById('igrpg_bs_reset').addEventListener('click', iqrpg_bs_reset_stats, false); } // get the players stat line & compare it to previous stored stats var player_sl = $("div.game-grid > div.main-game-section > div.main-section__body > div > div > div > div:nth-child(2)"); if(player_sl.prop('innerHTML') !== player_cache) { player_cache = player_sl.prop('innerHTML'); parse_player_stat_line(player_sl); display_needs_update = true; } // get the mobs stat line & compare it to previous stored stats var mobs_sl = $("div.game-grid > div.main-game-section > div.main-section__body > div > div > div > div:nth-child(3)"); if(mobs_sl.prop('innerHTML') !== enemy_cache) { enemy_cache = mobs_sl.prop('innerHTML'); parse_enemy_stat_line(mobs_sl); display_needs_update = true; } // we already have display_needs_update, so let's use it as a trigger for our new health tracking if(display_needs_update === true && track_min_health_perc === true) { let hp_sl = $("div.game-grid > div.main-game-section > div.main-section__body > div > div > div > div.battle-container > div.battle-container__section > div:nth-child(2) > div.progress__text"); let hp_totals = hp_sl.prop('innerHTML').split(" / "); let this_perc = (parseInt(hp_totals[0].replaceAll(",", "")) / parseInt(hp_totals[1].replaceAll(",", ""))) * 100; if(this_perc < player_stats.hp_perc) player_stats.hp_perc = this_perc; } // update displayed values if(display_needs_update === true) { update_display(); } } } // parses the drop log and buils array of quantified contents function parse_drop_log() { const skiplist = ['[Gold]','Gold Rush:','Action Bonus:','Resource Rush:','Skill:','Mastery:','[Wood]','[Stone]','[Metal]']; let first_log_entry = ''; first_log_entry = dropalyse_clean_entry( $( 'div#log-div > div:first' ) ); // capture cached entry for possible later use let count = 0; $( 'div#log-div > div' ).each(function( index ) { // check if already analysed let str = dropalyse_clean_entry($(this)); //console.log("str - " + str); //console.log("cache - " + dropalyse_cache ); if(str === dropalyse_cache) return false; // break loop // skip unwanted if (skiplist.some(v => str.includes(v))) return true; // continue loop // parse into time, qty, item let entry = parse_drop_log_entry($( this )); count++; // add to data store if (typeof dropalyse_store[entry.item] !== 'undefined') { dropalyse_store[entry.item] += entry.qty; } else { dropalyse_store[entry.item] = entry.qty; } }); // do we have new entries? if(count>0) { dropalyse_cache = first_log_entry; console.log("------------------------------"); console.log(dropalyse_store); } } function dropalyse_clean_entry(txt) { //console.log("Cleaning - " + $(txt).text() ); var r = txt.clone(); r.find('.popup').remove(); //console.log("Clean - " + r.text() ); return r.text(); } function parse_drop_log_entry(entry) { let r = {}; // timestamp - not needed?? r.timestamp = $('span:first', entry).text(); // item let data_str = $('span', entry).eq(1).text(); let matches = data_str.match(/^[+-]?\d+(\.\d+)?[%]?/g); if(matches && matches.length>0) { let n = matches[0]; r.qty = Number(n.replace('+', '').replace('%', '')); r.item = data_str.replace(n,'').trim(); } else { r.qty = 1; // it's something unusual r.item = data_str.trim(); } // strip extra data r.item = r.item.split("]")[0].replace("[","").replace("]",""); return r; } function dropalyse_render_content() { let html = ''; if(Object.entries(dropalyse_store).length == 0 ) { html = '<div>Waiting for drops...</div>'; } else { for (let [key, qty] of Object.entries(dropalyse_store)) { let formatted_qty = qty; if(dropalyse_format==='hour') { formatted_qty = ( ( qty / dropalyse_get_time_elapsed() ) * 60 * 60 ).toFixed(2); } else if(dropalyse_format==='day') { formatted_qty = ( ( qty / dropalyse_get_time_elapsed() ) * 60 * 60 * 24).toFixed(2); } html += '<div style="position: relative;" class="iqrpgbs_hover_highlight"><div style="display: flex; justify-content: space-between;">' + '<span>' + key + '</span>' + '<span style="color:#3c3">' + formatted_qty + '</span>' + '</div></div>'; } } $('div#iqrpgbs_drop_log_content').html(html); } function dropalyse_insert_log() { let html = `<div id="iqrpgbs_dropalyse_container" class="main-section" style="background-color: #0a0a0a;margin-bottom: .2rem;border: 1px solid #333;"> <div id="iqrpgbs_drop_log_header"> <p>Drops per hour</p><!----> </div> <div class="main-section__body" style="border-top: 1px solid #333;padding: .5rem;"> <div> <div id="iqrpgbs_drop_log_content">Waiting for drops...</div> <div id="iqrpgbs_dropalyse_timer">0:0:0:0</div> <div id="iqrpgbs_dropalyse_options">[<a id="iqrpgbs_dropalyse_opt_hour" href="#">Hour</a> - <a id="iqrpgbs_dropalyse_opt_day" href="#">Day</a> - <a id="iqrpgbs_dropalyse_opt_total" href="#">Total</a>] [<a id="iqrpgbs_dropalyse_reset" href="#">Reset</a>]</div> </div></div></div>`; $(html).insertAfter($('div.game-grid > div:first > div.main-section').last()); // setup format options and events dropalyse_set_format(dropalyse_format); // set the initial format document.getElementById('iqrpgbs_dropalyse_opt_hour').addEventListener("click", function(e) { e.preventDefault(); dropalyse_set_format('hour'); }); document.getElementById('iqrpgbs_dropalyse_opt_day').addEventListener("click", function(e) { e.preventDefault(); dropalyse_set_format('day'); }); document.getElementById('iqrpgbs_dropalyse_opt_total').addEventListener("click", function(e) { e.preventDefault(); dropalyse_set_format('total'); }); document.getElementById('iqrpgbs_dropalyse_reset').addEventListener("click", function(e) { e.preventDefault(); dropalyse_reset(); }); } function dropalyse_reset() { dropalyse_store = {}; dropalyse_start_datum = Date.now(); dropalyse_render_content(); } function dropalyse_set_format(format) { $('a#iqrpgbs_dropalyse_opt_hour').removeClass("iqrpgbs_highlight"); $('a#iqrpgbs_dropalyse_opt_day').removeClass("iqrpgbs_highlight"); $('a#iqrpgbs_dropalyse_opt_total').removeClass("iqrpgbs_highlight"); if(format==='hour') { dropalyse_format = 'hour'; $('a#iqrpgbs_dropalyse_opt_hour').addClass("iqrpgbs_highlight"); $('div#iqrpgbs_drop_log_header > p').html('Drops Per Hour'); } else if(format==='day') { dropalyse_format = 'day'; $('a#iqrpgbs_dropalyse_opt_day').addClass("iqrpgbs_highlight"); $('div#iqrpgbs_drop_log_header > p').html('Drops Per Day'); } else if(format==='total') { dropalyse_format = 'total'; $('a#iqrpgbs_dropalyse_opt_total').addClass("iqrpgbs_highlight"); $('div#iqrpgbs_drop_log_header > p').html('Drops - Total'); } dropalyse_render_content(); // update view } function dropalyse_get_time_elapsed() { return ( Date.now() - dropalyse_start_datum )/1000; } function dropalyse_render_nice_timer() { var delta = dropalyse_get_time_elapsed(); var days = Math.floor(delta / 86400); delta -= days * 86400; var hours = Math.floor(delta / 3600) % 24; delta -= hours * 3600; var minutes = Math.floor(delta / 60) % 60; delta -= minutes * 60; var seconds = delta % 60; let html = ''; if(days>0) html += days + 'd '; if(hours>0||days>0) html += hours + 'h '; if(hours>0||days>0||minutes>0) html += minutes + 'm '; html += Math.floor(seconds) + 's'; return html; } function parse_player_stat_line(player_sl) { var hits = player_sl.find("p:nth-child(1) > span:nth-child(2)"); if(hits.length > 0) { var actual_hits = hits.prop('innerHTML').replaceAll(" time(s)", ""); player_stats.hits += parseInt(actual_hits); } var dmg = player_sl.find("p:nth-child(1) > span:nth-child(3)"); if(dmg.length > 0 && hits.length > 0) { var actual_dmg = dmg.prop('innerHTML').replaceAll(" damage", "").replaceAll(",", ""); player_stats.dmg += parseInt(actual_dmg) * parseInt(actual_hits); } var misses = player_sl.find("p:nth-child(2) > span:nth-child(1)"); if(misses.length > 0) { var actual_misses = misses.prop('innerHTML').replaceAll(" time(s)", ""); player_stats.misses += parseInt(actual_misses); } } function parse_enemy_stat_line(stat_line) { var hits = stat_line.find("p:nth-child(1) > span:nth-child(2)"); if(hits.length > 0) { var actual_hits = hits.prop('innerHTML').replaceAll(" time(s)", ""); enemy_stats.hits += parseInt(actual_hits); } var dmg = stat_line.find("p:nth-child(1) > span:nth-child(3)"); if(dmg.length > 0 && hits.length > 0) { var actual_dmg = dmg.prop('innerHTML').replaceAll(" damage", "").replaceAll(",", ""); enemy_stats.dmg += parseInt(actual_dmg) * parseInt(actual_hits); } var misses = stat_line.find("p:nth-child(2) > span:nth-child(2)"); if(misses.length > 0) { var actual_misses = misses.prop('innerHTML').replaceAll(" time(s)", ""); enemy_stats.misses += parseInt(actual_misses); } var dodges = stat_line.find("p:nth-child(2) > span:nth-child(3)"); if(dodges.length > 0) { var actual_dodges = dodges.prop('innerHTML').replaceAll(" attack(s)", ""); enemy_stats.dodges += parseInt(actual_dodges); } } function iqrpg_bs_reset_stats(e) { e.preventDefault(); player_stats.dmg = 0; player_stats.hits = 0; player_stats.misses = 0; player_stats.hp_perc = 100; enemy_stats.dmg = 0; enemy_stats.hits = 0; enemy_stats.misses = 0; enemy_stats.dodges = 0; update_display(); } function update_display() { // players $("#iqrpgbs_pl_dmg").html(new Intl.NumberFormat().format(player_stats.dmg)); $("#iqrpgbs_pl_hits").html(new Intl.NumberFormat().format(player_stats.hits)); var avg = Math.round(player_stats.dmg / player_stats.hits) || 0; $("#iqrpgbs_pl_avg").html(new Intl.NumberFormat().format(avg)); var acc = (player_stats.hits / (player_stats.hits + player_stats.misses))*100 || 0; $("#iqrpgbs_pl_acc").html(new Intl.NumberFormat().format(acc.toFixed(2))); let min_hp = player_stats.hp_perc || 0; $("#iqrpgbs_min_hp").html(new Intl.NumberFormat().format(min_hp)); // enemy var enemy_avg = Math.round(enemy_stats.dmg / enemy_stats.hits) || 0; $("#iqrpgbs_enmy_avg").html(new Intl.NumberFormat().format(enemy_avg)); var enemy_acc = ( (enemy_stats.hits + enemy_stats.dodges) / (enemy_stats.hits + enemy_stats.misses + enemy_stats.dodges))*100 || 0; $("#iqrpgbs_enmy_acc").html(new Intl.NumberFormat().format(enemy_acc.toFixed(2))); var enemy_dodges = (enemy_stats.dodges / (enemy_stats.hits /*+ enemy_stats.misses*/ + enemy_stats.dodges))*100 || 0; $("#iqrpgbs_enmy_dodges").html(new Intl.NumberFormat().format(enemy_dodges.toFixed(2))); } function render_battle_stats_panel() { var content = ` <div id="iqrpgbs_bs_panel" class="margin-top-large"> <div>Battle Stats <span>by Coastis</span></div> <div>You dealt a total of <span id="iqrpgbs_pl_dmg">0</span> damage in <span id="iqrpgbs_pl_hits">0</span> hits, with an average of <span id="iqrpgbs_pl_avg">0</span> per hit and <span id="iqrpgbs_pl_acc">0</span>% accuracy</div> <div>Enemy dealt an average of <span id="iqrpgbs_enmy_avg">0</span> per hit with an accuracy of <span id="iqrpgbs_enmy_acc">0</span>%, and you dodged <span id="iqrpgbs_enmy_dodges">0</span>% of attacks</div> `; if(track_min_health_perc === true) content += '<div>Your health reached a low of <span id="iqrpgbs_min_hp">100</span>%</div>'; content += '<div>[<a href="#" id="igrpg_bs_reset">Reset Battle Stats</a>]</div>'; content += '</div>'; return content; } GM_addStyle ( ` div#iqrpgbs_bs_panel div { text-align:center;padding:1px;} div#iqrpgbs_bs_panel div:nth-child(1) { font-weight:bold;} div#iqrpgbs_bs_panel div:nth-child(1) span { font-size:8px;font-weight:normal;} div#iqrpgbs_drop_log_header {display: flex; justify-content: center; align-items: center; padding: .5rem; background: linear-gradient(#000,#151515);} div#iqrpgbs_dropalyse_timer { padding:2px; margin-top:4px; text-align:center; } div#iqrpgbs_dropalyse_options { padding:2px; text-align:center; } .iqrpgbs_highlight { color:#3c3 !important; } div.iqrpgbs_hover_highlight:hover { background-color:#222222; } ` );