296 lines
16 KiB
JavaScript
296 lines
16 KiB
JavaScript
// /public/js/main.js
|
||
|
||
import { initAuth } from './auth.js';
|
||
import { initGameSetup } from './gameSetup.js';
|
||
import { initGameplay } from './gameplay.js';
|
||
// Предполагаем, что ui.js загружен перед этим скриптом (в HTML)
|
||
// и создал глобальный объект window.gameUI
|
||
// Также ui.js будет читать window.gameState, window.gameData, window.myPlayerId, window.GAME_CONFIG
|
||
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
const socket = io({
|
||
// Опции Socket.IO, если нужны
|
||
});
|
||
|
||
// --- DOM Элементы для общего UI-управления ---
|
||
// (Эти элементы управляют общим потоком приложения, а не деталями боя)
|
||
const authSection = document.getElementById('auth-section');
|
||
const loginForm = document.getElementById('login-form');
|
||
const registerForm = document.getElementById('register-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'); // Для auth.js
|
||
|
||
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 pvpCharacterRadios = document.querySelectorAll('input[name="pvp-character"]'); // для gameSetup.js
|
||
|
||
const gameWrapper = document.querySelector('.game-wrapper');
|
||
// Элементы, связанные с gameOver, управляются через window.gameUI.showGameOver,
|
||
// но кнопка "Вернуться в меню" может быть здесь для общего сброса.
|
||
const returnToMenuButton = document.getElementById('return-to-menu-button');
|
||
const turnTimerContainer = document.getElementById('turn-timer-container');
|
||
const turnTimerSpan = document.getElementById('turn-timer');
|
||
|
||
|
||
// --- Состояние клиента (глобальное для main и передаваемое в модули) ---
|
||
// Это состояние будет модифицироваться из разных модулей
|
||
let clientState = {
|
||
isLoggedIn: false,
|
||
loggedInUsername: '',
|
||
myUserId: null,
|
||
isInGame: false,
|
||
// Игровые переменные, которые ранее были глобальными в client.js
|
||
// и от которых зависит ui.js
|
||
currentGameId: null,
|
||
currentGameState: null,
|
||
myPlayerId: null,
|
||
myCharacterKey: null,
|
||
opponentCharacterKey: null,
|
||
playerBaseStatsServer: null,
|
||
opponentBaseStatsServer: null,
|
||
playerAbilitiesServer: null,
|
||
opponentAbilitiesServer: null,
|
||
};
|
||
|
||
// Обновляем глобальные переменные window, на которые рассчитывает ui.js
|
||
// Это временная мера. В идеале, ui.js должен получать эти данные как аргументы функций.
|
||
function updateGlobalWindowVariablesForUI() {
|
||
window.gameState = clientState.currentGameState;
|
||
window.gameData = {
|
||
playerBaseStats: clientState.playerBaseStatsServer,
|
||
opponentBaseStats: clientState.opponentBaseStatsServer,
|
||
playerAbilities: clientState.playerAbilitiesServer,
|
||
opponentAbilities: clientState.opponentAbilitiesServer
|
||
};
|
||
window.myPlayerId = clientState.myPlayerId;
|
||
// window.GAME_CONFIG остается как есть, если он глобальный и не меняется часто
|
||
// Если GAME_CONFIG приходит от сервера, его тоже нужно обновлять здесь
|
||
// if (clientState.serverConfig) window.GAME_CONFIG = { ...clientState.serverConfig };
|
||
}
|
||
|
||
|
||
// --- Функции управления UI (для переключения основных экранов и общих сообщений) ---
|
||
function showAuthScreen() {
|
||
authSection.style.display = 'block';
|
||
userInfoDiv.style.display = 'none';
|
||
gameSetupDiv.style.display = 'none';
|
||
gameWrapper.style.display = 'none';
|
||
if (window.gameUI?.showGameOver) { // Скрываем модалку GameOver, если была
|
||
window.gameUI.showGameOver(false, "", null, { finalGameState: { isGameOver: false } });
|
||
}
|
||
setAuthMessage("Ожидание подключения к серверу...");
|
||
statusContainer.style.display = 'block';
|
||
clientState.isInGame = false;
|
||
// disableGameControls(); // Вызов будет из gameplay.js
|
||
resetGameVariables(); // Важно для сброса состояния
|
||
updateGlobalWindowVariablesForUI(); // Обновляем глоб. переменные для ui.js
|
||
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';
|
||
if (window.gameUI?.showGameOver) { // Скрываем модалку GameOver
|
||
window.gameUI.showGameOver(false, "", null, { finalGameState: { isGameOver: false } });
|
||
}
|
||
setGameStatusMessage("Выберите режим игры или присоединитесь к существующей.");
|
||
statusContainer.style.display = 'block';
|
||
socket.emit('requestPvPGameList'); // Запрашиваем список игр
|
||
if (availableGamesDiv) availableGamesDiv.innerHTML = '<h3>Доступные PvP игры:</h3><p>Загрузка...</p>'; // Очистка перед запросом
|
||
if (gameIdInput) gameIdInput.value = '';
|
||
|
||
const elenaRadio = document.getElementById('char-elena'); // Для сброса выбора персонажа
|
||
if (elenaRadio) elenaRadio.checked = true;
|
||
|
||
clientState.isInGame = false;
|
||
// disableGameControls(); // Вызов будет из gameplay.js
|
||
resetGameVariables();
|
||
updateGlobalWindowVariablesForUI();
|
||
if (turnTimerContainer) turnTimerContainer.style.display = 'none';
|
||
if (turnTimerSpan) turnTimerSpan.textContent = '--';
|
||
enableSetupButtons();
|
||
}
|
||
|
||
function showGameScreen() {
|
||
if (window.gameUI?.showGameOver) { // Скрываем модалку GameOver
|
||
window.gameUI.showGameOver(false, "", null, { finalGameState: { isGameOver: false } });
|
||
}
|
||
authSection.style.display = 'none';
|
||
userInfoDiv.style.display = 'block'; // Оставляем инфо о пользователе
|
||
gameSetupDiv.style.display = 'none';
|
||
gameWrapper.style.display = 'flex';
|
||
setGameStatusMessage(""); // Очищаем статус
|
||
statusContainer.style.display = 'none'; // Скрываем общий статус контейнер
|
||
clientState.isInGame = true;
|
||
// disableGameControls(); // Начальная деактивация, gameplay.js включит при ходе
|
||
updateGlobalWindowVariablesForUI(); // Убедимся, что ui.js имеет свежие данные
|
||
if (turnTimerContainer) turnTimerContainer.style.display = 'block';
|
||
if (turnTimerSpan) turnTimerSpan.textContent = '--';
|
||
}
|
||
|
||
function resetGameVariables() {
|
||
clientState.currentGameId = null;
|
||
clientState.currentGameState = null;
|
||
clientState.myPlayerId = null;
|
||
clientState.myCharacterKey = null;
|
||
clientState.opponentCharacterKey = null;
|
||
clientState.playerBaseStatsServer = null;
|
||
clientState.opponentBaseStatsServer = null;
|
||
clientState.playerAbilitiesServer = null;
|
||
clientState.opponentAbilitiesServer = null;
|
||
// Также обновляем глобальные переменные для ui.js
|
||
updateGlobalWindowVariablesForUI();
|
||
}
|
||
|
||
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'; // Скрываем другой статус
|
||
}
|
||
|
||
// Функции для управления кнопками на экране выбора игры (могут быть вызваны из gameSetup)
|
||
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 (в gameSetup.js)
|
||
}
|
||
|
||
// --- Сборка зависимостей для передачи в модули ---
|
||
const dependencies = {
|
||
socket,
|
||
clientState, // Объект состояния, который модули могут читать и изменять
|
||
ui: { // Функции и элементы для управления общим UI и состоянием
|
||
showAuthScreen,
|
||
showGameSelectionScreen,
|
||
showGameScreen,
|
||
setAuthMessage,
|
||
setGameStatusMessage,
|
||
resetGameVariables,
|
||
updateGlobalWindowVariablesForUI, // Важно для ui.js
|
||
disableSetupButtons,
|
||
enableSetupButtons,
|
||
elements: { // Передаем элементы, нужные для специфической логики модулей
|
||
// Для auth.js
|
||
loginForm,
|
||
registerForm,
|
||
logoutButton,
|
||
// Для gameSetup.js
|
||
createAIGameButton,
|
||
createPvPGameButton,
|
||
joinPvPGameButton,
|
||
findRandomPvPGameButton,
|
||
gameIdInput,
|
||
availableGamesDiv,
|
||
pvpCharacterRadios,
|
||
// Для gameplay.js (или для обработки gameover здесь)
|
||
returnToMenuButton,
|
||
}
|
||
},
|
||
// gameUI: window.gameUI // Можно передать, если модули должны напрямую вызывать gameUI.
|
||
// Но пока gameplay.js будет использовать глобальный window.gameUI
|
||
};
|
||
|
||
// Инициализация модулей
|
||
initAuth(dependencies);
|
||
initGameSetup(dependencies);
|
||
initGameplay(dependencies);
|
||
|
||
|
||
// --- Обработчики событий Socket.IO (глобальные для приложения) ---
|
||
socket.on('connect', () => {
|
||
console.log('[Client] Socket connected:', socket.id);
|
||
setAuthMessage("Успешно подключено к серверу. Вход...");
|
||
if (clientState.isLoggedIn && clientState.myUserId) {
|
||
// Пытаемся восстановить состояние игры, если были залогинены
|
||
socket.emit('requestGameState');
|
||
} else {
|
||
// Показываем экран логина, если не залогинены
|
||
showAuthScreen();
|
||
}
|
||
});
|
||
|
||
socket.on('disconnect', (reason) => {
|
||
console.warn('[Client] Disconnected:', reason);
|
||
setGameStatusMessage(`Отключено от сервера: ${reason}. Попытка переподключения...`, true);
|
||
// Здесь можно добавить логику для UI, показывающую состояние "отключено"
|
||
// disableGameControls(); // будет в gameplay
|
||
if (turnTimerSpan) turnTimerSpan.textContent = 'Откл.';
|
||
// Не сбрасываем isLoggedIn, чтобы при переподключении можно было восстановить сессию
|
||
});
|
||
|
||
// Общая обработка ошибок от сервера, если они не перехвачены в модулях
|
||
socket.on('gameError', (data) => {
|
||
console.error('[Client] Received gameError:', data.message);
|
||
// Показываем ошибку пользователю
|
||
if (clientState.isInGame && window.gameUI?.addToLog) {
|
||
window.gameUI.addToLog(`❌ Ошибка сервера: ${data.message}`, 'system');
|
||
// Здесь можно решить, нужно ли возвращать в меню или просто показать сообщение
|
||
} else if (clientState.isLoggedIn) {
|
||
setGameStatusMessage(`❌ Ошибка: ${data.message}`, true);
|
||
enableSetupButtons(); // Возвращаем активность кнопкам на экране выбора игры
|
||
} else {
|
||
setAuthMessage(`❌ Ошибка: ${data.message}`, true);
|
||
if(registerForm) registerForm.querySelector('button').disabled = false;
|
||
if(loginForm) loginForm.querySelector('button').disabled = false;
|
||
}
|
||
});
|
||
|
||
// Обработчик для gameNotFound, который может прийти при реконнекте, если игры нет
|
||
socket.on('gameNotFound', (data) => {
|
||
console.log('[Client] Main: Game not found/ended:', data?.message);
|
||
dependencies.ui.resetGameVariables(); // Сбрасываем игровые переменные
|
||
clientState.isInGame = false;
|
||
// disableGameControls(); // в gameplay
|
||
if (window.gameUI?.showGameOver) window.gameUI.showGameOver(false, "", null, { finalGameState: { isGameOver: false } }); // Скрыть модалку
|
||
if (turnTimerContainer) turnTimerContainer.style.display = 'none';
|
||
if (turnTimerSpan) turnTimerSpan.textContent = '--';
|
||
|
||
if (clientState.isLoggedIn) {
|
||
showGameSelectionScreen(clientState.loggedInUsername);
|
||
setGameStatusMessage(data?.message || "Активная игровая сессия не найдена.");
|
||
} else {
|
||
showAuthScreen();
|
||
setAuthMessage(data?.message || "Пожалуйста, войдите.");
|
||
}
|
||
});
|
||
|
||
|
||
// --- Инициализация UI ---
|
||
showAuthScreen(); // Показываем начальный экран аутентификации
|
||
}); |