// /public/js/client.js document.addEventListener('DOMContentLoaded', () => { const socket = io({ // Опции Socket.IO, если нужны // transports: ['websocket'], // Можно попробовать для отладки, если есть проблемы с polling }); // --- Состояние клиента --- let currentGameState = null; let myPlayerId = null; // Технический ID слота в игре ('player' или 'opponent') let myUserId = null; // ID залогиненного пользователя (из БД) let myCharacterKey = null; let opponentCharacterKey = null; let currentGameId = null; let playerBaseStatsServer = null; let opponentBaseStatsServer = null; let playerAbilitiesServer = null; let opponentAbilitiesServer = null; let isLoggedIn = false; let loggedInUsername = ''; let isInGame = false; // --- DOM Элементы --- const authSection = document.getElementById('auth-section'); const registerForm = document.getElementById('register-form'); const loginForm = document.getElementById('login-form'); const authMessage = document.getElementById('auth-message'); const statusContainer = document.getElementById('status-container'); const userInfoDiv = document.getElementById('user-info'); const loggedInUsernameSpan = document.getElementById('logged-in-username'); const logoutButton = document.getElementById('logout-button'); const gameSetupDiv = document.getElementById('game-setup'); const createAIGameButton = document.getElementById('create-ai-game'); const createPvPGameButton = document.getElementById('create-pvp-game'); const joinPvPGameButton = document.getElementById('join-pvp-game'); // Убедитесь, что ID в HTML 'join-pvp-game' const findRandomPvPGameButton = document.getElementById('find-random-pvp-game'); const gameIdInput = document.getElementById('game-id-input'); const availableGamesDiv = document.getElementById('available-games-list'); const gameStatusMessage = document.getElementById('game-status-message'); const pvpCharacterRadios = document.querySelectorAll('input[name="pvp-character"]'); const gameWrapper = document.querySelector('.game-wrapper'); const attackButton = document.getElementById('button-attack'); const returnToMenuButton = document.getElementById('return-to-menu-button'); const gameOverScreen = document.getElementById('game-over-screen'); const abilitiesGrid = document.getElementById('abilities-grid'); const turnTimerSpan = document.getElementById('turn-timer'); const turnTimerContainer = document.getElementById('turn-timer-container'); // --- Функции управления UI --- function showAuthScreen() { authSection.style.display = 'block'; userInfoDiv.style.display = 'none'; gameSetupDiv.style.display = 'none'; gameWrapper.style.display = 'none'; hideGameOverModal(); setAuthMessage("Ожидание подключения к серверу..."); statusContainer.style.display = 'block'; isInGame = false; disableGameControls(); resetGameVariables(); if (turnTimerContainer) turnTimerContainer.style.display = 'none'; if (turnTimerSpan) turnTimerSpan.textContent = '--'; } function showGameSelectionScreen(username) { authSection.style.display = 'none'; userInfoDiv.style.display = 'block'; loggedInUsernameSpan.textContent = username; gameSetupDiv.style.display = 'block'; gameWrapper.style.display = 'none'; hideGameOverModal(); setGameStatusMessage("Выберите режим игры или присоединитесь к существующей."); statusContainer.style.display = 'block'; socket.emit('requestPvPGameList'); updateAvailableGamesList([]); // Очищаем перед запросом if (gameIdInput) gameIdInput.value = ''; const elenaRadio = document.getElementById('char-elena'); if (elenaRadio) elenaRadio.checked = true; isInGame = false; disableGameControls(); resetGameVariables(); // Сбрасываем игровые переменные при выходе в меню if (turnTimerContainer) turnTimerContainer.style.display = 'none'; if (turnTimerSpan) turnTimerSpan.textContent = '--'; enableSetupButtons(); // Включаем кнопки на экране выбора игры } function showGameScreen() { hideGameOverModal(); authSection.style.display = 'none'; userInfoDiv.style.display = 'block'; // Оставляем инфо о пользователе gameSetupDiv.style.display = 'none'; gameWrapper.style.display = 'flex'; setGameStatusMessage(""); // Очищаем статус, т.к. есть индикатор хода statusContainer.style.display = 'none'; // Скрываем общий статус контейнер isInGame = true; disableGameControls(); // Кнопки включатся, когда будет ход игрока if (turnTimerContainer) turnTimerContainer.style.display = 'block'; // Показываем таймер if (turnTimerSpan) turnTimerSpan.textContent = '--'; // Начальное значение } function resetGameVariables() { currentGameId = null; currentGameState = null; myPlayerId = null; myCharacterKey = null; opponentCharacterKey = null; playerBaseStatsServer = null; opponentBaseStatsServer = null; playerAbilitiesServer = null; opponentAbilitiesServer = null; window.gameState = null; window.gameData = null; window.myPlayerId = null; } function hideGameOverModal() { const hiddenClass = window.GAME_CONFIG?.CSS_CLASS_HIDDEN || 'hidden'; if (gameOverScreen && !gameOverScreen.classList.contains(hiddenClass)) { gameOverScreen.classList.add(hiddenClass); if (window.gameUI?.uiElements?.gameOver?.modalContent) { window.gameUI.uiElements.gameOver.modalContent.style.transform = 'scale(0.8) translateY(30px)'; window.gameUI.uiElements.gameOver.modalContent.style.opacity = '0'; } const opponentPanel = window.gameUI?.uiElements?.opponent?.panel; if (opponentPanel?.classList.contains('dissolving')) { opponentPanel.classList.remove('dissolving'); opponentPanel.style.opacity = '1'; opponentPanel.style.transform = 'scale(1) translateY(0)'; } } } function setAuthMessage(message, isError = false) { if (authMessage) { authMessage.textContent = message; authMessage.className = isError ? 'error' : 'success'; authMessage.style.display = message ? 'block' : 'none'; } if (message && gameStatusMessage) gameStatusMessage.style.display = 'none'; } function setGameStatusMessage(message, isError = false) { if (gameStatusMessage) { gameStatusMessage.textContent = message; gameStatusMessage.style.display = message ? 'block' : 'none'; gameStatusMessage.style.color = isError ? 'var(--damage-color, red)' : 'var(--turn-color, yellow)'; if (statusContainer) statusContainer.style.display = message ? 'block' : 'none'; } if (message && authMessage) authMessage.style.display = 'none'; } function getSelectedCharacterKey() { let selectedKey = 'elena'; if (pvpCharacterRadios) { pvpCharacterRadios.forEach(radio => { if (radio.checked) selectedKey = radio.value; }); } return selectedKey; } function enableGameControls(enableAttack = true, enableAbilities = true) { if (attackButton) attackButton.disabled = !enableAttack; if (abilitiesGrid) { const cls = window.GAME_CONFIG?.CSS_CLASS_ABILITY_BUTTON || 'ability-button'; abilitiesGrid.querySelectorAll(`.${cls}`).forEach(b => { b.disabled = !enableAbilities; }); } if (window.gameUI?.uiElements?.controls?.buttonBlock) window.gameUI.uiElements.controls.buttonBlock.disabled = true; } function disableGameControls() { enableGameControls(false, false); } function disableSetupButtons() { if(createAIGameButton) createAIGameButton.disabled = true; if(createPvPGameButton) createPvPGameButton.disabled = true; if(joinPvPGameButton) joinPvPGameButton.disabled = true; if(findRandomPvPGameButton) findRandomPvPGameButton.disabled = true; if(availableGamesDiv) availableGamesDiv.querySelectorAll('button').forEach(btn => btn.disabled = true); } function enableSetupButtons() { if(createAIGameButton) createAIGameButton.disabled = false; if(createPvPGameButton) createPvPGameButton.disabled = false; if(joinPvPGameButton) joinPvPGameButton.disabled = false; if(findRandomPvPGameButton) findRandomPvPGameButton.disabled = false; // Кнопки в списке игр включаются в updateAvailableGamesList } // --- Инициализация обработчиков событий --- if (registerForm) registerForm.addEventListener('submit', (e) => { e.preventDefault(); const u = document.getElementById('register-username').value; const p = document.getElementById('register-password').value; registerForm.querySelector('button').disabled = true; if(loginForm) loginForm.querySelector('button').disabled = true; socket.emit('register', { username: u, password: p }); }); if (loginForm) loginForm.addEventListener('submit', (e) => { e.preventDefault(); const u = document.getElementById('login-username').value; const p = document.getElementById('login-password').value; if(registerForm) registerForm.querySelector('button').disabled = true; loginForm.querySelector('button').disabled = true; socket.emit('login', { username: u, password: p }); }); if (logoutButton) logoutButton.addEventListener('click', () => { logoutButton.disabled = true; socket.emit('logout'); isLoggedIn = false; loggedInUsername = ''; myUserId = null; resetGameVariables(); isInGame = false; disableGameControls(); showAuthScreen(); setGameStatusMessage("Вы вышли из системы."); logoutButton.disabled = false; }); if (createAIGameButton) createAIGameButton.addEventListener('click', () => { if (!isLoggedIn) { setGameStatusMessage("Пожалуйста, войдите.", true); return; } disableSetupButtons(); socket.emit('createGame', { mode: 'ai', characterKey: 'elena' }); // AI всегда за Елену setGameStatusMessage("Создание игры против AI..."); }); if (createPvPGameButton) createPvPGameButton.addEventListener('click', () => { if (!isLoggedIn) { setGameStatusMessage("Пожалуйста, войдите.", true); return; } disableSetupButtons(); socket.emit('createGame', { mode: 'pvp', characterKey: getSelectedCharacterKey() }); setGameStatusMessage("Создание PvP игры..."); }); if (joinPvPGameButton) joinPvPGameButton.addEventListener('click', () => { // Убедитесь, что ID кнопки 'join-pvp-game' if (!isLoggedIn) { setGameStatusMessage("Пожалуйста, войдите.", true); return; } const gameId = gameIdInput.value.trim(); if (gameId) { disableSetupButtons(); socket.emit('joinGame', { gameId: gameId }); setGameStatusMessage(`Присоединение к игре ${gameId}...`); } else setGameStatusMessage("Введите ID игры.", true); }); if (findRandomPvPGameButton) findRandomPvPGameButton.addEventListener('click', () => { if (!isLoggedIn) { setGameStatusMessage("Пожалуйста, войдите.", true); return; } disableSetupButtons(); socket.emit('findRandomGame', { characterKey: getSelectedCharacterKey() }); setGameStatusMessage("Поиск случайной PvP игры..."); }); if (attackButton) attackButton.addEventListener('click', () => { if (isLoggedIn && isInGame && currentGameId && currentGameState && !currentGameState.isGameOver) { socket.emit('playerAction', { actionType: 'attack' }); } else { /* обработка ошибки/некорректного состояния */ } }); function handleAbilityButtonClick(event) { const abilityId = event.currentTarget.dataset.abilityId; if (isLoggedIn && isInGame && currentGameId && abilityId && currentGameState && !currentGameState.isGameOver) { socket.emit('playerAction', { actionType: 'ability', abilityId: abilityId }); } else { /* обработка ошибки/некорректного состояния */ } } if (returnToMenuButton) returnToMenuButton.addEventListener('click', () => { if (!isLoggedIn) { showAuthScreen(); return; } returnToMenuButton.disabled = true; resetGameVariables(); isInGame = false; disableGameControls(); hideGameOverModal(); showGameSelectionScreen(loggedInUsername); // Возвращаемся на экран выбора // Кнопка включится при следующем показе модалки }); function initializeAbilityButtons() { // ... (код без изменений, как был) if (!abilitiesGrid || !window.gameUI || !window.GAME_CONFIG) { if (abilitiesGrid) abilitiesGrid.innerHTML = '
Ошибка загрузки способностей.
'; return; } abilitiesGrid.innerHTML = ''; const config = window.GAME_CONFIG; const abilitiesToDisplay = playerAbilitiesServer; const baseStatsForResource = playerBaseStatsServer; if (!abilitiesToDisplay || abilitiesToDisplay.length === 0 || !baseStatsForResource) { abilitiesGrid.innerHTML = 'Нет доступных способностей.
'; return; } const resourceName = baseStatsForResource.resourceName || "Ресурс"; const abilityButtonClass = config.CSS_CLASS_ABILITY_BUTTON || 'ability-button'; abilitiesToDisplay.forEach(ability => { const button = document.createElement('button'); button.id = `ability-btn-${ability.id}`; button.classList.add(abilityButtonClass); button.dataset.abilityId = ability.id; let cooldown = ability.cooldown; let cooldownText = (typeof cooldown === 'number' && cooldown > 0) ? ` (КД: ${cooldown} х.)` : ""; let title = `${ability.name} (${ability.cost} ${resourceName})${cooldownText} - ${ability.description || 'Нет описания'}`; button.setAttribute('title', title); const nameSpan = document.createElement('span'); nameSpan.classList.add('ability-name'); nameSpan.textContent = ability.name; button.appendChild(nameSpan); const descSpan = document.createElement('span'); descSpan.classList.add('ability-desc'); descSpan.textContent = `(${ability.cost} ${resourceName})`; button.appendChild(descSpan); const cdDisplay = document.createElement('span'); cdDisplay.classList.add('ability-cooldown-display'); cdDisplay.style.display = 'none'; button.appendChild(cdDisplay); button.addEventListener('click', handleAbilityButtonClick); abilitiesGrid.appendChild(button); }); const placeholder = abilitiesGrid.querySelector('.placeholder-text'); if (placeholder) placeholder.remove(); } function updateAvailableGamesList(games) { if (!availableGamesDiv) return; availableGamesDiv.innerHTML = 'Нет доступных игр. Создайте свою!
'; } enableSetupButtons(); // Включаем основные кнопки создания/поиска } // --- Обработчики событий Socket.IO --- socket.on('connect', () => { console.log('[Client] Socket connected:', socket.id); if (isLoggedIn && myUserId) { // Проверяем и isLoggedIn и myUserId socket.emit('requestGameState'); // Запрашиваем состояние, если были залогинены } else { showAuthScreen(); // Иначе показываем экран логина } }); socket.on('registerResponse', (data) => { setAuthMessage(data.message, !data.success); if (data.success && registerForm) registerForm.reset(); if(registerForm) registerForm.querySelector('button').disabled = false; if(loginForm) loginForm.querySelector('button').disabled = false; }); socket.on('loginResponse', (data) => { setAuthMessage(data.message, !data.success); if (data.success) { isLoggedIn = true; loggedInUsername = data.username; myUserId = data.userId; // === ИЗМЕНЕНИЕ: Сохраняем ID пользователя === setAuthMessage(""); showGameSelectionScreen(data.username); } else { isLoggedIn = false; loggedInUsername = ''; myUserId = null; if(registerForm) registerForm.querySelector('button').disabled = false; if(loginForm) loginForm.querySelector('button').disabled = false; } }); socket.on('gameNotFound', (data) => { console.log('[Client] Game not found/ended:', data?.message); resetGameVariables(); isInGame = false; disableGameControls(); hideGameOverModal(); if (turnTimerContainer) turnTimerContainer.style.display = 'none'; if (turnTimerSpan) turnTimerSpan.textContent = '--'; if (isLoggedIn) { showGameSelectionScreen(loggedInUsername); setGameStatusMessage(data?.message || "Активная игровая сессия не найдена."); } else { showAuthScreen(); setAuthMessage(data?.message || "Пожалуйста, войдите."); } }); socket.on('disconnect', (reason) => { console.log('[Client] Disconnected:', reason); setGameStatusMessage(`Отключено: ${reason}. Обновите страницу.`, true); disableGameControls(); if (turnTimerSpan) turnTimerSpan.textContent = 'Откл.'; // Не сбрасываем isLoggedIn, чтобы при переподключении можно было восстановить сессию }); socket.on('gameCreated', (data) => { // Сервер присылает это после успешного createGame console.log('[Client] Game created by this client:', data); currentGameId = data.gameId; myPlayerId = data.yourPlayerId; // Сервер должен прислать роль создателя // Остальные данные (gameState, baseStats) придут с gameStarted или gameState (если это PvP ожидание) // Если это PvP и игра ожидает, сервер может прислать waitingForOpponent }); socket.on('gameStarted', (data) => { if (!isLoggedIn) return; console.log('[Client] Game started:', data); // ... (остальной код gameStarted без изменений, как был) if (window.gameUI?.uiElements?.opponent?.panel) { const opponentPanel = window.gameUI.uiElements.opponent.panel; if (opponentPanel.classList.contains('dissolving')) { opponentPanel.classList.remove('dissolving'); opponentPanel.style.opacity = '1'; opponentPanel.style.transform = 'scale(1) translateY(0)'; } } currentGameId = data.gameId; myPlayerId = data.yourPlayerId; currentGameState = data.initialGameState; playerBaseStatsServer = data.playerBaseStats; opponentBaseStatsServer = data.opponentBaseStats; playerAbilitiesServer = data.playerAbilities; opponentAbilitiesServer = data.opponentAbilities; myCharacterKey = playerBaseStatsServer?.characterKey; opponentCharacterKey = opponentBaseStatsServer?.characterKey; if (data.clientConfig) window.GAME_CONFIG = { ...data.clientConfig }; else if (!window.GAME_CONFIG) { window.GAME_CONFIG = { PLAYER_ID: 'player', OPPONENT_ID: 'opponent', CSS_CLASS_HIDDEN: 'hidden' }; } window.gameState = currentGameState; window.gameData = { playerBaseStats: playerBaseStatsServer, opponentBaseStats: opponentBaseStatsServer, playerAbilities: playerAbilitiesServer, opponentAbilities: opponentAbilitiesServer }; window.myPlayerId = myPlayerId; showGameScreen(); initializeAbilityButtons(); if (window.gameUI?.uiElements?.log?.list) window.gameUI.uiElements.log.list.innerHTML = ''; if (window.gameUI && typeof window.gameUI.addToLog === 'function' && data.log) { data.log.forEach(logEntry => window.gameUI.addToLog(logEntry.message, logEntry.type)); } requestAnimationFrame(() => { if (window.gameUI && typeof window.gameUI.updateUI === 'function') { window.gameUI.updateUI(); } }); hideGameOverModal(); setGameStatusMessage(""); }); // Используется для восстановления состояния уже идущей игры socket.on('gameState', (data) => { if (!isLoggedIn) return; console.log('[Client] Received full gameState (e.g. on reconnect):', data); // Это событие теперь может дублировать 'gameStarted' для переподключения. // Убедимся, что логика похожа на gameStarted. currentGameId = data.gameId; myPlayerId = data.yourPlayerId; currentGameState = data.gameState; // Используем gameState вместо initialGameState playerBaseStatsServer = data.playerBaseStats; opponentBaseStatsServer = data.opponentBaseStats; playerAbilitiesServer = data.playerAbilities; opponentAbilitiesServer = data.opponentAbilities; myCharacterKey = playerBaseStatsServer?.characterKey; opponentCharacterKey = opponentBaseStatsServer?.characterKey; if (data.clientConfig) window.GAME_CONFIG = { ...data.clientConfig }; else if (!window.GAME_CONFIG) { window.GAME_CONFIG = { PLAYER_ID: 'player', OPPONENT_ID: 'opponent', CSS_CLASS_HIDDEN: 'hidden' }; } window.gameState = currentGameState; window.gameData = { playerBaseStats: playerBaseStatsServer, opponentBaseStats: opponentBaseStatsServer, playerAbilities: playerAbilitiesServer, opponentAbilities: opponentAbilitiesServer }; window.myPlayerId = myPlayerId; if (!isInGame) showGameScreen(); // Показываем экран игры, если еще не там initializeAbilityButtons(); // Переинициализируем кнопки // Лог при 'gameState' может быть уже накопленным, добавляем его if (window.gameUI?.uiElements?.log?.list && data.log) { // Очищаем лог перед добавлением нового при полном обновлении window.gameUI.uiElements.log.list.innerHTML = ''; } if (window.gameUI && typeof window.gameUI.addToLog === 'function' && data.log) { data.log.forEach(logEntry => window.gameUI.addToLog(logEntry.message, logEntry.type)); } requestAnimationFrame(() => { if (window.gameUI && typeof window.gameUI.updateUI === 'function') { window.gameUI.updateUI(); } }); hideGameOverModal(); // Таймер будет обновлен следующим событием 'turnTimerUpdate' }); socket.on('gameStateUpdate', (data) => { if (!isLoggedIn || !isInGame || !currentGameId || !window.GAME_CONFIG) return; currentGameState = data.gameState; window.gameState = currentGameState; if (window.gameUI?.updateUI) window.gameUI.updateUI(); if (window.gameUI?.addToLog && data.log) { data.log.forEach(log => window.gameUI.addToLog(log.message, log.type)); } }); socket.on('logUpdate', (data) => { if (!isLoggedIn || !isInGame || !currentGameId || !window.GAME_CONFIG) return; if (window.gameUI?.addToLog && data.log) { data.log.forEach(log => window.gameUI.addToLog(log.message, log.type)); } }); socket.on('gameOver', (data) => { // ... (код без изменений, как был) if (!isLoggedIn || !currentGameId || !window.GAME_CONFIG) { if (!currentGameId && isLoggedIn) socket.emit('requestGameState'); else if (!isLoggedIn) showAuthScreen(); return; } const playerWon = data.winnerId === myPlayerId; currentGameState = data.finalGameState; window.gameState = currentGameState; if (window.gameUI?.updateUI) window.gameUI.updateUI(); if (window.gameUI?.addToLog && data.log) { data.log.forEach(log => window.gameUI.addToLog(log.message, log.type)); } if (window.gameUI?.showGameOver) { const oppKey = window.gameData?.opponentBaseStats?.characterKey; window.gameUI.showGameOver(playerWon, data.reason, oppKey, data); } if (returnToMenuButton) returnToMenuButton.disabled = false; setGameStatusMessage("Игра окончена. " + (playerWon ? "Вы победили!" : "Вы проиграли.")); if (window.gameUI?.updateTurnTimerDisplay) { // Обновляем UI таймера window.gameUI.updateTurnTimerDisplay(null, false, currentGameState?.gameMode); // Передаем null, чтобы показать "Конец" или скрыть } }); socket.on('waitingForOpponent', () => { if (!isLoggedIn) return; setGameStatusMessage("Ожидание присоединения оппонента..."); disableGameControls(); // Боевые кнопки неактивны disableSetupButtons(); // Кнопки создания/присоединения тоже, пока ждем if (createPvPGameButton) createPvPGameButton.disabled = false; // Оставляем активной "Создать PvP" для отмены if (window.gameUI?.updateTurnTimerDisplay) { window.gameUI.updateTurnTimerDisplay(null, false, 'pvp'); // Таймер неактивен } }); socket.on('opponentDisconnected', (data) => { if (!isLoggedIn || !isInGame || !currentGameId || !window.GAME_CONFIG) return; const name = data.disconnectedCharacterName || 'Противник'; if (window.gameUI?.addToLog) window.gameUI.addToLog(`🔌 Противник (${name}) отключился.`, 'system'); if (currentGameState && !currentGameState.isGameOver) { setGameStatusMessage(`Противник (${name}) отключился. Ожидание...`, true); disableGameControls(); } }); socket.on('gameError', (data) => { console.error('[Client] Server error:', data.message); if (isLoggedIn && isInGame && currentGameState && !currentGameState.isGameOver && window.gameUI?.addToLog) { window.gameUI.addToLog(`❌ Ошибка игры: ${data.message}`, 'system'); disableGameControls(); setGameStatusMessage(`Ошибка: ${data.message}.`, true); } else { setGameStatusMessage(`❌ Ошибка: ${data.message}`, true); if (isLoggedIn) enableSetupButtons(); // Если на экране выбора игры, включаем кнопки else { // Если на экране логина if(registerForm) registerForm.querySelector('button').disabled = false; if(loginForm) loginForm.querySelector('button').disabled = false; } } }); socket.on('availablePvPGamesList', (games) => { if (!isLoggedIn) return; updateAvailableGamesList(games); }); socket.on('noPendingGamesFound', (data) => { // Вызывается, когда создается новая игра после поиска if (!isLoggedIn) return; setGameStatusMessage(data.message || "Свободных игр не найдено. Создана новая для вас."); updateAvailableGamesList([]); // Очищаем список // currentGameId и myPlayerId должны были прийти с gameCreated isInGame = false; // Еще не в активной фазе боя disableGameControls(); disableSetupButtons(); // Мы в ожидающей игре if (window.gameUI?.updateTurnTimerDisplay) { window.gameUI.updateTurnTimerDisplay(null, false, 'pvp'); } }); socket.on('turnTimerUpdate', (data) => { if (!isInGame || !currentGameState || currentGameState.isGameOver) { if (window.gameUI?.updateTurnTimerDisplay && !currentGameState?.isGameOver) { // Только если не game over window.gameUI.updateTurnTimerDisplay(null, false, currentGameState?.gameMode); } return; } if (window.gameUI && typeof window.gameUI.updateTurnTimerDisplay === 'function') { // Определяем, является ли текущий ход ходом этого клиента const isMyActualTurn = myPlayerId && currentGameState.isPlayerTurn === (myPlayerId === GAME_CONFIG.PLAYER_ID); window.gameUI.updateTurnTimerDisplay(data.remainingTime, isMyActualTurn, currentGameState.gameMode); } }); showAuthScreen(); // Начальный экран });