CatWar Word Counter

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

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

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

You will need to install an extension such as Tampermonkey to install this script.

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

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

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

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

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

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

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

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

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

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

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

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

// ==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);
})();