HeroesWM Clan Masters Info

Собирает информацию о мастерах и кузницах клана и выводит в виде таблиц во всплывающем окне

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         HeroesWM Clan Masters Info
// @namespace    http://tampermonkey.net/
// @version      1.37
// @description  Собирает информацию о мастерах и кузницах клана и выводит в виде таблиц во всплывающем окне
// @author       o3-mini-ChatGPT
// @match        https://www.heroeswm.ru/clan_info.php?id=*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';
    
    /**
     * @typedef {Object} PlayerResult
     * @property {string} id - Идентификатор игрока
     * @property {string} name - Имя игрока
     * @property {string} link - HTML-ссылка на страницу игрока
     * @property {number} guildKuznetsov - Значение "Гильдия Кузнецов"
     * @property {number} guildOrujejnikov - Значение "Гильдия Оружейников"
     * @property {number} masterOrujie - Значение "Мастер оружия"
     * @property {number} masterBronya - Значение "Мастер доспехов"
     * @property {number} jeweler - Значение "Ювелир"
     */
    
    // Создаём кнопку в левом верхнем углу для открытия анализа
    const button = document.createElement('button');
    button.innerText = 'Анализ мастеров';
    button.style.position = 'fixed';
    button.style.top = '10px';
    button.style.left = '10px';
    button.style.zIndex = '1000';
    button.style.padding = '5px';
    button.style.cursor = 'pointer';
    document.body.appendChild(button);
    
    // Функция для создания модального окна с содержимым
    function createModal(content) {
      const modal = document.createElement('div');
      modal.style.position = 'fixed';
      modal.style.top = '50%';
      modal.style.left = '50%';
      modal.style.transform = 'translate(-50%, -50%)';
      modal.style.backgroundColor = '#fff';
      modal.style.padding = '20px';
      modal.style.border = '1px solid #000';
      modal.style.boxShadow = '0 0 10px rgba(0,0,0,0.5)';
      modal.style.zIndex = '2000';
      modal.style.maxHeight = '80vh';
      modal.style.overflowY = 'auto';
      modal.innerHTML = content;
    
      const closeButton = document.createElement('button');
      closeButton.innerText = 'Закрыть';
      closeButton.style.marginTop = '10px';
      closeButton.onclick = () => {
        document.body.removeChild(modal);
      };
      modal.appendChild(closeButton);
    
      return modal;
    }
    
    // Обработчик нажатия на кнопку анализа
    button.onclick = async function() {
      // Создаем модальное окно с индикатором прогресса
      const modalDiv = document.createElement('div');
      modalDiv.style.position = 'fixed';
      modalDiv.style.top = '50%';
      modalDiv.style.left = '50%';
      modalDiv.style.transform = 'translate(-50%, -50%)';
      modalDiv.style.backgroundColor = '#fff';
      modalDiv.style.padding = '20px';
      modalDiv.style.border = '1px solid #000';
      modalDiv.style.boxShadow = '0 0 10px rgba(0,0,0,0.5)';
      modalDiv.style.zIndex = '2000';
      modalDiv.style.minWidth = '600px';
      modalDiv.innerHTML = '<h2>Сбор информации...</h2><div id="progress">0%</div>';
      document.body.appendChild(modalDiv);
    
      // Поиск игроков только в таблице с id "table-content"
      const tableContent = document.getElementById('table-content');
      if (!tableContent) {
        modalDiv.innerHTML = '<h2>Ошибка</h2><p>Не найден блок с игроками (table-content).</p>';
        return;
      }
      const playerLinkElements = tableContent.querySelectorAll('a.pi[href^="pl_info.php?id="]');
      if (playerLinkElements.length === 0) {
        modalDiv.innerHTML = '<h2>Ошибка</h2><p>Игроки не найдены в блоке table-content.</p>';
        return;
      }
    
      /** @type {PlayerResult[]} */
      const results = [];
      const playerElements = Array.from(playerLinkElements);
      console.log(`Найдено ${playerElements.length} игроков`);
    
      // Обрабатываем каждого игрока по очереди
      for (let i = 0; i < playerElements.length; i++) {
        const linkElem = playerElements[i];
        const href = linkElem.getAttribute('href');
        const id = href.split('=')[1];
        const name = linkElem.textContent.trim();
        const playerLink = linkElem.outerHTML;
    
        // Обновляем индикатор прогресса
        const progressElem = document.getElementById('progress');
        if (progressElem) {
          progressElem.textContent = `${Math.round(((i + 1) / playerElements.length) * 100)}% (${i + 1}/${playerElements.length})`;
        }
    
        try {
          // Загружаем страницу игрока через скрытый iframe для эмуляции поведения реального пользователя
          const iframe = document.createElement('iframe');
          iframe.style.display = 'none';
          document.body.appendChild(iframe);
          iframe.src = `https://www.heroeswm.ru/pl_info.php?id=${id}`;
          await new Promise(resolve => iframe.onload = resolve);
          const doc = iframe.contentDocument || iframe.contentWindow.document;
          document.body.removeChild(iframe);
          // Используем innerText для видимого текста и нормализуем его, заменяя неразрывные пробелы
          const normalizedBody = doc.body.innerText.replace(/\u00A0/g, ' ');
    
          // Извлекаем значение Гильдия Кузнецов из нормализованного текста страницы
          const guildKuznetsovMatch = normalizedBody.match(/Гильдия Кузнецов\s*:\s*(\d+)/i);
          const guildKuznetsov = guildKuznetsovMatch ? parseInt(guildKuznetsovMatch[1], 10) : 0;
    
          // Для Гильдии оружейников сначала пробуем получить данные из элемента с id "home_2"
          let guildOrujejnikov = 0;
          const home2 = doc.getElementById("home_2");
          if (home2) {
            const normalizedHome2 = home2.innerText.replace(/\u00A0/g, ' ');
            const match = normalizedHome2.match(/Гильдия Оружейников\s*:\s*(\d+)/i);
            guildOrujejnikov = match ? parseInt(match[1], 10) : 0;
          }
          // Если не найдено через home2, пробуем по всему тексту
          if (guildOrujejnikov === 0) {
            const extraMatch = normalizedBody.match(/Гильдия Оружейников\s*:\s*(\d+)/i);
            if (extraMatch) {
              guildOrujejnikov = parseInt(extraMatch[1], 10);
            }
          }
    
          // Извлекаем значения отделения гильдии оружейников (навыки) из нормализованного текста
          const masterOrujieMatch = normalizedBody.match(/Мастер оружия\s*:\s*(\d+)/i);
          const masterOrujie = masterOrujieMatch ? parseInt(masterOrujieMatch[1], 10) : 0;
    
          const masterBronyaMatch = normalizedBody.match(/Мастер доспехов\s*:\s*(\d+)/i);
          const masterBronya = masterBronyaMatch ? parseInt(masterBronyaMatch[1], 10) : 0;
    
          const jewelerMatch = normalizedBody.match(/Ювелир\s*:\s*(\d+)/i);
          const jeweler = jewelerMatch ? parseInt(jewelerMatch[1], 10) : 0;
    
          console.log(`Игрок ${name}: Кузница=${guildKuznetsov}, Оружейники=${guildOrujejnikov}, Мастер оружия=${masterOrujie}, Мастер доспехов=${masterBronya}, Ювелир=${jeweler}`);
    
          // Если у игрока не прокачан ни один стата в оружейных, пропускаем его
          if (masterOrujie === 0 && masterBronya === 0 && jeweler === 0 && guildKuznetsov === 0) {
            console.log(`Игрок ${name} пропущен, т.к. отсутствует прокачка статов в оружейных и кузницы.`);
            continue;
          }
    
          results.push({
            id,
            name,
            link: playerLink,
            guildKuznetsov,
            guildOrujejnikov,
            masterOrujie,
            masterBronya,
            jeweler
          });
    
          // Пауза между запросами
          await new Promise(resolve => setTimeout(resolve, 300));
        } catch (error) {
          console.error(`Ошибка при обработке игрока ${name}:`, error);
        }
      }
    
      // Если результатов нет, выводим сообщение
      if (results.length === 0) {
        document.body.removeChild(modalDiv);
        const noDataModal = createModal('<h2>Результаты</h2><p>Нет игроков с прокачанными статами в оружейных.</p>');
        document.body.appendChild(noDataModal);
        return;
      }
    
      // Формирование текстовой таблицы мастеров для удобного копирования
      let masterTableText = '|| Оружие || Броня || Ювелир || Персонаж\n\n';
      results.forEach(player => {
        if (!player.guildOrujejnikov) return; // пропускаем игроков с 0 гильдий
        const weaponValue = (player.guildOrujejnikov && player.masterOrujie) ? (Math.min(player.guildOrujejnikov + 1, 5) + '*' + String(Math.min(player.masterOrujie + 1, 12)).padStart(2, '0') + '%') : '- - - - -';
        const armorValue = (player.guildOrujejnikov && player.masterBronya) ? (Math.min(player.guildOrujejnikov + 1, 5) + '*' + String(Math.min(player.masterBronya + 1, 12)).padStart(2, '0') + '%') : '- - - - -';
        const jewelerValue = (player.guildOrujejnikov && player.jeweler) ? (Math.min(player.guildOrujejnikov + 1, 5) + '*' + String(Math.min(player.jeweler + 1, 12)).padStart(2, '0') + '%') : '- - - - -';
        masterTableText += `|| ${weaponValue} || ${armorValue} || ${jewelerValue} || ${player.name}\n`;
      });
      
      let smithTableText = '|| Кузница || Персонаж\n\n';
      results.forEach(player => {
        if (!(player.guildKuznetsov > 0)) return;
        const smithValue = (Math.min((player.guildKuznetsov + 1) * 10, 90)) + '%';
        smithTableText += `|| ${smithValue} || ${player.name}\n`;
      });

      const finalHTML = '<h2>Информация о мастерах клана</h2><textarea style="width:100%;height:200px;" readonly>' + masterTableText + '</textarea>' +
                         '<h2>Информация о кузницах клана</h2><textarea style="width:100%;height:200px;" readonly>' + smithTableText + '</textarea>';
      document.body.removeChild(modalDiv);
      const resultModal = createModal(finalHTML);
      document.body.appendChild(resultModal);
    };
    
  })();