583 lines
32 KiB
JavaScript
583 lines
32 KiB
JavaScript
// /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 = '<p class="placeholder-text">Ошибка загрузки способностей.</p>';
|
||
return;
|
||
}
|
||
abilitiesGrid.innerHTML = '';
|
||
const config = window.GAME_CONFIG;
|
||
const abilitiesToDisplay = playerAbilitiesServer;
|
||
const baseStatsForResource = playerBaseStatsServer;
|
||
|
||
if (!abilitiesToDisplay || abilitiesToDisplay.length === 0 || !baseStatsForResource) {
|
||
abilitiesGrid.innerHTML = '<p class="placeholder-text">Нет доступных способностей.</p>';
|
||
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 = '<h3>Доступные PvP игры:</h3>';
|
||
if (games && games.length > 0) {
|
||
const ul = document.createElement('ul');
|
||
games.forEach(game => {
|
||
if (game && game.id) {
|
||
const li = document.createElement('li');
|
||
li.textContent = `ID: ${game.id.substring(0, 8)}... - ${game.status || 'Ожидает игрока'}`;
|
||
const joinBtn = document.createElement('button');
|
||
joinBtn.textContent = 'Присоединиться';
|
||
joinBtn.dataset.gameId = game.id;
|
||
|
||
// === ИЗМЕНЕНИЕ: Деактивация кнопки "Присоединиться" для своих игр ===
|
||
if (isLoggedIn && myUserId && game.ownerIdentifier === myUserId) {
|
||
joinBtn.disabled = true;
|
||
joinBtn.title = "Вы не можете присоединиться к своей же ожидающей игре.";
|
||
} else {
|
||
joinBtn.disabled = false;
|
||
}
|
||
// === КОНЕЦ ИЗМЕНЕНИЯ ===
|
||
|
||
joinBtn.addEventListener('click', (e) => {
|
||
if (!isLoggedIn) { setGameStatusMessage("Пожалуйста, войдите.", true); return; }
|
||
if (e.target.disabled) return; // Не обрабатывать клик по отключенной кнопке
|
||
disableSetupButtons();
|
||
socket.emit('joinGame', { gameId: e.target.dataset.gameId });
|
||
});
|
||
li.appendChild(joinBtn);
|
||
ul.appendChild(li);
|
||
}
|
||
});
|
||
availableGamesDiv.appendChild(ul);
|
||
} else {
|
||
availableGamesDiv.innerHTML += '<p>Нет доступных игр. Создайте свою!</p>';
|
||
}
|
||
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(); // Начальный экран
|
||
}); |