HWM smith and cost per battle

ЦЗБ текущего ремонта, данные с аукциона для сравнения

当前为 2025-02-13 提交的版本,查看 最新版本

// ==UserScript==
// @name         HWM smith and cost per battle
// @namespace    http://tampermonkey.net/
// @version      4.8
// @description  ЦЗБ текущего ремонта, данные с аукциона для сравнения
// @author       o3-mini-ChatGPT
// @match        https://www.heroeswm.ru/mod_workbench.php?art_id=*&type=repair
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Глобальные переменные
    let globalItemName = "";
    let repairRatio = 0;  // Соотношение стоимости ремонта (рассчитанное значение)
    let ratioSpan = null; // Элемент, в котором отображается ratio

    function runScript() {
        console.log("Запуск скрипта...");

        //-----------------------------
        // Извлечение имени предмета
        //-----------------------------
        // Ищем элемент, содержащий информацию о предмете
        let itemTd = document.querySelector('td.wb[align="center"][colspan="2"]');
        if (itemTd) {
            let itemText = itemTd.textContent.trim();
            // Формат: "Выбрано: ИМЯ [0/..."
            let parts = itemText.split(": ");
            if (parts.length > 1) {
                let itemName = parts[1].split(" [0/")[0];
                globalItemName = itemName; // сохраняем в глобальную переменную
                console.log("Имя предмета:", itemName);
            } else {
                console.error("Не удалось извлечь имя предмета из строки:", itemText);
            }
        } else {
            console.error("Элемент с информацией о предмете не найден!");
        }

        //-----------------------------
        // Часть 1: Расчёт соотношения стоимости ремонта
        //-----------------------------
        let form = document.querySelector("form[name='fmain']");
        if (!form) {
            console.error("Форма 'fmain' не найдена!");
            return;
        }

        // Извлекаем прочность ремонта (формат "11/13")
        let bTag = form.querySelector("b");
        if (!bTag) {
            console.error("Элемент с прочностью не найден!");
            return;
        }
        let durabilityText = bTag.textContent.trim();  // например "11/13"
        let durabilityParts = durabilityText.split('/');
        let currentDurability = parseFloat(durabilityParts[0]);
        if (isNaN(currentDurability)) {
            console.error("Не удалось извлечь текущее значение прочности из: '" + durabilityText + "'");
            return;
        }
        console.log("Текущая прочность:", currentDurability);

        // Извлекаем стоимость ремонта
        let costImg = form.querySelector("img[src*='gold.png']");
        if (!costImg) {
            console.error("Изображение стоимости ремонта не найдено!");
            return;
        }
        // Ищем текстовый узел после изображения (например, "2400" или "10469")
        let costTextNode = costImg.nextSibling;
        while (costTextNode && costTextNode.nodeType === Node.TEXT_NODE && !costTextNode.nodeValue.trim()) {
            costTextNode = costTextNode.nextSibling;
        }
        if (!costTextNode) {
            costTextNode = costImg.nextElementSibling;
        }
        if (!costTextNode) {
            console.error("Текстовый узел стоимости ремонта не найден!");
            return;
        }
        let costText = costTextNode.nodeValue ? costTextNode.nodeValue.trim() : costTextNode.textContent.trim();
        // Убираем разделитель тысяч (если есть)
        let repairCost = parseFloat(costText.replace(/,/g, ''));
        if (isNaN(repairCost)) {
            console.error("Не удалось извлечь стоимость ремонта из текста: '" + costText + "'");
            return;
        }
        console.log("Стоимость ремонта:", repairCost);

        // Вычисляем соотношение стоимости ремонта к прочности
        let ratio = repairCost / currentDurability;
        ratio = Math.round(ratio * 100) / 100;
        repairRatio = ratio; // сохраняем для дальнейшего сравнения
        console.log("Вычисленное соотношение:", ratio);

        // Выводим значение ratio рядом с прочностью.
        // Первоначально значение ЦЗБ выводится без выделения,
        // оно будет обновлено после вычисления аукционных данных.
        let resultSpan = document.createElement("span");
        resultSpan.style.marginLeft = "10px";
        resultSpan.style.fontWeight = "bold";
        resultSpan.textContent = " ЦЗБ: " + ratio;
        bTag.parentNode.insertBefore(resultSpan, bTag.nextSibling);
        ratioSpan = resultSpan; // сохраняем ссылку для обновления цвета
    }

    // Функция для получения и вывода ссылки на аукцион через iframe с использованием MutationObserver
    function loadAuctionInfo() {
        // Поиск ссылки на страницу с информацией о предмете (например: art_info.php?id=sword18)
        let artInfoAnchor = document.querySelector('a[href^="art_info.php?id="]');
        if (!artInfoAnchor) {
            console.error("Ссылка на информацию о предмете не найдена!");
            return;
        }
        let artInfoUrl = artInfoAnchor.href;
        // Приводим относительный адрес к абсолютному, если нужно
        artInfoUrl = new URL(artInfoUrl, window.location.origin).href;
        console.log("Переход на страницу с информацией о предмете:", artInfoUrl);

        // Создаем скрытый iframe для загрузки страницы информации о предмете
        let iframe = document.createElement("iframe");
        console.log("DEBUG: Создан iframe");
        iframe.style.display = "none";
        iframe.src = artInfoUrl;
        console.log("DEBUG: Установлен iframe.src =", artInfoUrl);
        iframe.onload = function() {
            console.log("DEBUG: iframe.onload сработал");
            try {
                let iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
                console.log("DEBUG: Получен документ iframe:", iframeDoc);
                // Используем MutationObserver для отслеживания появления ссылки на аукцион
                let observer = new MutationObserver(function(mutations, obs) {
                    console.log("DEBUG: MutationObserver сработал, число мутаций =", mutations.length);
                    let auctionLinkElem = iframeDoc.querySelector("a[href*='auction.php?']");
                    console.log("DEBUG: auctionLinkElem =", auctionLinkElem);
                    if (auctionLinkElem) {
                        // Извлекаем параметры из ссылки на аукцион
                        let urlParams = new URLSearchParams(auctionLinkElem.href.split('?')[1]);
                        let sortParam = urlParams.get('sort') || '4';
                        let catParam = urlParams.get('cat') || '';
                        let artTypeParam = urlParams.get('art_type') || '';
                        console.log("DEBUG: Извлечены параметры: sort =", sortParam, "| cat =", catParam, "| artType =", artTypeParam);

                        // Если параметры не найдены, пытаемся другую стратегию
                        if (!sortParam || !catParam || !artTypeParam) {
                            console.log("DEBUG: Основные параметры отсутствуют, пробуем извлечь из других ссылок");
                            // Попытка извлечения из других элементов
                            let links = iframeDoc.querySelectorAll("a");
                            links.forEach(link => {
                                if (link.href.indexOf("auction.php?") !== -1) {
                                    let paramsTemp = new URLSearchParams(link.href.split('?')[1]);
                                    sortParam = paramsTemp.get('sort') || sortParam;
                                    catParam = paramsTemp.get('cat') || catParam;
                                    artTypeParam = paramsTemp.get('art_type') || artTypeParam;
                                    console.log("DEBUG: Обновленные параметры после перебора ссылки: sort =", sortParam, "| cat =", catParam, "| artType =", artTypeParam);
                                }
                            });
                        }

                        if (!sortParam || !catParam || !artTypeParam) {
                            console.error("DEBUG: Не удалось извлечь все параметры для формирования ссылки на аукцион.");
                            obs.disconnect();
                            iframe.remove();
                            return;
                        }

                        // Формируем итоговую ссылку, добавляя параметры type=0 и snew=1
                        let finalAuctionLink = `https://www.heroeswm.ru/auction.php?sort=${sortParam}&cat=${catParam}&art_type=${artTypeParam}&sbn=1&sau=0&snew=1`;
                        console.log("DEBUG: Сформированная finalAuctionLink =", finalAuctionLink);
                        obs.disconnect();
                        iframe.remove();
                        // Загружаем страницу аукциона и ищем лоты, где в одном из элементов присутствует имя предмета
                        processAuctionPage(finalAuctionLink);
                    }
                });
                observer.observe(iframeDoc.body, { childList: true, subtree: true });
                console.log("DEBUG: MutationObserver установлен");
            } catch(e) {
                console.error("DEBUG: Ошибка в iframe.onload:", e);
                iframe.remove();
            }
        };
        document.body.appendChild(iframe);
        console.log("DEBUG: iframe добавлен в document.body");
    }

    // Функция для загрузки страницы аукциона, поиска лотов с нужным именем предмета,
    // извлечения значений прочности и цены, выбора минимальной цены для каждой прочности,
    // вычисления цены за единицу прочности и формирования гиперссылки с итоговой информацией.
    function processAuctionPage(finalAuctionLink) {
        console.log("Загрузка страницы аукциона:", finalAuctionLink);
        let auctionIframe = document.createElement("iframe");
        auctionIframe.style.display = "none";
        auctionIframe.src = finalAuctionLink;
        auctionIframe.onload = function() {
            try {
                let auctionDoc = auctionIframe.contentDocument || auctionIframe.contentWindow.document;
                console.log("Страница аукциона загружена. Ожидание динамического контента...");
                // Задержка 2 секунды для полной загрузки динамического контента
                setTimeout(() => {
                    // Находим все строки (элементы <tr> с классом "wb"), в которых встречается строка вида " - <itemName>"
                    let allRows = auctionDoc.querySelectorAll("tr.wb");
                    let lots = [];
                    allRows.forEach(row => {
                        if (row.innerText.indexOf("- " + globalItemName) !== -1) {
                            // Извлекаем прочность из ячейки с valign="top" (из строки "Прочность: NN/...")
                            let strengthCell = row.querySelector("td[valign='top']");
                            if (strengthCell) {
                                let strengthMatch = strengthCell.innerText.match(/Прочность:\s*(\d+)\//);
                                if (strengthMatch) {
                                    let strength = Number(strengthMatch[1]);
                                    // Извлекаем цену из ячейки, содержащей изображение золота
                                    let goldImg = row.querySelector("img[src*='gold.png']");
                                    if (goldImg && goldImg.parentElement) {
                                        let priceTd = goldImg.parentElement.nextElementSibling;
                                        if (priceTd) {
                                            // Извлекаем значение цены (например "13,000")
                                            let priceMatch = priceTd.textContent.match(/([\d,]+)/);
                                            if (priceMatch) {
                                                let priceStr = priceMatch[1].replace(/,/g, '');
                                                let price = Number(priceStr);
                                                lots.push({ strength, price });
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    });

                    console.log("Извлеченные лоты (прочность и цена):", lots);

                    // Если лотов нет, выходим
                    if (lots.length === 0) {
                        console.error("Лоты с предметом '" + globalItemName + "' не найдены.");
                        auctionIframe.remove();
                        return;
                    }

                    // Группируем по прочности (минимальная цена для каждой прочности)
                    let minPricesByStrength = {};
                    lots.forEach(lot => {
                        if (!minPricesByStrength[lot.strength] || lot.price < minPricesByStrength[lot.strength]) {
                            minPricesByStrength[lot.strength] = lot.price;
                        }
                    });
                    console.log("Минимальные цены для каждой прочности:", minPricesByStrength);

                    // Вычисляем цену за единицу прочности для каждого лота и ищем:
                    // - лот с минимальной прочностью
                    // - лот с максимальной прочностью
                    // - лот с наименьшей ценой за единицу прочности
                    let candidateMin = lots[0],
                        candidateMax = lots[0],
                        candidateBest = lots[0];
                    for (let lot of lots) {
                        if (lot.strength < candidateMin.strength) {
                            candidateMin = lot;
                        }
                        if (lot.strength > candidateMax.strength) {
                            candidateMax = lot;
                        }
                        if ((lot.price / lot.strength) < (candidateBest.price / candidateBest.strength)) {
                            candidateBest = lot;
                        }
                    }
                    let unitPriceMin = candidateMin.price / candidateMin.strength;
                    let unitPriceMax = candidateMax.price / candidateMax.strength;
                    let unitPriceBest = candidateBest.price / candidateBest.strength;

                    // Формируем строку вида:
                    // "Аук: <минимальная прочность> - <unitPrice_min>;
                    //       <максимальная прочность> - <unitPrice_max>;
                    //       <прочность лота с минимальной unitPrice> - <unitPrice_best (выделено жирным зелёным)>"
                    let linkHTML = "Аук: "
                        + candidateMin.strength + " - " + unitPriceMin.toFixed(2) + "; "
                        + candidateMax.strength + " - " + unitPriceMax.toFixed(2) + "; "
                        + candidateBest.strength + " - " + "<span style='color:green; font-weight:bold;'>" + unitPriceBest.toFixed(2) + "</span>";

                    console.log("Сформированная ссылка для аукциона:", linkHTML);

                    // Сравнение соотношения ремонта (repairRatio) с unitPriceBest и выделение его цветом:
                    // Если repairRatio меньше unitPriceBest - выделяем зелёным, иначе красным.
                    let ratioColor = (repairRatio < unitPriceBest) ? "green" : "red";
                    if (ratioSpan) {
                        ratioSpan.innerHTML = " ЦЗБ: <span style='color:" + ratioColor + "; font-weight:bold;'>" + repairRatio.toFixed(2) + "</span>";
                    }

                    // Создаем гиперссылку на аукцион с полученной строкой
                    let linkElement = document.createElement("a");
                    linkElement.href = finalAuctionLink;
                    linkElement.innerHTML = linkHTML;

                    console.log("Сформированная ссылка для аукциона:", linkElement.outerHTML);

                    // Обновляем раздел стоимости ремонта на странице (в форме fmain)
                    // Находим текстовый узел после изображения стоимости ремонта.
                    let formElem = document.querySelector("form[name='fmain']");
                    if (formElem) {
                        let goldImg = formElem.querySelector("img[src*='gold.png']");
                        if (goldImg) {
                            let candidateNode = goldImg.nextSibling;
                            while (candidateNode && candidateNode.nodeType === Node.TEXT_NODE && !candidateNode.nodeValue.trim()) {
                                candidateNode = candidateNode.nextSibling;
                            }
                            if (candidateNode) {
                                // Если сразу после текстового узла есть перенос строки, удаляем его.
                                let nextElem = candidateNode.nextElementSibling;
                                if (nextElem && nextElem.tagName.toLowerCase() === "br") {
                                    nextElem.remove();
                                }
                                // Ищем существующий span после текста стоимости ремонта.
                                let auctionSpan = candidateNode.nextElementSibling;
                                if (auctionSpan && auctionSpan.tagName.toLowerCase() === "span") {
                                    // Обновляем содержимое этого span, убираем лишние <br>.
                                    auctionSpan.innerHTML = linkElement.outerHTML;
                                    // Убираем лишние переносы внутри span, если они есть.
                                    let brs = auctionSpan.querySelectorAll("br");
                                    brs.forEach(br => br.remove());
                                    // Если за span не следует перенос строки, добавляем его.
                                    let afterSpan = auctionSpan.nextSibling;
                                    if (!(afterSpan && afterSpan.nodeType === Node.ELEMENT_NODE
                                          && afterSpan.tagName.toLowerCase() === "br")) {
                                        let br = document.createElement("br");
                                        auctionSpan.parentNode.insertBefore(br, auctionSpan.nextSibling);
                                    }
                                } else {
                                    // Если span отсутствует, создаем его со структурой:
                                    // [перенос строки] <span>ссылка</span> [перенос строки]
                                    let brBefore = document.createElement("br");
                                    let auctionContainer = document.createElement("span");
                                    auctionContainer.innerHTML = linkElement.outerHTML;
                                    let brAfter = document.createElement("br");
                                    candidateNode.parentNode.insertBefore(brBefore, candidateNode.nextSibling);
                                    candidateNode.parentNode.insertBefore(auctionContainer, brBefore.nextSibling);
                                    candidateNode.parentNode.insertBefore(brAfter, auctionContainer.nextSibling);
                                }
                            }
                        }
                    }
                    auctionIframe.remove();
                }, 2000);
            } catch (e) {
                console.error("Ошибка при обработке страницы аукциона:", e);
                auctionIframe.remove();
            }
        };
        document.body.appendChild(auctionIframe);
    }

    if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", function() {
            runScript();
            loadAuctionInfo();
        });
    } else {
        runScript();
        loadAuctionInfo();
    }
})();