NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Оповещалка
// @namespace http://tampermonkey.net/
// @version 2.3
// @description
// @author whynotsoido
// @match https://catwar.net/cw3/*
// @grant GM_notification
// @grant GM_setValue
// @grant GM_getValue
// @license MIT
// ==/UserScript==
(function() {
'use strict';
const STORAGE_KEY = 'catwar_monitoring_paused';
let currentPlayersOnField = new Set();
let isFirstRun = true;
let isJustResumedFromPause = false;
let isPaused = localStorage.getItem(STORAGE_KEY) === 'true';
function getPlayerId(element) {
const link = element.querySelector('a[href^="/cat"]');
if (link) {
const match = link.href.match(/\/cat(\d+)/);
return match ? match[1] : null;
}
return null;
}
function getPlayerName(element) {
const link = element.querySelector('a[href^="/cat"]');
return link ? link.textContent.trim() : 'Неизвестный игрок';
}
function getCurrentPlayers() {
const players = new Map();
const cageItems = document.querySelectorAll('#cages .cage .cage_items');
cageItems.forEach(item => {
const catElement = item.querySelector('.cat');
if (catElement) {
const playerId = getPlayerId(catElement);
const playerName = getPlayerName(catElement);
if (playerId && playerName) {
players.set(playerId, {
id: playerId,
name: playerName,
element: item
});
}
}
});
return players;
}
function playAlertSound() {
try {
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const oscillator1 = audioCtx.createOscillator();
const gainNode1 = audioCtx.createGain();
oscillator1.connect(gainNode1);
gainNode1.connect(audioCtx.destination);
oscillator1.frequency.value = 880;
oscillator1.type = 'sine';
gainNode1.gain.setValueAtTime(0.3, audioCtx.currentTime);
gainNode1.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.3);
oscillator1.start(audioCtx.currentTime);
oscillator1.stop(audioCtx.currentTime + 0.3);
setTimeout(() => {
try {
const oscillator2 = audioCtx.createOscillator();
const gainNode2 = audioCtx.createGain();
oscillator2.connect(gainNode2);
gainNode2.connect(audioCtx.destination);
oscillator2.frequency.value = 1108.73;
oscillator2.type = 'sine';
gainNode2.gain.setValueAtTime(0.3, audioCtx.currentTime);
gainNode2.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.4);
oscillator2.start(audioCtx.currentTime);
oscillator2.stop(audioCtx.currentTime + 0.4);
} catch(e) {
console.log('Второй звук не воспроизведен');
}
}, 200);
} catch(e) {
console.log('Ошибка воспроизведения звука:', e);
}
}
function checkNewPlayers(forceInit = false) {
const currentPlayers = getCurrentPlayers();
const currentIds = new Set(currentPlayers.keys());
if (forceInit || isFirstRun) {
currentPlayersOnField = currentIds;
isFirstRun = false;
isJustResumedFromPause = false;
console.log(`Инициализация: на поле ${currentPlayersOnField.size} игроков`);
return;
}
if (isJustResumedFromPause) {
currentPlayersOnField = currentIds;
isJustResumedFromPause = false;
console.log(`Снято с паузы: запомнено ${currentPlayersOnField.size} игроков на поле`);
return;
}
const newPlayers = [];
currentPlayers.forEach((player, id) => {
if (!currentPlayersOnField.has(id)) {
newPlayers.push(player);
}
});
if (newPlayers.length > 0) {
console.log(`Обнаружено ${newPlayers.length} новых игроков:`);
newPlayers.forEach(player => {
console.log(` - ${player.name} (ID: ${player.id})`);
});
}
newPlayers.forEach(player => {
playAlertSound();
try {
GM_notification({
title: '🎮 Новый игрок на поле!',
text: `${player.name} появился на локации!`,
timeout: 5000,
silent: true
});
} catch(e) {
console.log('Уведомление не поддерживается');
}
const cage = player.element.closest('.cage');
if (cage) {
const originalBorder = cage.style.border;
const originalBg = cage.style.backgroundColor;
cage.style.border = '3px solid #ff0000';
cage.style.backgroundColor = 'rgba(255, 0, 0, 0.2)';
cage.style.transition = 'all 0.3s';
setTimeout(() => {
cage.style.border = originalBorder || '';
cage.style.backgroundColor = originalBg || '';
}, 2000);
}
});
if (currentIds.size < currentPlayersOnField.size) {
const leftPlayers = [];
currentPlayersOnField.forEach(id => {
if (!currentIds.has(id)) {
leftPlayers.push(id);
}
});
if (leftPlayers.length > 0) {
console.log(`Игроки покинули поле: ${leftPlayers.length}`);
}
}
currentPlayersOnField = currentIds;
}
function resetMonitoring() {
isFirstRun = true;
isJustResumedFromPause = false;
currentPlayersOnField = new Set();
console.log('Сброс мониторинга');
}
let lastUrl = location.href;
function checkUrlChange() {
const currentUrl = location.href;
if (currentUrl !== lastUrl) {
console.log('🌍 Обнаружена смена локации');
lastUrl = currentUrl;
if (!isPaused) {
resetMonitoring();
setTimeout(() => {
checkNewPlayers(true);
}, 500);
} else {
resetMonitoring();
console.log('⏸ Мониторинг на паузе');
}
}
}
function createControlPanel() {
const panel = document.createElement('div');
panel.style.cssText = `
position: fixed;
bottom: 100px;
right: 10px;
z-index: 9999;
background: rgba(0, 0, 0, 0.85);
color: white;
padding: 12px 15px;
border-radius: 8px;
font-family: Arial, sans-serif;
font-size: 12px;
box-shadow: 0 4px 15px rgba(0,0,0,0.6);
min-width: 150px;
user-select: none;
`;
const title = document.createElement('div');
title.textContent = 'Мониторинг поля';
title.style.cssText = `
font-weight: bold;
font-size: 13px;
margin-bottom: 8px;
color: #4caf50;
`;
const statusText = document.createElement('div');
statusText.id = 'player-alert-status';
statusText.textContent = isPaused ? '⏸ На паузе' : 'Активен';
statusText.style.cssText = `
margin-bottom: 5px;
color: ${isPaused ? '#ff9800' : '#4caf50'};
font-size: 11px;
`;
const countText = document.createElement('div');
countText.id = 'player-count';
countText.textContent = `👥 Игроков: 0`;
countText.style.cssText = `
margin-bottom: 8px;
font-size: 11px;
color: #aaa;
`;
const btnContainer = document.createElement('div');
btnContainer.style.cssText = `
display: flex;
gap: 5px;
`;
const toggleBtn = document.createElement('button');
toggleBtn.id = 'toggle-monitoring-btn';
toggleBtn.textContent = isPaused ? '▶ Старт' : '⏸ Стоп';
toggleBtn.style.cssText = `
background: ${isPaused ? '#4caf50' : '#555'};
color: white;
border: none;
padding: 4px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 11px;
flex: 1;
transition: background 0.3s;
`;
toggleBtn.onclick = function() {
const wasPaused = isPaused;
isPaused = !isPaused;
localStorage.setItem(STORAGE_KEY, isPaused ? 'true' : 'false');
this.textContent = isPaused ? '▶ Запуск' : '⏸ Пауза';
this.style.background = isPaused ? '#4caf50' : '#555';
const statusEl = document.getElementById('player-alert-status');
if (statusEl) {
statusEl.textContent = isPaused ? '⏸ На паузе' : '✅ Активен';
statusEl.style.color = isPaused ? '#ff9800' : '#4caf50';
}
if (!isPaused && wasPaused) {
console.log('▶️ Запуск мониторинга после паузы');
isJustResumedFromPause = true;
setTimeout(() => {
checkNewPlayers(false);
}, 100);
} else if (isPaused) {
console.log('⏸ Мониторинг приостановлен');
}
};
const testBtn = document.createElement('button');
testBtn.textContent = '🔊';
testBtn.style.cssText = `
background: #555;
color: white;
border: none;
padding: 4px 8px;
border-radius: 4px;
cursor: pointer;
font-size: 11px;
`;
testBtn.title = 'Тест звука';
testBtn.onclick = function() {
playAlertSound();
};
const resetBtn = document.createElement('button');
resetBtn.textContent = '🔄';
resetBtn.style.cssText = `
background: #555;
color: white;
border: none;
padding: 4px 8px;
border-radius: 4px;
cursor: pointer;
font-size: 11px;
`;
resetBtn.title = 'Сбросить список (заново запомнить игроков)';
resetBtn.onclick = function() {
resetMonitoring();
if (!isPaused) {
setTimeout(() => {
checkNewPlayers(true);
}, 100);
} else {
console.log('⏸ Мониторинг на паузе, список сброшен');
}
};
btnContainer.appendChild(toggleBtn);
btnContainer.appendChild(testBtn);
btnContainer.appendChild(resetBtn);
panel.appendChild(title);
panel.appendChild(statusText);
panel.appendChild(countText);
panel.appendChild(btnContainer);
document.body.appendChild(panel);
setInterval(() => {
const countEl = document.getElementById('player-count');
if (countEl) {
countEl.textContent = `👥 Игроков: ${currentPlayersOnField.size}`;
}
}, 3000);
if (isPaused) {
console.log('⏸ Мониторинг на паузе (сохранено в localStorage)');
}
}
function startMonitoring() {
const checkTable = setInterval(() => {
const table = document.getElementById('cages');
if (table) {
clearInterval(checkTable);
console.log('✅ Игровое поле найдено, запускаем мониторинг...');
console.log(`📌 Состояние паузы: ${isPaused ? '⏸ Активна' : '✅ Выключена'}`);
setTimeout(() => {
const currentPlayers = getCurrentPlayers();
currentPlayersOnField = new Set(currentPlayers.keys());
isFirstRun = false;
console.log(`👥 Загружено ${currentPlayersOnField.size} игроков`);
createControlPanel();
setInterval(() => {
if (!isPaused) {
checkNewPlayers(false);
}
}, 2000);
setInterval(checkUrlChange, 1000);
}, 500);
}
}, 1000);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', startMonitoring);
} else {
startMonitoring();
}
console.log('🐱 CatWar New Player Alert v2.3 загружен!');
})();