bc/public/js/client.js
2025-05-09 12:11:07 +00:00

400 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// /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 = '<p class="placeholder-text">Нет доступных способностей.</p>';
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 = '<h3>Доступные PvP игры:</h3>';
if (games && games.length > 0) {
const ul = document.createElement('ul');
games.forEach(game => {
if (game) {
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;
joinBtn.addEventListener('click', (e) => {
const idToJoin = e.target.dataset.gameId;
socket.emit('joinGame', { gameId: idToJoin });
});
li.appendChild(joinBtn);
ul.appendChild(li);
}
});
availableGamesDiv.appendChild(ul);
} else {
availableGamesDiv.innerHTML += '<p>Нет доступных игр. Создайте свою!</p>';
}
}
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();
});