NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Horse Genetics Extender // @namespace cryptal // @license MIT // @description For Howrse: Extends the genetic information of a horse // @author CryptalEquine // @include */elevage/chevaux/cheval?id=* // @include */elevage/fiche/?id=* // @version 1.2.1 // @run-at document-start // @noframes true // @grant unsafeWindow // @grant GM_setValue // @grant GM_getValue // @require https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js // @require https://gist.githubusercontent.com/BrockA/2625891/raw/9c97aa67ff9c5d56be34a55ad6c18a314e5eb548/waitForKeyElements.js // @updateURL https://openuserjs.org/meta/CryptalEquine/Horse_Genetics_Extender.meta.js // @downloadURL https://openuserjs.org/install/CryptalEquine/Horse_Genetics_Extender.user.js // ==/UserScript== // Remove the elements created by the now-inferior Genetics Revealer script waitForKeyElements('span[style="font-size: 0.85em; color: blue;"]', function(e) { $('span[style="font-size: 0.85em; color: blue;"]').remove(); }, false); // Wait til the necessary HTML elements have loaded waitForKeyElements("#origins-body-content", function(e) { (async function() { var decimals = 5; // Change this to change how many decimal points show var skills = ['endurance', 'vitesse', 'dressage', 'galop', 'trot', 'saut']; var skillsEng = ['stamina', 'speed', 'dressage', 'gallop', 'trot', 'jumping']; var skillsShort = ['st', 'sp', 'dr', 'ga', 'tr', 'ju']; var id = location.href.match(/id\=[0-9]+/g)[0].match(/[0-9]+/g)[0].trim(); var db = JSON.parse(GM_getValue('geneticsDB', '{}')); var queries = [], perc = {}, parents = $('#origins-body-content').find('a.horsename'); function acc(x, n) { return parseFloat(x.toFixed(n)); } function abort() { console.log('NOT ENOUGH DATA TO DO EXTENDED GENETIC COMPARISON!'); for (var i = 0; i < skillsShort.length; i++) { $('#' + skills[i] + 'Genetique').parent().append('<span class="genetics_perc">' + acc(perc[skillsShort[i]] * 100, 3) + '%</span>'); } } function getStats(_id, _html, _owner) { if (_id == null || _html == null || _owner == null) return null; var dbEntry = {gp: 0, date: (new Date()).getTime()}; for (var i = 0, stat = 0; i < skillsShort.length; i++) { if (!_owner) stat = parseFloat(_html.find('#' + skills[i] + 'Genetique').text()); else stat = unsafeWindow[skills[i] + 'Genetique']; // Has better precision dbEntry[skillsShort[i]] = stat; dbEntry.gp += stat; } dbEntry.gp = acc(dbEntry.gp, decimals); dbEntry.blup = getBLUP(_html.find('#genetic-body-content').find('table').eq(1).find('strong.nowrap').text()); db[_id] = dbEntry; GM_setValue('geneticsDB', JSON.stringify(db)); return dbEntry; } function getBLUP(_value) { var _conversion = parseFloat(_value); return isNaN(_conversion) ? 100 : _conversion; } function checkPage(_html, _id) { var _page = _html == null ? null : $(typeof _html != 'string' ? _html[0] : _html); // If parent link was valid but horse is in haven/heaven _page = _page != null && _page.find('img[alt="paradis"]').length > 0 ? null : _page; if (_page == null && db[_id] == null) console.log("Cannot query " + _id + " page, as either they're dead or don't exist"); return _page; } if (db[id] == null) getStats(id, $('#tab-genetics'), location.href.indexOf('elevage/fiche') == -1); // Calculate basic genetic percentages for (var i = 0; i < skillsShort.length; i++) { perc[skillsShort[i]] = (db[id][skillsShort[i]] / db[id].gp); } // Script only works if there's two valid parent horse links if (parents.length == 2) { var sireLink = parents.eq(0).attr('href'); var sireID = sireLink.match(/[0-9]+/g)[0].trim(); var damLink = parents.eq(1).attr('href'); var damID = damLink.match(/[0-9]+/g)[0].trim(); // GET request for parents horse page if (db[sireID] == null) queries.push($.ajax({method: 'GET', url: location.origin + sireLink})); if (db[damID] == null) queries.push($.ajax({method: 'GET', url: location.origin + damLink})); // Once GET requests are done $.when(...queries).done(function(sireHTML, damHTML) { if (db[sireID] != null) { damHTML = sireHTML; sireHTML = null; } var sirePage = checkPage(sireHTML, sireID); var damPage = checkPage(damHTML, damID); if ((sirePage == null && db[sireID] == null) || (damPage == null && db[damID] == null)) return abort(); var sire = getStats(sireID, sirePage, false) || db[sireID]; var dam = getStats(damID, damPage, false) || db[damID]; var pAvg = {}, pAvgPerc = {}, diffAbs = {}, diff = {}; // Absolute values of parents GP and differences for (var i = 0; i < skillsShort.length; i++) { pAvg[skillsShort[i]] = (sire[skillsShort[i]] + dam[skillsShort[i]]) / 2; diffAbs[skillsShort[i]] = acc(db[id][skillsShort[i]] - pAvg[skillsShort[i]], decimals); } pAvg.gp = acc((sire.gp + dam.gp) / 2, decimals); pAvg.blup = acc((sire.blup + dam.blup) / 2, decimals); diff.gp = acc(db[id].gp - ((sire.gp + dam.gp) / 2), decimals); var ibsMaxNT = acc(db[id].gp * 0.208 * (((sire.blup + dam.blup) / 2) / 100), 2); var ibsMaxNT_IB = acc(db[id].gp * 0.208, 2); var ibsMaxT = acc(db[id].gp * 0.312 * (((sire.blup + dam.blup) / 2) / 100), 2); var ibsMaxT_IB = acc(db[id].gp * 0.312, 2); var maxSkills = acc(ibsMaxT + db[id].gp + 238.5, 2); var maxSkills_IB = acc(ibsMaxT_IB + db[id].gp + 238.5, 2); // Percentages of parents GP and differences for (var i = 0; i < skillsShort.length; i++) { pAvgPerc[skillsShort[i]] = pAvg[skillsShort[i]] / pAvg.gp; diff[skillsShort[i]] = acc(100 * (perc[skillsShort[i]] - pAvgPerc[skillsShort[i]]), decimals); } var css = `<style> .genetics_extender {color: blue; margin-top: 5px;} .genetics_formulas {color: blue;} #staminaGenetics, #speedGenetics, #dressageGenetics, #gallopGenetics, #trotGenetics, #jumpingGenetics { font-size: 0.9em; } </style>`; var gtEl = $('#genetic-body-content').find('table').eq(0); gtEl.after(css + '<table class="width-100 genetics_extender">' + gtEl.html() + '</table>'); var gpTitle = $('.genetics_extender').find('tr').eq(0).find('td').first().find('strong'); var gpValue = $('.genetics_extender').find('tr').eq(0).find('td').last().find('strong'); gpTitle.html(gpTitle.html() + ' change'); gpValue.html('+' + diff.gp); for (var i = 0; i < skills.length; i++) { $('.genetics_extender').find('#' + skills[i] + 'Genetique').attr('id', skillsEng[i] + 'Genetics'); $('#' + skillsEng[i] + 'Genetics').html((diff[skillsShort[i]] > 0 ? ('+' + diff[skillsShort[i]]) : diff[skillsShort[i]]) + '%'); $('#' + skills[i] + 'Genetique').parent().append('<span class="genetics_perc">' + acc(perc[skillsShort[i]] * 100, 3) + '%</span>'); } $('.genetics_extender').find('span').removeClass('bold'); $('.genetics_extender').find('tbody').append(` <tr class="genetics_formulas"></tr> <tr class="genetics_config"></tr> <tr class="genetics_ibsNT"></tr> <tr class="genetics_ibsT"></tr> <tr class="genetics_maxSkills"></tr> <tr class="genetics_resetParents"></tr> `); $('.genetics_formulas').html('<td class="first align-left bold"></td><td class="align-left bold"></td><td></td><td class="last align-right" colspan="2"></td>'); $('.genetics_config').html('<td class="first"></td><td></td><td class="last align-right" colspan="3"></td>'); $('.genetics_ibsNT').html('<td class="first align-left bold" colspan="3">Max IBS (no tears):</td><td colspan="1">' + ibsMaxNT + (ibsMaxNT < ibsMaxNT_IB ? ' [' + ibsMaxNT_IB + ']' : '') + '</td>'); $('.genetics_ibsT').html('<td class="first align-left bold" colspan="3">Max IBS (tears):</td><td colspan="1">' + ibsMaxT + (ibsMaxT < ibsMaxT_IB ? ' [' + ibsMaxT_IB + ']' : '') + '</td>'); $('.genetics_maxSkills').html('<td class="first align-left bold" colspan="3">Max Skills (WOY/POC):</td><td colspan="1">' + maxSkills + (maxSkills < maxSkills_IB ? ' [' + maxSkills_IB + ']' : '') + '</td>'); $('.genetics_resetParents').html('<td class="first align-left bold" colspan="4"><input type="button" value="Reset Parents Stats"></td>'); $('.genetics_formulas').find('td').last().html(`<select id="genetics_weightings"> <option id="tr45_sp30_dr20">Trot (Prix)</option> <option id="ga45_sp30_dr20">Gallop (Prix)</option> <option id="dr45_tr30_ga20">Dressage (Prix)</option> <option id="st45_dr30_ju20">Cross Country (Prix)</option> <option id="ju45_dr30_sp20">Show Jumping (Prix)</option> <option id="sp45_st30_ga20">Barrel Racing (Prix)</option> <option id="st45_dr30_sp20">Cutting (Prix)</option> <option id="dr45_tr30_ju20">Trail Class (Prix)</option> <option id="ga45_dr30_st20">Reining (Prix)</option> <option id="tr45_st30_dr20">Western Pleasure (Prix)</option> <option id="st100">Pure Stamina (Genetics)</option> <option id="sp100">Pure Speed (Genetics)</option> <option id="dr100">Pure Dressage (Genetics)</option> <option id="ga100">Pure Gallop (Genetics)</option> <option id="tr100">Pure Trot (Genetics)</option> <option id="ju100">Pure Jump (Genetics)</option> </select>`); function changeToggle(e) { var absolutes = $(e.target).is(':checked'); for (var i = 0; i < skills.length; i++) { if (!absolutes) $('#' + skillsEng[i] + 'Genetics').html((diff[skillsShort[i]] > 0 ? ('+' + diff[skillsShort[i]]) : diff[skillsShort[i]]) + '%'); else $('#' + skillsEng[i] + 'Genetics').html((diffAbs[skillsShort[i]] > 0 ? ('+' + diffAbs[skillsShort[i]]) : diffAbs[skillsShort[i]])); } } $('.genetics_config').find('td').last().html('<input type="checkbox" id="genetics_toggle"> <span>Show absolute values</span>'); $('#genetics_toggle').change(changeToggle); $('.genetics_config').find('span').click(function(){ $('#genetics_toggle').click(); }); function getFormula(obj, w) { var f = 0; for (var i = 0; i < w.length; i++) { f += obj[w[0].substr(0, 2)] * parseInt(w[0].substr(2)); } return f; } function showFormula() { var weightingsText = $('#genetics_weightings').children(":selected").attr('id').split('_'); var parentFormula = getFormula(pAvgPerc, weightingsText); var childFormula = getFormula(perc, weightingsText); var formula = acc((childFormula - parentFormula) * 1000, decimals); var formulaHTML = '<span>' + ((formula > 0 ? '+' : '') + formula) + '</span>'; $('.genetics_formulas').find('td').first().html('Competition:'); $('.genetics_formulas').find('td').eq(1).html(formulaHTML); } function resetParents() { console.log("Re-requesting parents genetic data ..."); var resetQueries = []; resetQueries.push($.ajax({method: 'GET', url: location.origin + sireLink})); resetQueries.push($.ajax({method: 'GET', url: location.origin + damLink})); $.when(...resetQueries).done(function(_sireHTML, _damHTML) { var _sirePage = checkPage(_sireHTML, sireID); var _damPage = checkPage(_damHTML, damID); db[sireID].blup = getBLUP(_sirePage.find('#genetic-body-content').find('table').eq(1).find('strong.nowrap').text()); db[damID].blup = getBLUP(_damPage.find('#genetic-body-content').find('table').eq(1).find('strong.nowrap').text()); console.log('Sire BLUP: ' + acc(db[sireID].blup, 2) + ', Dam BLUP: ' + acc(db[damID].blup, 2)); var comboBLUP = (db[sireID].blup + db[damID].blup) / 2; var ibsMaxNT = acc(db[id].gp * 0.208 * (comboBLUP / 100), 2); var ibsMaxNT_IB = acc(db[id].gp * 0.208, 2); var ibsMaxT = acc(db[id].gp * 0.312 * (comboBLUP / 100), 2); var ibsMaxT_IB = acc(db[id].gp * 0.312, 2); var maxSkills = acc(ibsMaxT + db[id].gp + 238.5, 2); var maxSkills_IB = acc(ibsMaxT_IB + db[id].gp + 238.5, 2); $('.genetics_ibsNT').html('<td class="first align-left bold" colspan="3">Max IBS (no tears):</td><td colspan="1">' + ibsMaxNT + (ibsMaxNT < ibsMaxNT_IB ? ' [' + ibsMaxNT_IB + ']' : '') + '</td>'); $('.genetics_ibsT').html('<td class="first align-left bold" colspan="3">Max IBS (tears):</td><td colspan="1">' + ibsMaxT + (ibsMaxT < ibsMaxT_IB ? ' [' + ibsMaxT_IB + ']' : '') + '</td>'); $('.genetics_maxSkills').html('<td class="first align-left bold" colspan="3">Max Skills (WOY/POC):</td><td colspan="1">' + maxSkills + (maxSkills < maxSkills_IB ? ' [' + maxSkills_IB + ']' : '') + '</td>'); }); } $('#genetics_weightings').change(showFormula); $('.genetics_resetParents').find('input').click(function() { resetParents(); }); showFormula(); }); } else abort(); $('#genetic-body-content').append('<style>.genetics_perc {color: blue; font-size: 0.85em;}</style>'); })(); }, true);