Greasy Fork is available in English.
Оценка силы существ в расчёте на лидерство/стоимость
// ==UserScript== // @name HWM_UnitsAssessment // @namespace Небылица // @version 1.1 // @description Оценка силы существ в расчёте на лидерство/стоимость // @author Небылица // @include /^https{0,1}:\/\/((www|qrator)\.heroeswm\.ru|178\.248\.235\.15)\/(army_info|leader_army)\.php/ // @grant GM_setValue // @grant GM_getValue // ==/UserScript== (function() { "use strict"; // Настройки – вес стата const statWeight = 0.033; // // Вспомогательные функции Number.prototype.roundTo = function(digits){ // Oкругление числа до digits знаков после запятой return Math.round(this*(10**digits))/(10**digits); } function insertAfter(newNode, referenceNode){ // Вставка newNode после referenceNode referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); } function assess(){ // Расчёт и вывод показателей для текущего существа var params = document.querySelectorAll(".scroll_content_half > div"), paramsNumbers = (LGArmyPage) ? [0, 2, 4, 6, 10, 11] : [0, 2, 4, 6, 7, 9], // номера нужных граф на странице ГЛ и на странице существа attack = parseInt(params[paramsNumbers[0]].innerText), defence = parseInt(params[paramsNumbers[1]].innerText), damageArr = params[paramsNumbers[2]].innerText.split("-"), damageMin = parseInt(damageArr[0]), damageMax = parseInt(damageArr[1]), damageBonus = (!LGArmyPage && GM_getValue("BFEnabled")) ? 1 : 0, // учёт урона с ББ на странице отряда при наличии соотв. настройки damageAvg = (damageMin+damageMax)/2 + damageBonus, HPBonus = (!LGArmyPage && GM_getValue("vitalityEnabled")) ? 2 : 0, // учёт ХП со стойкостью на странице отряда при наличии соотв. настройки HP = parseInt(params[paramsNumbers[3]].innerText) + HPBonus, ini = parseInt(params[paramsNumbers[4]].innerText), leadershipText = params[paramsNumbers[5]].innerText.replace(",", "") || params[paramsNumbers[5]].firstChild.value, // лидерство без и с полем ввода своей цены leadership = parseInt(leadershipText) || "?", // 1000 – нормировочная константа для получения удобного для восприятия порядка величин damage = 1000*(damageAvg*(ini/10)*(1+statWeight*attack)), defenceWeight = (!LGArmyPage && GM_getValue("calculateWithoutDef")) ? 0 : statWeight, // расчёт без учёта дефа на странице отряда при наличии соотв. настройки vitality = 1000*(HP*(1+defenceWeight*defence)), damagePer = (damage/leadership).roundTo(2) || "?", vitalityPer = (vitality/leadership).roundTo(2) || "?", strength = Math.sqrt(damagePer*vitalityPer).roundTo(2) || "?", ECE = (100*(damage/vitality)).roundTo(2); damagePerInnerDiv.innerText = damagePer; vitalityPerInnerDiv.innerText = vitalityPer; strengthInnerDiv.innerText = strength; ECEInnerDiv.innerText = ECE; } function setupReassessment(){ // Привязка перерасчёта параметров к смене юнита на странице ГЛ var unitNameDiv = document.getElementById("unit_name"), observer = new MutationObserver(function(mutations){ mutations.forEach(function(mutation){ assess(); }); }), config = {characterData: false, attributes: false, childList: true, subtree: false}; observer.observe(unitNameDiv, config); } function setLeadershipTitle(){ // Замена "Лидерства" на "Стоимость" в зависимости от того, выбран ли ручной ввод leadershipTitle = (customPriceCheckbox.checked) ? "Стоимость" : "Лидерство"; leadershipTitleNode.nodeValue = leadershipTitle; } function setupLeadershipNumberElement(){ // Отрисовывает элемент со стоимостью стека и вешает событие по изменению кастомной цены в зависимости от сохранённой настройки if (GM_getValue("customPriceEnabled")){ leadershipNumber.innerHTML = "<input type='text' value='" + oldLeadership + "'>"; var leadershipNumberInput = leadershipNumber.firstChild; leadershipNumberInput.setAttribute("id", "unitsAssessmentLeadershipNumberInput"); leadershipNumberInput.style = "width: 50px;" + "height: 19px;" + "background-image: linear-gradient(rgb(166, 166, 166), rgb(163, 163, 163));" + "border: 0px;" + "text-align: right;" + "font-size: 15.6px;" + "font-weight: bold;" + "font-family: verdana, geneva, arial cyr;" + "color: #000000;"; // вешаем событие по изменению кастомной цены leadershipNumberInput.oninput = function(){ if (leadershipNumberInput.value.match(/^\d+$/)){ // если лидерство – число, то считаем показатели assess(); } }; } else{ leadershipNumber.innerHTML = oldLeadership; } } function imitateCheck(checkbox){ // Имитация нажатия на чекбокс checkbox.checked = !checkbox.checked; checkbox.dispatchEvent(changeEvent); } // const LGArmyPage = location.href.match("/leader_army"), emptyImageSrc = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAQMAAABtzGvEAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAANQTFRFAAAAp3o92gAAAAF0Uk5TAEDm2GYAAAANSURBVHicY2AYBdQEAAFQAAHr5ijJAAAAAElFTkSuQmCC"; var damagePerDiv = document.createElement("div"), vitalityPerDiv = document.createElement("div"), strengthDiv = document.createElement("div"), ECEDiv = document.createElement("div"), armyInfo = document.getElementsByClassName("info_text_content")[0]; damagePerDiv.setAttribute("class", "scroll_content_half"); vitalityPerDiv.setAttribute("class", "scroll_content_half"); strengthDiv.setAttribute("class", "scroll_content_half"); ECEDiv.setAttribute("class", "scroll_content_half"); var rightColPaddingLeft = (LGArmyPage) ? "0.45em" : "0.3em"; vitalityPerDiv.style.paddingLeft = rightColPaddingLeft; ECEDiv.style.paddingLeft = rightColPaddingLeft; damagePerDiv.innerHTML = "<img src='" + emptyImageSrc + "' width='48' height='48'>Дамажность<div id='unitsAssessmentDamagePerInnerDiv'></div>"; vitalityPerDiv.innerHTML = "<img src='" + emptyImageSrc + "' width='48' height='48'>Живучесть<div id='unitsAssessmentVitalityPerInnerDiv'></div>"; strengthDiv.innerHTML = "<img src='" + emptyImageSrc + "' width='48' height='48'>Сила<div id='unitsAssessmentStrengthInnerDiv'></div>"; ECEDiv.innerHTML = "<img src='" + emptyImageSrc + "' width='48' height='48'>КПД<div id='unitsAssessmentECEInnerDiv'></div>"; damagePerDiv.title = "Чем Дамажность больше, тем больше ДПС (дамага на время) приходится на единицу лидерства. " + "Учитываются урон, статы атаки и инициатива (как модификатор при расчёте ДПС, без учёта возможного эффекта от права первого удара). " + "Значение ориентировочное, абилки НЕ учитываются. Рассчитано с весом стата = " + statWeight + ". Совокупность Дамажности и Живучести составляет Силу. Отношение этих двух показателей – КПД."; vitalityPerDiv.title = "Чем Живучесть больше, тем больше мяса приходится на единицу лидерства. Учитываются здоровье и статы защиты. " + "Значение ориентировочное, абилки НЕ учитываются. Рассчитано с весом стата = " + statWeight + ". Совокупность Дамажности и Живучести составляет Силу. Отношение этих двух показателей – КПД."; strengthDiv.title = "Чем Сила больше, тем больше ДПС и мяса приходится на единицу лидерства (и тем лучше). " + "Учитываются урон, здоровье, статы атаки и защиты, инициатива (как модификатор при расчёте ДПС, без учёта возможного эффекта от права первого удара). " + "Значение ориентировочное, абилки НЕ учитываются. Рассчитано с весом стата = " + statWeight + ". Увеличение/уменьшение лидерства в n раз = синхронное уменьшение/увеличение и урона, и ХП в n раз = уменьшение/увеличение Силы в n раз. " + "При досчитывании показателя отдельно по 1 составляющей (для учёта абилок в каждом конкретном сценарии их реализации) " + "надо брать корень из модификатора разницы (урон вдвое выше – Сила в 1.41 раза выше)."; ECEDiv.title = "Сколько процентов составляет урон от мяса (актуально не только для ГЛ, лидерство не влияет). " + "Чем он больше, тем больше ДПС приходится на единицу ХП (юнит более дамажный и тонкий); чем меньше – тем более мясной. " + "Могут быть оптимальны различные показатели в зависимости от стиля игры. " + "В общем случае желательна близость всех боевых юнитов в наборе по КПД (за исключением коробочной связки). " + "Рассчитано с весом стата = " + statWeight + "."; armyInfo.appendChild(damagePerDiv); armyInfo.appendChild(vitalityPerDiv); armyInfo.appendChild(strengthDiv); armyInfo.appendChild(ECEDiv); var damagePerInnerDiv = document.getElementById("unitsAssessmentDamagePerInnerDiv"), vitalityPerInnerDiv = document.getElementById("unitsAssessmentVitalityPerInnerDiv"), strengthInnerDiv = document.getElementById("unitsAssessmentStrengthInnerDiv"), ECEInnerDiv = document.getElementById("unitsAssessmentECEInnerDiv"); // активируем расчёт в зависимости от страницы if (LGArmyPage){ setupReassessment(); } else{ assess(); // рисуем и вставляем чекбоксы с настройками var frameDiv = document.getElementsByClassName("army_info")[0], showArmyDiv = document.getElementById("show_army"), settingsDiv = document.createElement("div"); if (frameDiv.clientHeight < 410){frameDiv.style.height = "410px";} // растягиваем поле с параметрами при недостаточной высоте settingsDiv.setAttribute("id", "unitsAssessmentSettingsDiv"); settingsDiv.innerHTML = "<style>" + "#unitsAssessmentSettingsDiv{" + "width: 300px;" + "height: 40px;" + "margin-left: 656px;" + "font-size: 10pt;" + "font-weight: bold;" + "color: #272727;" + "}" + "#unitsAssessmentSettingsCustomPriceSpan, #unitsAssessmentSettingsCalculateWithoutDefSpan," + "#unitsAssessmentSettingsBFSpan, #unitsAssessmentSettingsVitalitySpan{" + "display: inline-block;" + "width: 48%;" + "margin: 2px 0px 0px 4px;" + "}" + "#unitsAssessmentSettingsCustomPriceTitle, #unitsAssessmentSettingsCalculateWithoutDefTitle," + "#unitsAssessmentSettingsBFTitle, #unitsAssessmentSettingsVitalityTitle{" + "display: inline-block;" + "width: 87%;" + "text-align: left;" + "}" + "#unitsAssessmentSettingsCustomPriceCheckboxSpan, #unitsAssessmentSettingsCalculateWithoutDefCheckboxSpan," + "#unitsAssessmentSettingsBFCheckboxSpan, #unitsAssessmentSettingsVitalityCheckboxSpan{" + "display: inline-block;" + "width: 13%;" + "text-align: right;" + "}" + "</style>" + "<span id='unitsAssessmentSettingsCustomPriceSpan'><span id='unitsAssessmentSettingsCustomPriceTitle'>Ручной ввод</span><span id='unitsAssessmentSettingsCustomPriceCheckboxSpan'><input type='checkbox' id='unitsAssessmentSettingsCustomPriceCheckbox'></span></span>" + "<span id='unitsAssessmentSettingsCalculateWithoutDefSpan'><span id='unitsAssessmentSettingsCalculateWithoutDefTitle'>Без учёта дефа</span><span id='unitsAssessmentSettingsCalculateWithoutDefCheckboxSpan'><input type='checkbox' id='unitsAssessmentSettingsCalculateWithoutDefCheckbox'></span></span><br>" + "<span id='unitsAssessmentSettingsBFSpan'><span id='unitsAssessmentSettingsBFTitle'>Боевое безумие</span><span id='unitsAssessmentSettingsBFCheckboxSpan'><input type='checkbox' id='unitsAssessmentSettingsBFCheckbox'></span></span>" + "<span id='unitsAssessmentSettingsVitalitySpan'><span id='unitsAssessmentSettingsVitalityTitle'>Стойкость</span><span id='unitsAssessmentSettingsVitalityCheckboxSpan'><input type='checkbox' id='unitsAssessmentSettingsVitalityCheckbox'></span></span>"; insertAfter(settingsDiv, showArmyDiv); // задаём переменные для созданных элементов var customPriceSpan = document.getElementById("unitsAssessmentSettingsCustomPriceSpan"), calculateWithoutDefSpan = document.getElementById("unitsAssessmentSettingsCalculateWithoutDefSpan"), BFSpan = document.getElementById("unitsAssessmentSettingsBFSpan"), vitalitySpan = document.getElementById("unitsAssessmentSettingsVitalitySpan"), customPriceTitle = document.getElementById("unitsAssessmentSettingsCustomPriceTitle"), calculateWithoutDefTitle = document.getElementById("unitsAssessmentSettingsCalculateWithoutDefTitle"), BFTitle = document.getElementById("unitsAssessmentSettingsBFTitle"), vitalityTitle = document.getElementById("unitsAssessmentSettingsVitalityTitle"), customPriceCheckbox = document.getElementById("unitsAssessmentSettingsCustomPriceCheckbox"), calculateWithoutDefCheckbox = document.getElementById("unitsAssessmentSettingsCalculateWithoutDefCheckbox"), BFCheckbox = document.getElementById("unitsAssessmentSettingsBFCheckbox"), vitalityCheckbox = document.getElementById("unitsAssessmentSettingsVitalityCheckbox"), RBCorner = document.querySelector(".corner_rb"); // и для уголка customPriceSpan.title = "Ручной ввод стоимости (для сравнения юнитов по текущей цене в ивенте, а не по лидерству в рамках ГЛ)."; calculateWithoutDefSpan.title = "Расчёт показателей с нулевым весом дефа (против хаоситов)."; BFSpan.title = "Расчёт показателей с учётом взятия Боевого безумия."; vitalitySpan.title = "Расчёт показателей с учётом взятия Стойкости."; RBCorner.title = vitalitySpan.title; // ставим галочки при наличии сохранённых положительных значений customPriceCheckbox.checked = GM_getValue("customPriceEnabled"); calculateWithoutDefCheckbox.checked = GM_getValue("calculateWithoutDef"); BFCheckbox.checked = GM_getValue("BFEnabled"); vitalityCheckbox.checked = GM_getValue("vitalityEnabled"); // сохраняем реальное лидерство var leadershipDiv = armyInfo.children[9], leadershipTitleNode = leadershipDiv.firstChild.nextSibling.nextSibling, leadershipTitle, leadershipNumber = leadershipDiv.children[1], oldLeadership = leadershipNumber.innerText.replace(",", ""); // выставляем подпись лидерству, отрисовываем поле ввода цены в соответствии с сохранёнными настройками setLeadershipTitle(); setupLeadershipNumberElement(); // вешаем события по изменению чекбоксов customPriceCheckbox.onchange = function(){ GM_setValue("customPriceEnabled", customPriceCheckbox.checked); setLeadershipTitle(); setupLeadershipNumberElement(); if (!customPriceCheckbox.checked){ // при снятии чекбокса пересчитываем показатели (уже по старому лидерству) assess(); } }; calculateWithoutDefCheckbox.onchange = function(){ GM_setValue("calculateWithoutDef", calculateWithoutDefCheckbox.checked); assess(); }; BFCheckbox.onchange = function(){ GM_setValue("BFEnabled", BFCheckbox.checked); assess(); }; vitalityCheckbox.onchange = function(){ GM_setValue("vitalityEnabled", vitalityCheckbox.checked); assess(); }; // имитируем нажатия на чекбоксы по клику на заголовках (для стойкости – и на НП уголке) var changeEvent = new Event("change"); customPriceTitle.onclick = function(){imitateCheck(customPriceCheckbox);}; calculateWithoutDefTitle.onclick = function(){imitateCheck(calculateWithoutDefCheckbox);}; BFTitle.onclick = function(){imitateCheck(BFCheckbox);}; vitalityTitle.onclick = function(){imitateCheck(vitalityCheckbox);}; RBCorner.onclick = function(){imitateCheck(vitalityCheckbox);}; } })();