CatWar Word Counter

Счётчик слов для CatWar с исправлениями

目前為 2025-04-30 提交的版本,檢視 最新版本

// ==UserScript==
// @name         CatWar Word Counter
// @namespace    https://github.com/yourusername/catwar-word-counter
// @version      1.8
// @description  Счётчик слов для CatWar с исправлениями
// @author       bell
// @match        https://catwar.net/*
// @match        http://catwar.net/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const targetSpanId = 'ist';
    
    // Все фразы для поиска
    const wordsToFind = [
        'Обнаружил плотную водоросль',
        'Обнаружил мох',
        'Обнаружил крепкую ветку',
        'Обнаружил целебную водоросль',
        'Обнаружил Форель',
        'Обнаружил карася',
        'Обнаружил чёрного окуня',
        'Обнаружил белого амура',
        'Обнаружил толстолобика',
        'Обнаружил сазана',
        'Обнаружил небольшую ракушку',
        'Обнаружил большую ракушку',
        'Обнаружил небольшой камень',
        'Обнаружил камень',
        'Обнаружила плотную водоросль',
        'Обнаружила мох',
        'Обнаружила крепкую ветку',
        'Обнаружила целебную водоросль',
        'Обнаружила Форель',
        'Обнаружила карася',
        'Обнаружила чёрного окуня',
        'Обнаружила белого амура',
        'Обнаружила толстолобика',
        'Обнаружила сазана',
        'Обнаружила небольшую ракушку',
        'Обнаружила большую ракушку',
        'Обнаружила небольшой камень',
        'Обнаружила камень'
    ];

    // Группировка ресурсов по категориям
    const resourceCategories = {
        'Целебные ресурсы': [
            'Обнаружил плотную водоросль',
            'Обнаружил мох',
            'Обнаружил крепкую ветку',
            'Обнаружил целебную водоросль',
            'Обнаружила плотную водоросль',
            'Обнаружила мох',
            'Обнаружила крепкую ветку',
            'Обнаружила целебную водоросль'
        ],
        'Рыба': [
            'Обнаружил Форель',
            'Обнаружил карася',
            'Обнаружил чёрного окуня',
            'Обнаружил белого амура',
            'Обнаружил толстолобика',
            'Обнаружил сазана',
            'Обнаружила Форель',
            'Обнаружила карася',
            'Обнаружила чёрного окуня',
            'Обнаружила белого амура',
            'Обнаружила толстолобика',
            'Обнаружила сазана'
        ],
        'Ракушки': [
            'Обнаружил небольшую ракушку',
            'Обнаружил большую ракушку',
            'Обнаружила небольшую ракушку',
            'Обнаружила большую ракушку'
        ],
        'Камни': [
            'Обнаружил небольшой камень',
            'Обнаружил камень',
            'Обнаружила небольшой камень',
            'Обнаружила камень'
        ]
    };

    function detectPlayerGender(text) {
        if (/Обнаружил|Пошёл|Нырнул/.test(text)) return 'male';
        if (/Обнаружила|Пошла|Нырнула/.test(text)) return 'female';
        return null;
    }

    function countWords(text, words) {
        const counts = {};
        for (const word of words) {
            const regex = new RegExp(word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
            const matches = text.match(regex);
            counts[word] = matches ? matches.length : 0;
        }
        return counts;
    }

    function countGroupedResources(text) {
        const counts = {};
        
        for (const [category, phrases] of Object.entries(resourceCategories)) {
            counts[category] = 0;
            for (const phrase of phrases) {
                const regex = new RegExp(phrase.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
                const matches = text.match(regex);
                counts[category] += matches ? matches.length : 0;
            }
        }
        
        return counts;
    }

    function countDives(text, gender) {
        const maleDivesAll = (text.match(/Нырнул\./g) || []).length;
        const femaleDivesAll = (text.match(/Нырнула\./g) || []).length;
        
        const canceledMaleDives = (text.match(/Нырнул\. Отменил действие\./g) || []).length;
        const canceledFemaleDives = (text.match(/Нырнула\. Отменила действие\./g) || []).length;
        
        return gender === 'male' ? maleDivesAll - canceledMaleDives : femaleDivesAll - canceledFemaleDives;
    }

    function countLocationVisits(text, gender) {
        if (gender === 'male') {
            const maleRegex = /Пошёл в локацию («|")Дно цветущей заводи(»|")\. Нырнул\.(?! Отменил действие\.)/g;
            return (text.match(maleRegex) || []).length;
        } else if (gender === 'female') {
            const femaleRegex = /Пошла в локацию («|")Дно цветущей заводи(»|")\. Нырнула\.(?! Отменила действие\.)/g;
            return (text.match(femaleRegex) || []).length;
        }
        return 0;
    }

    function createTable(counts, visitCount, successfulDives, gender, groupedCounts) {
        const oldTable = document.getElementById('foundWordsTable');
        if (oldTable) oldTable.remove();

        const table = document.createElement('table');
        table.id = 'foundWordsTable';
        table.style.position = 'fixed';
        table.style.top = '10px';
        table.style.right = '10px';
        table.style.backgroundColor = 'rgba(255,255,255,0.9)';
        table.style.border = '1px solid #ccc';
        table.style.zIndex = '9999';
        table.style.padding = '8px';
        table.style.fontSize = '14px';
        table.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)';
        table.style.borderCollapse = 'collapse';
        table.style.fontFamily = 'Arial, sans-serif';

        // Заголовок таблицы
        const headerRow = table.insertRow();
        headerRow.style.backgroundColor = '#f0f0f0';
        const headerCell1 = headerRow.insertCell();
        const headerCell2 = headerRow.insertCell();
        headerCell1.textContent = 'Описание';
        headerCell2.textContent = 'Количество';
        headerCell1.style.padding = '5px 10px';
        headerCell2.style.padding = '5px 10px';

        // Количество заходов
        const visitRow = table.insertRow();
        const visitCell1 = visitRow.insertCell();
        const visitCell2 = visitRow.insertCell();
        visitCell1.textContent = 'Количество заходов';
        visitCell2.textContent = visitCount;
        visitCell1.style.padding = '5px 10px';
        visitCell2.style.padding = '5px 10px';

        // Успешные нырки
        const divesRow = table.insertRow();
        const divesCell1 = divesRow.insertCell();
        const divesCell2 = divesRow.insertCell();
        divesCell1.textContent = 'Успешные нырки';
        divesCell2.textContent = successfulDives;
        divesCell1.style.padding = '5px 10px';
        divesCell2.style.padding = '5px 10px';
        divesRow.style.fontWeight = 'bold';

        // Найденные предметы/рыбы (подробный список)
        const itemsHeader = table.insertRow();
        itemsHeader.style.backgroundColor = '#f0f0f0';
        const itemsCell = itemsHeader.insertCell();
        itemsCell.colSpan = 2;
        itemsCell.textContent = 'Найденные предметы/рыбы';
        itemsCell.style.padding = '5px 10px';
        itemsCell.style.textAlign = 'center';

        for (const [word, count] of Object.entries(counts)) {
            if (count > 0) {
                if ((gender === 'male' && word.startsWith('Обнаружил')) || 
                   (gender === 'female' && word.startsWith('Обнаружила'))) {
                    const row = table.insertRow();
                    const cell1 = row.insertCell();
                    const cell2 = row.insertCell();
                    cell1.textContent = word;
                    cell2.textContent = count;
                    cell1.style.padding = '5px 10px';
                    cell2.style.padding = '5px 10px';
                }
            }
        }

        // Разделительная черта перед группированными ресурсами
        const dividerRow = table.insertRow();
        const dividerCell = dividerRow.insertCell();
        dividerCell.colSpan = 2;
        dividerCell.style.padding = '5px 0';
        dividerCell.innerHTML = '<hr style="margin:5px 0;border:0;border-top:1px solid #ddd;">';

        // Группированные ресурсы
        const groupedHeader = table.insertRow();
        groupedHeader.style.backgroundColor = '#f0f0f0';
        const groupedHeaderCell = groupedHeader.insertCell();
        groupedHeaderCell.colSpan = 2;
        groupedHeaderCell.textContent = 'Ресурсы по категориям';
        groupedHeaderCell.style.padding = '5px 10px';
        groupedHeaderCell.style.textAlign = 'center';

        for (const [category, count] of Object.entries(groupedCounts)) {
            if (count > 0) {
                const row = table.insertRow();
                const cell1 = row.insertCell();
                const cell2 = row.insertCell();
                cell1.textContent = category;
                cell2.textContent = count;
                cell1.style.padding = '5px 10px';
                cell2.style.padding = '5px 10px';
                
                // Выделяем итоговые строки жирным
                if (category === 'Целебные ресурсы' || category === 'Рыба' || 
                    category === 'Ракушки' || category === 'Камни') {
                    row.style.fontWeight = 'bold';
                }
            }
        }

        document.body.appendChild(table);
        console.log('Таблица создана');
    }

    function processContent() {
        console.log('Начало обработки контента...');
        const span = document.getElementById(targetSpanId);
        
        if (!span) {
            console.warn('Элемент #ist не найден');
            return;
        }

        const text = span.textContent;
        if (!text.trim()) {
            console.warn('Элемент #ist пуст');
            return;
        }

        const gender = detectPlayerGender(text);
        console.log('Определён пол игрока:', gender);
        
        const counts = countWords(text, wordsToFind);
        const groupedCounts = countGroupedResources(text);
        const locationVisits = countLocationVisits(text, gender);
        const successfulDives = countDives(text, gender);

        if (Object.values(counts).some(count => count > 0) || locationVisits > 0 || successfulDives > 0) {
            createTable(counts, locationVisits, successfulDives, gender, groupedCounts);
        }
    }

    // MutationObserver
    const observer = new MutationObserver(function(mutations) {
        if (document.getElementById(targetSpanId)) {
            setTimeout(processContent, 300);
        }
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    // Первый запуск
    setTimeout(processContent, 1000);
})();