Catwar - Cell

Подсвечивает клетки в игровой с возможностью покраски цветом и созданием меток

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name        Catwar - Cell
// @namespace   http://tampermonkey.net/
// @version     1.9
// @description Подсвечивает клетки в игровой с возможностью покраски цветом и созданием меток
// @author      Кто-то
// @match       https://catwar.su/cw3/*
// @match       https://catwar.net/cw3/*
// @grant       GM_addStyle
// @grant       GM_setValue
// @grant       GM_getValue
// @license     MIT
// ==/UserScript==

(function() {
    'use strict';

    GM_addStyle(`
        .cw-highlight-ui {
            position: fixed;
            top: 20px;
            right: 20px;
            background: rgba(255, 255, 255, 0.95);
            padding: 15px;
            border-radius: 10px;
            box-shadow: 0 4px 15px rgba(0,0,0,0.2);
            z-index: 9999;
            font-family: 'Arial', sans-serif;
            min-width: 280px;
            max-width: 350px;
            border: 1px solid #ddd;
            transition: all 0.3s ease;
        }

        .cw-ui-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 15px;
            padding-bottom: 10px;
            border-bottom: 1px solid #eee;
            cursor: move;
        }

        .cw-ui-title {
            font-weight: bold;
            color: #333;
            font-size: 14px;
        }

        .cw-ui-toggle {
            background: none;
            border: none;
            font-size: 16px;
            cursor: pointer;
            color: #666;
            padding: 0;
            width: 20px;
            height: 20px;
        }

        .cw-ui-content {
            display: block;
        }

        .cw-ui-collapsed .cw-ui-content {
            display: none;
        }

        .location-info {
            margin-bottom: 15px;
            padding: 10px;
            background: linear-gradient(135deg, #FFB300 0%, #FF6F00 100%);
            color: white;
            border-radius: 6px;
            font-size: 12px;
            text-align: center;
        }

        .add-cell-form {
            margin-bottom: 15px;
            padding: 12px;
            background: #f8f9fa;
            border-radius: 6px;
            border: 1px solid #e9ecef;
        }

        .form-group {
            margin-bottom: 10px;
        }

        .form-group label {
            display: block;
            margin-bottom: 3px;
            font-size: 11px;
            color: #666;
            font-weight: bold;
        }

        .form-input {
            width: 100%;
            padding: 6px 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 12px;
            box-sizing: border-box;
        }

        .form-textarea {
            min-height: 40px;
            resize: vertical;
            white-space: pre-wrap;
        }

        .color-picker {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .color-input {
            width: 30px;
            height: 30px;
            padding: 0;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }

        .color-value {
            font-size: 11px;
            color: #666;
        }

        .btn {
            padding: 6px 12px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 12px;
            transition: background-color 0.2s;
        }

        .btn-primary {
            background: linear-gradient(135deg, #FFB300 0%, #FF6F00 100%);
            color: white;
        }

        .btn-secondary {
            background: #6c757d;
            color: white;
        }

        .btn-danger {
            background: #dc3545;
            color: white;
        }

        .btn:hover {
            opacity: 0.9;
        }

        .highlighted-cell {
            position: relative;
            cursor: pointer;
            transition: all 0.3s ease;
        }

        .cell-label {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(255, 255, 255, 0.95);
            color: #000;
            padding: 4px 8px;
            border-radius: 6px;
            font-size: 11px;
            font-weight: bold;
            z-index: 10000;
            pointer-events: none;
            border: 1px solid rgba(0, 0, 0, 0.2);
            box-shadow: 0 2px 6px rgba(0,0,0,0.2);
            text-align: center;
            line-height: 1.2;
            min-width: 50px;
            white-space: pre-wrap;
            word-wrap: break-word;
        }

        .settings-modal {
            display: none;
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: white;
            padding: 20px;
            border-radius: 10px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.3);
            z-index: 10001;
            min-width: 300px;
        }

        .modal-overlay {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0,0,0,0.5);
            z-index: 10000;
        }

        .import-export {
            margin-top: 15px;
            padding: 12px;
            background: #f8f9fa;
            border-radius: 6px;
            border: 1px solid #e9ecef;
        }

        .import-export-buttons {
            display: flex;
            gap: 8px;
            margin-top: 10px;
        }

        .import-textarea {
            min-height: 60px;
            resize: vertical;
            font-family: monospace;
            font-size: 11px;
        }

        .notification {
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            background: #28a745;
            color: white;
            padding: 10px 20px;
            border-radius: 5px;
            z-index: 10002;
            opacity: 0;
            transition: opacity 0.3s;
        }

        .notification.show {
            opacity: 1;
        }
    `);

    // Карта локаций и соответствующих клеток
    let locationCells = GM_getValue('locationCells', {
        'Локация 1': [],
        'Локация 2': []
    });

    let currentLocation = "";
    let highlightedCells = [];
    let isDragging = false;
    let dragOffset = { x: 0, y: 0 };
    let currentEditingCell = null;
    let uiElement = null;
    let cellClickHandlers = new Map(); // Хранилище обработчиков

    function createUI() {
        const ui = document.createElement('div');
        ui.className = 'cw-highlight-ui';
        ui.innerHTML = `
            <div class="cw-ui-header">
                <div class="cw-ui-title">Подсветка клеток</div>
                <button class="cw-ui-toggle">−</button>
            </div>
            <div class="cw-ui-content">
                <div class="location-info" id="cw-current-location">Локация: ${currentLocation || "не определено"}</div>
                <div class="add-cell-form">
                    <div class="form-group">
                        <label>Строка:</label>
                        <input type="number" class="form-input" id="cw-row-input" min="1" max="10">
                    </div>
                    <div class="form-group">
                        <label>Столбец:</label>
                        <input type="number" class="form-input" id="cw-col-input" min="1" max="10">
                    </div>
                    <div class="form-group">
                        <label>Текст (Shift+Enter для новой строки):</label>
                        <textarea class="form-input form-textarea" id="cw-text-input"></textarea>
                    </div>
                    <div class="form-group">
                        <label>Цвет подсветки:</label>
                        <div class="color-picker">
                            <input type="color" class="color-input" id="cw-color-input" value="#ffff00">
                            <span class="color-value" id="cw-color-value">#ffff00</span>
                        </div>
                    </div>
                    <button class="btn btn-primary" id="cw-add-btn">Добавить клетку</button>
                </div>
                <div class="import-export">
                    <div class="form-group">
                        <label>Импорт/экспорт карты:</label>
                        <textarea class="form-input import-textarea" id="cw-import-data" placeholder="Вставьте JSON данные для импорта"></textarea>
                    </div>
                    <div class="import-export-buttons">
                        <button class="btn btn-secondary" id="cw-copy-btn">Копировать карту</button>
                        <button class="btn btn-secondary" id="cw-import-btn">Импортировать</button>
                    </div>
                </div>
            </div>
        `;

        document.body.appendChild(ui);
        uiElement = ui; // Сохраняем ссылку на UI элемент
        createModal();
        createNotification();

        // Обработчики событий
        document.getElementById('cw-add-btn').addEventListener('click', addNewCell);
        document.getElementById('cw-color-input').addEventListener('input', updateColorValue);
        document.querySelector('.cw-ui-toggle').addEventListener('click', toggleUI);
        document.getElementById('cw-copy-btn').addEventListener('click', copyMapToClipboard);
        document.getElementById('cw-import-btn').addEventListener('click', importMapFromText);
        
        // Перетаскивание
        const header = ui.querySelector('.cw-ui-header');
        header.addEventListener('mousedown', startDrag);
    }

    function createNotification() {
        const notification = document.createElement('div');
        notification.className = 'notification';
        notification.id = 'cw-notification';
        document.body.appendChild(notification);
    }

    function showNotification(message, isSuccess = true) {
        const notification = document.getElementById('cw-notification');
        notification.textContent = message;
        notification.style.background = isSuccess ? '#28a745' : '#dc3545';
        notification.classList.add('show');
        
        setTimeout(() => {
            notification.classList.remove('show');
        }, 3000);
    }

    // Функция копирования карты в буфер обмена
    function copyMapToClipboard() {
        try {
            const mapData = JSON.stringify(locationCells, null, 2);
            navigator.clipboard.writeText(mapData).then(() => {
                showNotification('Карта скопирована в буфер обмена!');
            }).catch(err => {
                // Fallback для старых браузеров
                const textArea = document.createElement('textarea');
                textArea.value = mapData;
                document.body.appendChild(textArea);
                textArea.select();
                document.execCommand('copy');
                document.body.removeChild(textArea);
                showNotification('Карта скопирована в буфер обмена!');
            });
        } catch (error) {
            showNotification('Ошибка при копировании: ' + error.message, false);
        }
    }

    // Функция импорта карты из текста
    function importMapFromText() {
        const importText = document.getElementById('cw-import-data').value.trim();
        
        if (!importText) {
            showNotification('Введите данные для импорта', false);
            return;
        }

        try {
            const parsedData = JSON.parse(importText);
            
            // Проверяем структуру данных
            if (typeof parsedData !== 'object' || parsedData === null) {
                throw new Error('Неверный формат данных');
            }

            // Проверяем, что все значения - массивы
            for (const location in parsedData) {
                if (!Array.isArray(parsedData[location])) {
                    throw new Error(`Локация "${location}" должна содержать массив клеток`);
                }
                
                // Проверяем структуру каждой клетки
                for (const cell of parsedData[location]) {
                    if (!cell.row || !cell.col || !cell.label) {
                        throw new Error('Неверный формат клетки: должны быть row, col и label');
                    }
                }
            }

            // Сохраняем новые данные
            locationCells = parsedData;
            GM_setValue('locationCells', locationCells);
            
            // Обновляем интерфейс
            highlightCellsByLocation();
            document.getElementById('cw-import-data').value = '';
            
            showNotification('Карта успешно импортирована!');
            
        } catch (error) {
            showNotification('Ошибка импорта: ' + error.message, false);
        }
    }

    function createModal() {
        const modalHTML = `
            <div class="modal-overlay" id="cw-modal-overlay"></div>
            <div class="settings-modal" id="cw-settings-modal">
                <h3 style="margin-top: 0;">Редактирование клетки</h3>
                <div class="form-group">
                    <label>Текст:</label>
                    <textarea class="form-input form-textarea" id="cw-edit-text"></textarea>
                </div>
                <div class="form-group">
                    <label>Цвет:</label>
                    <div class="color-picker">
                        <input type="color" class="color-input" id="cw-edit-color">
                        <span class="color-value" id="cw-edit-color-value"></span>
                    </div>
                </div>
                <div style="display: flex; gap: 10px;">
                    <button class="btn btn-primary" id="cw-save-btn">Сохранить</button>
                    <button class="btn btn-danger" id="cw-delete-btn">Удалить</button>
                    <button class="btn" id="cw-cancel-btn">Отмена</button>
                </div>
            </div>
        `;
        document.body.insertAdjacentHTML('beforeend', modalHTML);

        // Обработчики модального окна
        document.getElementById('cw-save-btn').addEventListener('click', saveCellSettings);
        document.getElementById('cw-delete-btn').addEventListener('click', deleteCell);
        document.getElementById('cw-cancel-btn').addEventListener('click', closeModal);
        document.getElementById('cw-modal-overlay').addEventListener('click', closeModal);
        document.getElementById('cw-edit-color').addEventListener('input', function() {
            document.getElementById('cw-edit-color-value').textContent = this.value;
        });
    }

    function updateColorValue() {
        const colorInput = document.getElementById('cw-color-input');
        document.getElementById('cw-color-value').textContent = colorInput.value;
    }

    function toggleUI() {
        const ui = document.querySelector('.cw-highlight-ui');
        const toggleBtn = document.querySelector('.cw-ui-toggle');
        ui.classList.toggle('cw-ui-collapsed');
        toggleBtn.textContent = ui.classList.contains('cw-ui-collapsed') ? '+' : '−';
    }

    function startDrag(e) {
        if (e.target.closest('.cw-ui-toggle')) return;
        isDragging = true;
        const rect = uiElement.getBoundingClientRect();
        dragOffset.x = e.clientX - rect.left;
        dragOffset.y = e.clientY - rect.top;
        uiElement.style.transition = 'none';
        
        // Добавляем обработчики на документ
        document.addEventListener('mousemove', drag);
        document.addEventListener('mouseup', stopDrag);
    }

    function drag(e) {
        if (!isDragging) return;
        uiElement.style.left = (e.clientX - dragOffset.x) + 'px';
        uiElement.style.top = (e.clientY - dragOffset.y) + 'px';
        uiElement.style.right = 'auto';
    }

    function stopDrag() {
        isDragging = false;
        uiElement.style.transition = 'all 0.3s ease';
        
        // Убираем обработчики с документа
        document.removeEventListener('mousemove', drag);
        document.removeEventListener('mouseup', stopDrag);
    }

    function addNewCell() {
        const rowInput = document.getElementById('cw-row-input');
        const colInput = document.getElementById('cw-col-input');
        const textInput = document.getElementById('cw-text-input');
        
        const row = parseInt(rowInput.value);
        const col = parseInt(colInput.value);
        const text = textInput.value.trim();
        const color = document.getElementById('cw-color-input').value;

        if (isNaN(row) || isNaN(col) || !text) {
            showNotification('Заполните все поля!', false);
            return;
        }

        if (row < 1 || row > 10 || col < 1 || col > 10) {
            showNotification('Строка и столбец должны быть от 1 до 10!', false);
            return;
        }

        if (!currentLocation) {
            showNotification('Сначала определите локацию!', false);
            return;
        }

        if (!locationCells[currentLocation]) {
            locationCells[currentLocation] = [];
        }

        // Проверяем, существует ли уже клетка с такими координатами
        const existingIndex = locationCells[currentLocation].findIndex(cell => 
            cell.row === row && cell.col === col
        );

        const newCell = {
            row: row,
            col: col,
            label: text,
            color: color
        };

        if (existingIndex >= 0) {
            locationCells[currentLocation][existingIndex] = newCell;
            showNotification('Клетка обновлена!');
        } else {
            locationCells[currentLocation].push(newCell);
            showNotification('Клетка добавлена!');
        }

        GM_setValue('locationCells', locationCells);
        highlightCellsByLocation();

        // Очищаем поля ввода
        rowInput.value = '';
        colInput.value = '';
        textInput.value = '';
    }

    function openSettingsModal(cellInfo, cellElement) {
        currentEditingCell = { cellInfo, cellElement };
        
        document.getElementById('cw-edit-text').value = cellInfo.label;
        document.getElementById('cw-edit-color').value = cellInfo.color || '#ffff00';
        document.getElementById('cw-edit-color-value').textContent = cellInfo.color || '#ffff00';
        
        document.getElementById('cw-modal-overlay').style.display = 'block';
        document.getElementById('cw-settings-modal').style.display = 'block';
    }

    function closeModal() {
        document.getElementById('cw-modal-overlay').style.display = 'none';
        document.getElementById('cw-settings-modal').style.display = 'none';
        currentEditingCell = null;
    }

    function saveCellSettings() {
        if (!currentEditingCell) return;

        const text = document.getElementById('cw-edit-text').value;
        const color = document.getElementById('cw-edit-color').value;

        // Обновляем данные в storage
        const locationData = locationCells[currentLocation];
        const cellIndex = locationData.findIndex(cell => 
            cell.row === currentEditingCell.cellInfo.row && 
            cell.col === currentEditingCell.cellInfo.col
        );

        if (cellIndex >= 0) {
            locationData[cellIndex].label = text;
            locationData[cellIndex].color = color;
            GM_setValue('locationCells', locationCells);
            
            // Обновляем подсветку
            highlightCellsByLocation();
            showNotification('Клетка обновлена!');
        }

        closeModal();
    }

    function deleteCell() {
        if (!currentEditingCell) return;

        const locationData = locationCells[currentLocation];
        const cellIndex = locationData.findIndex(cell => 
            cell.row === currentEditingCell.cellInfo.row && 
            cell.col === currentEditingCell.cellInfo.col
        );

        if (cellIndex >= 0) {
            locationData.splice(cellIndex, 1);
            GM_setValue('locationCells', locationCells);
            
            // Обновляем подсветку
            highlightCellsByLocation();
            showNotification('Клетка удалена!');
        }

        closeModal();
    }

    // Поиск текущего Локациянахождения кота
    function findCurrentLocation() {
        // Ищем элемент с информацией о Локациянахождении
        const locationElement = document.querySelector('span#location');
        if (locationElement) {
            const newLocation = locationElement.textContent.trim();
            if (newLocation !== currentLocation) {
                currentLocation = newLocation;
                return true;
            }
            return false;
        }

        // Альтернативный поиск в истории
        const historyElement = document.querySelector('span#ist');
        if (historyElement && historyElement.textContent.includes('Моё местонахождение:')) {
            const locationMatch = historyElement.textContent.match(/Моё местонахождение:\s*([^<]+)/);
            if (locationMatch && locationMatch[1]) {
                const newLocation = locationMatch[1].trim();
                if (newLocation !== currentLocation) {
                    currentLocation = newLocation;
                    return true;
                }
            }
        }

        return false;
    }

    // Подсветка клеток в зависимости от Локацияположения
    function highlightCellsByLocation() {
        // Очищаем предыдущие подсветки
        clearHighlights();

        if (!currentLocation || !locationCells[currentLocation]) {
            console.log(`Для локации "${currentLocation}" нет настроенных клеток`);
            return;
        }

        const table = document.getElementById('cages');
        if (!table) {
            console.log('Игровое поле не найдено!');
            return;
        }

        const cellsToHighlight = locationCells[currentLocation];
        cellsToHighlight.forEach(cellInfo => {
            try {
                // Проверяем существование строки и ячейки
                if (cellInfo.row - 1 >= table.rows.length) return;
                const row = table.rows[cellInfo.row - 1];
                if (!row || cellInfo.col - 1 >= row.cells.length) return;
                
                const targetCell = row.cells[cellInfo.col - 1];
                if (!targetCell) return;

                // Подсвечиваем клетку
                targetCell.style.backgroundColor = cellInfo.color ? 
                    cellInfo.color + '80' : 'rgba(255, 255, 0, 0.5)';
                targetCell.style.boxShadow = `0 0 10px 5px ${cellInfo.color || 'yellow'}`;
                targetCell.classList.add('highlighted-cell');

                // Добавляем надпись
                const label = document.createElement('div');
                label.className = 'cell-label';
                label.textContent = cellInfo.label;
                targetCell.appendChild(label);

                // Создаем обработчик клика для редактирования
                const clickHandler = function(e) {
                    if (e.target.classList.contains('cell-label')) return;
                    openSettingsModal(cellInfo, targetCell);
                };

                // Добавляем обработчик и сохраняем ссылку на него
                targetCell.addEventListener('click', clickHandler);
                cellClickHandlers.set(targetCell, clickHandler);

                // Сохраняем ссылку на подсвеченную ячейку
                highlightedCells.push({
                    cell: targetCell,
                    label: label
                });

            } catch (e) {
                console.error('Ошибка при подсветке клетки:', e);
            }
        });
    }

    // Очистка всех подсветок
    function clearHighlights() {
        highlightedCells.forEach(highlighted => {
            if (highlighted.cell && highlighted.cell.parentNode) {
                highlighted.cell.style.backgroundColor = "";
                highlighted.cell.style.boxShadow = "";
                highlighted.cell.classList.remove('highlighted-cell');
                
                // Удаляем обработчик клика
                const clickHandler = cellClickHandlers.get(highlighted.cell);
                if (clickHandler) {
                    highlighted.cell.removeEventListener('click', clickHandler);
                    cellClickHandlers.delete(highlighted.cell);
                }
                
                // Удаляем надпись
                if (highlighted.label && highlighted.label.parentNode) {
                    highlighted.label.parentNode.removeChild(highlighted.label);
                }
            }
        });
        highlightedCells = [];
    }

    // Обновление информации о Локацияположении в интерфейсе
    function updateLocationInfo() {
        const locationInfo = document.getElementById('cw-current-location');
        if (locationInfo) {
            locationInfo.textContent = `Локация: ${currentLocation || "не определено"}`;
        }
    }

    // Основная функция инициализации
    function init() {
        console.log('Инициализация скрипта подсветки куч...');

        // Создаем интерфейс
        createUI();

        // Начальное определение Локацияположения
        findCurrentLocation();
        updateLocationInfo();
        highlightCellsByLocation();

        // Запускаем периодическую проверку Локацияположения
        setInterval(() => {
            const locationChanged = findCurrentLocation();
            if (locationChanged) {
                console.log(`Местонахождение изменилось на: ${currentLocation}`);
                updateLocationInfo();
                highlightCellsByLocation();
            }
        }, 500);
    }

    // Ждем загрузки страницы
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

})();