// /public/js/client.js document.addEventListener('DOMContentLoaded', () => { const socket = io({ }); let currentGameState = null; let myPlayerId = null; let currentGameId = null; let playerBaseStatsServer = null; let opponentBaseStatsServer = null; let playerAbilitiesServer = null; let opponentAbilitiesServer = null; const attackButton = document.getElementById('button-attack'); const abilitiesGrid = document.getElementById('abilities-grid'); 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'); 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 gameWrapper = document.querySelector('.game-wrapper'); const restartGameButton = document.getElementById('restart-game-button'); const gameOverScreen = document.getElementById('game-over-screen'); function hideGameOverModal() { const hiddenClass = (window.GAME_CONFIG && window.GAME_CONFIG.CSS_CLASS_HIDDEN) ? window.GAME_CONFIG.CSS_CLASS_HIDDEN : 'hidden'; if (gameOverScreen && !gameOverScreen.classList.contains(hiddenClass)) { gameOverScreen.classList.add(hiddenClass); if (gameUI && gameUI.uiElements && gameUI.uiElements.gameOver && gameUI.uiElements.gameOver.modalContent) { gameUI.uiElements.gameOver.modalContent.style.transform = 'scale(0.8) translateY(30px)'; gameUI.uiElements.gameOver.modalContent.style.opacity = '0'; } } } function showGameSetupScreen() { hideGameOverModal(); if (gameWrapper) gameWrapper.style.display = 'none'; if (gameSetupDiv) gameSetupDiv.style.display = 'block'; setGameStatusMessage("Выберите режим игры или присоединитесь к существующей."); updateAvailableGamesList([]); if (gameIdInput) gameIdInput.value = ''; } function showGameScreen() { hideGameOverModal(); if (gameSetupDiv) gameSetupDiv.style.display = 'none'; if (gameWrapper) gameWrapper.style.display = 'flex'; setGameStatusMessage(""); } 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)'; } } function initializeAbilityButtons() { if (!gameUI || !gameUI.uiElements || !gameUI.uiElements.controls.abilitiesGrid || !window.GAME_CONFIG) { console.error("Cannot initialize abilities UI: missing gameUI elements or GAME_CONFIG."); return; } const grid = gameUI.uiElements.controls.abilitiesGrid; grid.innerHTML = ''; const config = window.GAME_CONFIG; const abilitiesToDisplay = (myPlayerId === config.PLAYER_ID) ? playerAbilitiesServer : opponentAbilitiesServer; const baseStatsForResource = (myPlayerId === config.PLAYER_ID) ? playerBaseStatsServer : opponentBaseStatsServer; const resourceName = baseStatsForResource ? baseStatsForResource.resourceName : "Ресурс"; if (!abilitiesToDisplay || abilitiesToDisplay.length === 0) { grid.innerHTML = '
Нет доступных способностей.
'; return; } abilitiesToDisplay.forEach(ability => { const button = document.createElement('button'); button.id = `ability-btn-${ability.id}`; button.classList.add(config.CSS_CLASS_ABILITY_BUTTON || 'ability-button'); button.dataset.abilityId = ability.id; let descriptionText = ability.description; if (typeof ability.descriptionFunction === 'function') { const targetStatsForDesc = (myPlayerId === config.PLAYER_ID) ? opponentBaseStatsServer : playerBaseStatsServer; descriptionText = ability.descriptionFunction(config, targetStatsForDesc); } let title = `${ability.name} (${ability.cost} ${resourceName}) - ${descriptionText}`; let cooldown = ability.cooldown; if (myPlayerId === config.OPPONENT_ID) { if (ability.internalCooldownFromConfig && config[ability.internalCooldownFromConfig]) { cooldown = config[ability.internalCooldownFromConfig]; } else if (ability.internalCooldownValue) { cooldown = ability.internalCooldownValue; } } if (cooldown) title += ` (КД: ${cooldown} х.)`; 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); grid.appendChild(button); }); } function updateAvailableGamesList(games) { if (!availableGamesDiv) return; availableGamesDiv.innerHTML = 'Нет доступных игр. Создайте свою!
'; } } if (attackButton) { attackButton.addEventListener('click', () => { if (currentGameId && currentGameState && !currentGameState.isGameOver) { socket.emit('playerAction', { actionType: 'attack' }); } }); } function handleAbilityButtonClick(event) { const button = event.currentTarget; const abilityId = button.dataset.abilityId; if (currentGameId && abilityId && currentGameState && !currentGameState.isGameOver) { socket.emit('playerAction', { actionType: 'ability', abilityId: abilityId }); } } if (restartGameButton) { restartGameButton.addEventListener('click', () => { console.log("[Client] Restart button clicked. currentGameId:", currentGameId, "currentGameState:", currentGameState); if (currentGameId && currentGameState && currentGameState.isGameOver) { console.log("[Client] Sending 'requestRestart' for game ID:", currentGameId); socket.emit('requestRestart', { gameId: currentGameId }); setGameStatusMessage("Запрос на рестарт отправлен..."); restartGameButton.disabled = true; } else { console.warn("[Client] Cannot request restart. Conditions not met:", { currentGameId, currentGameStateExists: !!currentGameState, isGameOver: currentGameState?.isGameOver } ); if (!currentGameId) { alert("Ошибка: ID текущей игры не определен. Невозможно запросить рестарт."); showGameSetupScreen(); } } }); } if (createAIGameButton) { createAIGameButton.addEventListener('click', () => { socket.emit('createGame', { mode: 'ai' }); setGameStatusMessage("Создание игры против AI..."); }); } if (createPvPGameButton) { createPvPGameButton.addEventListener('click', () => { socket.emit('createGame', { mode: 'pvp' }); setGameStatusMessage("Создание PvP игры..."); }); } if (joinPvPGameButton && gameIdInput) { joinPvPGameButton.addEventListener('click', () => { const gameIdToJoin = gameIdInput.value.trim(); if (gameIdToJoin) { console.log("[Client] Attempting to join game with ID:", gameIdToJoin); socket.emit('joinGame', { gameId: gameIdToJoin }); setGameStatusMessage(`Присоединение к игре ${gameIdToJoin}...`); } else { setGameStatusMessage("Пожалуйста, введите ID игры для присоединения.", true); } }); } if (findRandomPvPGameButton) { findRandomPvPGameButton.addEventListener('click', () => { socket.emit('findRandomPvPGame'); setGameStatusMessage("Поиск случайной PvP игры..."); }); } socket.on('connect', () => { console.log('Успешно подключено к серверу. ID сокета:', socket.id); showGameSetupScreen(); socket.emit('requestAvailablePvPGames'); }); socket.on('disconnect', (reason) => { console.log('Отключено от сервера:', reason); setGameStatusMessage(`Отключено от сервера: ${reason}. Попробуйте обновить страницу.`, true); currentGameState = null; currentGameId = null; myPlayerId = null; hideGameOverModal(); showGameSetupScreen(); }); socket.on('gameCreated', (data) => { currentGameId = data.gameId; myPlayerId = data.yourPlayerId; console.log(`Игра создана/присоединена: ${currentGameId}, Режим: ${data.mode}, Вы играете за: ${myPlayerId}`); if (data.clientConfig) { window.GAME_CONFIG = { ...(window.GAME_CONFIG || {}), ...data.clientConfig }; } const playerConfigId = (window.GAME_CONFIG && window.GAME_CONFIG.PLAYER_ID) ? window.GAME_CONFIG.PLAYER_ID : 'player'; if (data.mode === 'pvp') { if (gameIdInput) gameIdInput.value = currentGameId; if (myPlayerId === playerConfigId) { setGameStatusMessage(`PvP игра создана. ID для друга: ${currentGameId}. Ожидание второго игрока...`); } else { setGameStatusMessage(`Присоединились к PvP игре. Ожидание начала...`); } } else { setGameStatusMessage(`Игра против AI создана. Ожидание начала...`); } }); socket.on('gameStarted', (data) => { console.log('Событие "gameStarted" получено:', data); currentGameId = data.gameId; myPlayerId = data.yourPlayerId; currentGameState = data.initialGameState; playerBaseStatsServer = data.playerBaseStats; opponentBaseStatsServer = data.opponentBaseStats; playerAbilitiesServer = data.playerAbilities; opponentAbilitiesServer = data.opponentAbilities; if (data.clientConfig) { window.GAME_CONFIG = { ...data.clientConfig }; } else if (!window.GAME_CONFIG) { console.warn("Server did not send clientConfig in 'gameStarted'. UI might rely on defaults or fail."); 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 (gameUI && gameUI.uiElements && gameUI.uiElements.log && gameUI.uiElements.log.list) { gameUI.uiElements.log.list.innerHTML = ''; } if (gameUI && typeof gameUI.addToLog === 'function' && data.log) { data.log.forEach(logEntry => gameUI.addToLog(logEntry.message, logEntry.type)); } if (gameUI && typeof gameUI.updateUI === 'function') { gameUI.updateUI(); } hideGameOverModal(); if (restartGameButton) { restartGameButton.disabled = true; restartGameButton.dataset.gameIdForRestart = ''; } setGameStatusMessage(""); }); socket.on('gameStateUpdate', (data) => { currentGameState = data.gameState; window.gameState = currentGameState; if (gameUI && typeof gameUI.updateUI === 'function') { gameUI.updateUI(); } if (gameUI && typeof gameUI.addToLog === 'function' && data.log) { data.log.forEach(logEntry => gameUI.addToLog(logEntry.message, logEntry.type)); } }); socket.on('logUpdate', (data) => { if (gameUI && typeof gameUI.addToLog === 'function' && data.log) { data.log.forEach(logEntry => gameUI.addToLog(logEntry.message, logEntry.type)); } }); socket.on('gameOver', (data) => { console.log('Игра окончена:', data); currentGameState = data.finalGameState; window.gameState = currentGameState; if (gameUI && typeof gameUI.updateUI === 'function') { gameUI.updateUI(); } if (gameUI && typeof gameUI.addToLog === 'function' && data.log) { data.log.forEach(logEntry => gameUI.addToLog(logEntry.message, logEntry.type)); } if (gameUI && typeof gameUI.showGameOver === 'function') { const playerWon = data.winnerId === myPlayerId; gameUI.showGameOver(playerWon, data.reason); if (restartGameButton) { restartGameButton.disabled = false; restartGameButton.dataset.gameIdForRestart = currentGameId; } } setGameStatusMessage("Игра окончена. " + (data.winnerId === myPlayerId ? "Вы победили!" : "Вы проиграли.")); }); socket.on('waitingForOpponent', () => { setGameStatusMessage("Ожидание присоединения оппонента..."); }); socket.on('opponentDisconnected', (data) => { const systemLogType = (window.GAME_CONFIG && window.GAME_CONFIG.LOG_TYPE_SYSTEM) ? window.GAME_CONFIG.LOG_TYPE_SYSTEM : 'system'; if (gameUI && typeof gameUI.addToLog === 'function') { gameUI.addToLog("Противник отключился.", systemLogType); } if (!currentGameState || !currentGameState.isGameOver) { setGameStatusMessage("Противник отключился. Игра прервана. Вы можете начать новую.", true); } }); socket.on('turnNotification', (data) => { }); socket.on('waitingForRestartVote', (data) => { const systemLogType = (window.GAME_CONFIG && window.GAME_CONFIG.LOG_TYPE_SYSTEM) ? window.GAME_CONFIG.LOG_TYPE_SYSTEM : 'system'; if (gameUI && typeof gameUI.addToLog === 'function') { gameUI.addToLog( `${data.voterCharacterName || 'Игрок'} (${data.voterRole}) проголосовал(а) за рестарт. Нужно еще ${data.votesNeeded} голосов.`, systemLogType ); } setGameStatusMessage(`Один из игроков (${data.voterCharacterName}) проголосовал за рестарт. Ожидание вашего решения или решения другого игрока.`); if (restartGameButton && currentGameState && currentGameState.isGameOver) { restartGameButton.disabled = false; } }); socket.on('gameError', (data) => { console.error('Ошибка от сервера:', data.message); const systemLogType = (window.GAME_CONFIG && window.GAME_CONFIG.LOG_TYPE_SYSTEM) ? window.GAME_CONFIG.LOG_TYPE_SYSTEM : 'system'; if (gameUI && typeof gameUI.addToLog === 'function') { gameUI.addToLog(`Ошибка: ${data.message}`, systemLogType); } setGameStatusMessage(`Ошибка: ${data.message}`, true); }); socket.on('availablePvPGamesList', (games) => { updateAvailableGamesList(games); }); socket.on('noPendingGamesFound', (data) => { setGameStatusMessage(data.message || "Свободных игр не найдено. Создана новая для вас, ожидайте оппонента."); updateAvailableGamesList([]); }); showGameSetupScreen(); });