From 0a627deac20ba2224f9c585ec229dda310845002 Mon Sep 17 00:00:00 2001 From: PsiMagistr Date: Thu, 29 May 2025 15:35:11 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20=D1=81=D0=B8=D1=82=D1=83=D0=B0=D1=86=D0=B8=D0=B9?= =?UTF-8?q?=20=D1=80=D0=B5=D0=BA=D0=BA=D0=BE=D0=BD=D0=B5=D0=BA=D1=82=D0=B0?= =?UTF-8?q?.=20=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B0=204.?= =?UTF-8?q?=20=D0=9A=D0=BB=D0=B8=D0=B5=D0=BD=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/js/gameplay.js | 221 +++++++++++++++++++++--------------------- 1 file changed, 113 insertions(+), 108 deletions(-) diff --git a/public/js/gameplay.js b/public/js/gameplay.js index 95110dd..6e3e812 100644 --- a/public/js/gameplay.js +++ b/public/js/gameplay.js @@ -2,19 +2,21 @@ export function initGameplay(dependencies) { const { socket, clientState, ui } = dependencies; - const { returnToMenuButton } = ui.elements; + const { returnToMenuButton } = ui.elements; // Предполагается, что это кнопка "Вернуться в меню" из модалки gameOver const attackButton = document.getElementById('button-attack'); const abilitiesGrid = document.getElementById('abilities-grid'); - // Инициализируем флаг в clientState, если он еще не существует (лучше делать в main.js) + // Инициализируем флаг в clientState, если он еще не существует (лучше делать в main.js при объявлении clientState) if (typeof clientState.isActionInProgress === 'undefined') { clientState.isActionInProgress = false; } // --- Вспомогательные функции --- function enableGameControls(enableAttack = true, enableAbilities = true) { - // console.log(`[GP] enableGameControls called. enableAttack: ${enableAttack}, enableAbilities: ${enableAbilities}, isActionInProgress: ${clientState.isActionInProgress}`); + const username = clientState.loggedInUsername || 'N/A'; + // console.log(`[CLIENT ${username} GP] enableGameControls called. enableAttack: ${enableAttack}, enableAbilities: ${enableAbilities}, isActionInProgress: ${clientState.isActionInProgress}`); + if (clientState.isActionInProgress) { if (attackButton) attackButton.disabled = true; if (abilitiesGrid) { @@ -22,8 +24,8 @@ export function initGameplay(dependencies) { const cls = config.CSS_CLASS_ABILITY_BUTTON || 'ability-button'; abilitiesGrid.querySelectorAll(`.${cls}`).forEach(b => { b.disabled = true; }); } - // console.log(`[GP] Action in progress, controls remain disabled.`); - if (window.gameUI?.updateUI) requestAnimationFrame(() => window.gameUI.updateUI()); + // console.log(`[CLIENT ${username} GP] Action is in progress, controls remain disabled by enableGameControls.`); + // Не вызываем updateUI здесь, чтобы не было рекурсии, если updateUI вызвал enableGameControls return; } @@ -33,37 +35,41 @@ export function initGameplay(dependencies) { const cls = config.CSS_CLASS_ABILITY_BUTTON || 'ability-button'; abilitiesGrid.querySelectorAll(`.${cls}`).forEach(b => { b.disabled = !enableAbilities; }); } - // console.log(`[GP] Controls set. Attack disabled: ${attackButton ? attackButton.disabled : 'N/A'}`); - if (window.gameUI?.updateUI) { - requestAnimationFrame(() => window.gameUI.updateUI()); // Обновляем UI, чтобы 반영 반영反映 изменения в disabled + // console.log(`[CLIENT ${username} GP] Controls set by enableGameControls. Attack disabled: ${attackButton ? attackButton.disabled : 'N/A'}`); + // Обновление UI для 반영反映 состояния disabled должно происходить в gameUI.updateUI() + // или здесь, если gameUI.updateUI() не покрывает это полностью. + if (window.gameUI?.updateUI && clientState.currentGameState && !clientState.currentGameState.isGameOver) { // Обновляем только если в активной игре + requestAnimationFrame(() => window.gameUI.updateUI()); } } function disableGameControls() { - // console.log(`[GP] disableGameControls called.`); + const username = clientState.loggedInUsername || 'N/A'; + // console.log(`[CLIENT ${username} GP] disableGameControls called.`); if (attackButton) attackButton.disabled = true; if (abilitiesGrid) { const config = window.GAME_CONFIG || {}; const cls = config.CSS_CLASS_ABILITY_BUTTON || 'ability-button'; abilitiesGrid.querySelectorAll(`.${cls}`).forEach(b => { b.disabled = true; }); } - if (window.gameUI?.updateUI) { - requestAnimationFrame(() => window.gameUI.updateUI()); // Обновляем UI, чтобы 반영 반영反映 изменения в disabled + // Обновление UI для 반영反映 состояния disabled + if (window.gameUI?.updateUI && clientState.currentGameState) { // Обновляем, если есть gameState (даже если gameOver) + requestAnimationFrame(() => window.gameUI.updateUI()); } } function initializeAbilityButtons() { if (!abilitiesGrid || !window.gameUI || !window.GAME_CONFIG) { - if (abilitiesGrid) abilitiesGrid.innerHTML = '

Ошибка загрузки способностей.

'; + if (abilitiesGrid) abilitiesGrid.innerHTML = '

Ошибка загрузки способностей (нет зависимостей).

'; return; } - abilitiesGrid.innerHTML = ''; + abilitiesGrid.innerHTML = ''; // Очищаем предыдущие кнопки const config = window.GAME_CONFIG; const abilitiesToDisplay = clientState.playerAbilitiesServer; const baseStatsForResource = clientState.playerBaseStatsServer; if (!abilitiesToDisplay || abilitiesToDisplay.length === 0 || !baseStatsForResource) { - abilitiesGrid.innerHTML = '

Нет доступных способностей.

'; + abilitiesGrid.innerHTML = '

Нет доступных способностей (данные отсутствуют).

'; return; } @@ -74,25 +80,25 @@ export function initGameplay(dependencies) { const button = document.createElement('button'); button.id = `ability-btn-${ability.id}`; button.classList.add(abilityButtonClass); - button.dataset.abilityId = ability.id; + button.dataset.abilityId = ability.id; // Сохраняем 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); + 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); + 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(); + if (placeholder) placeholder.remove(); // Удаляем плейсхолдер, если он был } function handleAbilityButtonClick(event) { const abilityId = event.currentTarget.dataset.abilityId; const username = clientState.loggedInUsername || 'N/A'; - console.log(`[CLIENT ${username}] handleAbilityButtonClick. AbilityID: ${abilityId}, isActionInProgress: ${clientState.isActionInProgress}`); + console.log(`[CLIENT ${username} GP] handleAbilityButtonClick. AbilityID: ${abilityId}, isActionInProgress: ${clientState.isActionInProgress}`); if (clientState.isLoggedIn && clientState.isInGame && @@ -100,14 +106,14 @@ export function initGameplay(dependencies) { abilityId && clientState.currentGameState && !clientState.currentGameState.isGameOver && - !clientState.isActionInProgress) { // <--- ПРОВЕРКА ФЛАГА + !clientState.isActionInProgress) { - console.log(`[CLIENT ${username}] Emitting playerAction (ability: ${abilityId}). Setting isActionInProgress = true.`); - clientState.isActionInProgress = true; // <--- УСТАНОВКА ФЛАГА - disableGameControls(); // <--- БЛОКИРОВКА СРАЗУ + console.log(`[CLIENT ${username} GP] Emitting playerAction (ability: ${abilityId}). Setting isActionInProgress = true.`); + clientState.isActionInProgress = true; + disableGameControls(); // Блокируем все контролы немедленно socket.emit('playerAction', { actionType: 'ability', abilityId: abilityId }); } else { - console.warn(`[CLIENT ${username}] Cannot perform ability action. Conditions not met or action in progress. InGame: ${clientState.isInGame}, GameOver: ${clientState.currentGameState?.isGameOver}, ActionInProgress: ${clientState.isActionInProgress}`); + console.warn(`[CLIENT ${username} GP] Cannot perform ability action. Conditions not met or action in progress. InGame: ${clientState.isInGame}, GameOver: ${clientState.currentGameState?.isGameOver}, ActionInProgress: ${clientState.isActionInProgress}, AbilityID: ${abilityId}`); } } @@ -115,36 +121,38 @@ export function initGameplay(dependencies) { if (attackButton) { attackButton.addEventListener('click', () => { const username = clientState.loggedInUsername || 'N/A'; - console.log(`[CLIENT ${username}] Attack button clicked. isActionInProgress: ${clientState.isActionInProgress}`); + console.log(`[CLIENT ${username} GP] Attack button clicked. isActionInProgress: ${clientState.isActionInProgress}`); if (clientState.isLoggedIn && clientState.isInGame && clientState.currentGameId && clientState.currentGameState && !clientState.currentGameState.isGameOver && - !clientState.isActionInProgress) { // <--- ПРОВЕРКА ФЛАГА + !clientState.isActionInProgress) { - console.log(`[CLIENT ${username}] Emitting playerAction (attack). Setting isActionInProgress = true.`); - clientState.isActionInProgress = true; // <--- УСТАНОВКА ФЛАГА - disableGameControls(); // <--- БЛОКИРОВКА СРАЗУ + console.log(`[CLIENT ${username} GP] Emitting playerAction (attack). Setting isActionInProgress = true.`); + clientState.isActionInProgress = true; + disableGameControls(); // Блокируем все контролы немедленно socket.emit('playerAction', { actionType: 'attack' }); } else { - console.warn(`[CLIENT ${username}] Cannot perform attack action. Conditions not met or action in progress. InGame: ${clientState.isInGame}, GameOver: ${clientState.currentGameState?.isGameOver}, ActionInProgress: ${clientState.isActionInProgress}`); + console.warn(`[CLIENT ${username} GP] Cannot perform attack action. Conditions not met or action in progress. InGame: ${clientState.isInGame}, GameOver: ${clientState.currentGameState?.isGameOver}, ActionInProgress: ${clientState.isActionInProgress}`); } }); } if (returnToMenuButton) { returnToMenuButton.addEventListener('click', () => { + const username = clientState.loggedInUsername || 'N/A'; + console.log(`[CLIENT ${username} GP] Return to menu button clicked.`); if (!clientState.isLoggedIn) { - ui.showAuthScreen(); + ui.showAuthScreen(); // Если не залогинен, на экран авторизации return; } - returnToMenuButton.disabled = true; // Блокируем сразу, чтобы избежать двойных кликов - clientState.isActionInProgress = false; // Сбрасываем на всякий случай, если покидаем игру + returnToMenuButton.disabled = true; + clientState.isActionInProgress = false; // Сбрасываем флаг при выходе в меню clientState.isInGame = false; - disableGameControls(); - ui.showGameSelectionScreen(clientState.loggedInUsername); + disableGameControls(); // Отключаем игровые контролы + ui.showGameSelectionScreen(clientState.loggedInUsername); // Показываем экран выбора игры }); } @@ -152,16 +160,16 @@ export function initGameplay(dependencies) { // --- ОБЩИЙ ОБРАБОТЧИК ДЛЯ ЗАПУСКА/ВОССТАНОВЛЕНИЯ ИГРЫ --- function handleGameDataReceived(data, eventName = "unknown") { if (!clientState.isLoggedIn) { - console.warn(`[CLIENT] handleGameDataReceived (${eventName}) called, but client not logged in. Ignoring.`); + console.warn(`[CLIENT GP] handleGameDataReceived (${eventName}) called, but client not logged in. Ignoring.`); return; } const username = clientState.loggedInUsername || 'N/A'; - console.log(`[CLIENT ${username}] handleGameDataReceived from event: ${eventName}. GameID: ${data.gameId}, YourPlayerID: ${data.yourPlayerId}, GS.isPlayerTurn: ${data.initialGameState?.isPlayerTurn || data.gameState?.isPlayerTurn}`); + console.log(`[CLIENT ${username} GP] handleGameDataReceived from event: ${eventName}. GameID: ${data.gameId}, YourPlayerID: ${data.yourPlayerId}, GS.isPlayerTurn: ${data.initialGameState?.isPlayerTurn || data.gameState?.isPlayerTurn}`); - clientState.isActionInProgress = false; // <--- СБРОС ФЛАГА при получении нового полного состояния + clientState.isActionInProgress = false; // Сброс флага при получении нового полного состояния игры clientState.currentGameId = data.gameId; - clientState.myPlayerId = data.yourPlayerId; + clientState.myPlayerId = data.yourPlayerId; // Роль игрока (player/opponent) clientState.currentGameState = data.initialGameState || data.gameState; clientState.playerBaseStatsServer = data.playerBaseStats; clientState.opponentBaseStatsServer = data.opponentBaseStats; @@ -173,28 +181,28 @@ export function initGameplay(dependencies) { if (clientState.currentGameState && !clientState.currentGameState.isGameOver) { clientState.isInGame = true; } else if (clientState.currentGameState && clientState.currentGameState.isGameOver) { - clientState.isInGame = false; + clientState.isInGame = false; // Если игра уже завершена, мы не в активной игре } if (data.clientConfig) { - window.GAME_CONFIG = { ...window.GAME_CONFIG, ...data.clientConfig }; - } else if (!window.GAME_CONFIG) { - window.GAME_CONFIG = { PLAYER_ID: 'player', OPPONENT_ID: 'opponent', CSS_CLASS_HIDDEN: 'hidden' }; // Базовый конфиг + window.GAME_CONFIG = { ...(window.GAME_CONFIG || {}), ...data.clientConfig }; + } else if (!window.GAME_CONFIG) { // Базовый конфиг, если не пришел с сервера + window.GAME_CONFIG = { PLAYER_ID: 'player', OPPONENT_ID: 'opponent', CSS_CLASS_HIDDEN: 'hidden', LOG_TYPE_SYSTEM: 'system' }; } - ui.updateGlobalWindowVariablesForUI(); + ui.updateGlobalWindowVariablesForUI(); // Обновляем глобальные переменные для ui.js const gameWrapperElement = document.querySelector('.game-wrapper'); if (clientState.isInGame && clientState.currentGameState && !clientState.currentGameState.isGameOver) { const isGameWrapperVisible = gameWrapperElement && (gameWrapperElement.style.display === 'flex' || getComputedStyle(gameWrapperElement).display === 'flex'); if (!isGameWrapperVisible) { - ui.showGameScreen(); + ui.showGameScreen(); // Показываем игровой экран, если он не был виден } } - initializeAbilityButtons(); + initializeAbilityButtons(); // Инициализируем кнопки способностей на основе полученных данных if (window.gameUI?.uiElements?.log?.list) { - window.gameUI.uiElements.log.list.innerHTML = ''; + window.gameUI.uiElements.log.list.innerHTML = ''; // Очищаем UI-лог перед добавлением новых } if (window.gameUI?.addToLog && data.log) { data.log.forEach(logEntry => { @@ -202,9 +210,10 @@ export function initGameplay(dependencies) { }); } + // Запрос на обновление UI и контролов requestAnimationFrame(() => { if (window.gameUI?.updateUI) { - window.gameUI.updateUI(); + window.gameUI.updateUI(); // Обновляем весь UI игры (панели игроков, эффекты и т.д.) } if (clientState.isInGame && clientState.currentGameState && !clientState.currentGameState.isGameOver && window.GAME_CONFIG) { const config = window.GAME_CONFIG; @@ -212,49 +221,53 @@ export function initGameplay(dependencies) { ((clientState.currentGameState.isPlayerTurn && clientState.myPlayerId === config.PLAYER_ID) || (!clientState.currentGameState.isPlayerTurn && clientState.myPlayerId === config.OPPONENT_ID)); - console.log(`[CLIENT ${username}] handleGameDataReceived - Determining controls. isMyActualTurn: ${isMyActualTurn}`); + console.log(`[CLIENT ${username} GP] handleGameDataReceived - Determining controls. isMyActualTurn: ${isMyActualTurn}`); if (isMyActualTurn) { enableGameControls(); } else { disableGameControls(); } } else if (clientState.currentGameState && clientState.currentGameState.isGameOver) { - console.log(`[CLIENT ${username}] handleGameDataReceived - Game is over, disabling controls.`); + console.log(`[CLIENT ${username} GP] handleGameDataReceived - Game is over, disabling controls.`); disableGameControls(); } }); + // Управление gameStatusMessage if (clientState.currentGameState && clientState.currentGameState.isGameOver) { - // Обработка gameOver уже есть в своем обработчике + // gameOver имеет свой обработчик статуса } else if (eventName === 'gameStarted' || eventName === 'gameState (reconnect)') { - console.log(`[CLIENT ${username}] ${eventName} - Clearing game status message because it's a fresh game/state load.`); + console.log(`[CLIENT ${username} GP] ${eventName} - Clearing game status message for fresh game/state load.`); ui.setGameStatusMessage(""); } else { if (clientState.isInGame) { - // Если это просто gameStateUpdate, и игра активна, убедимся, что нет сообщения об ожидании const statusMsgElement = document.getElementById('game-status-message'); const currentStatusText = statusMsgElement ? statusMsgElement.textContent : ""; - if (!currentStatusText.toLowerCase().includes("отключился")) { // Не стираем сообщение об отключении оппонента + if (!currentStatusText.toLowerCase().includes("отключился")) { ui.setGameStatusMessage(""); } } } + // Если игра пришла завершенной (даже в gameStarted), вызываем showGameOver if (clientState.currentGameState && clientState.currentGameState.isGameOver) { - if (window.gameUI?.showGameOver && !document.getElementById('game-over-screen').classList.contains(window.GAME_CONFIG?.CSS_CLASS_HIDDEN || 'hidden')) { - // Экран уже показан - } else if (window.gameUI?.showGameOver) { - let playerWon = false; - if (data.winnerId) { - playerWon = data.winnerId === clientState.myPlayerId; - } else if (clientState.currentGameState.player && clientState.currentGameState.opponent) { - // Дополнительная логика определения победителя, если winnerId нет (маловероятно при корректной работе сервера) - if (clientState.currentGameState.player.currentHp > 0 && clientState.currentGameState.opponent.currentHp <=0) { - playerWon = clientState.myPlayerId === clientState.currentGameState.player.id; - } else if (clientState.currentGameState.opponent.currentHp > 0 && clientState.currentGameState.player.currentHp <=0) { - playerWon = clientState.myPlayerId === clientState.currentGameState.opponent.id; - } + // Проверяем, не показан ли уже экран gameOver + const gameOverScreen = document.getElementById('game-over-screen'); + const hiddenClass = window.GAME_CONFIG?.CSS_CLASS_HIDDEN || 'hidden'; + if (gameOverScreen && gameOverScreen.classList.contains(hiddenClass)) { + let playerWon = data.winnerId === clientState.myPlayerId; + // Дополнительная логика определения победителя, если winnerId не всегда приходит + if (data.winnerId === null && data.reason && data.reason.startsWith('server_error')) { + // Ничья или ошибка сервера, никто не выиграл + } else if (!data.winnerId && clientState.currentGameState.player && clientState.currentGameState.opponent) { + if (clientState.currentGameState.player.currentHp <= 0 && clientState.currentGameState.opponent.currentHp <= 0) playerWon = false; // Ничья или оба проиграли + else if (clientState.currentGameState.player.currentHp > 0 && clientState.currentGameState.opponent.currentHp <= 0) playerWon = (clientState.myPlayerId === clientState.currentGameState.player.id); + else if (clientState.currentGameState.opponent.currentHp > 0 && clientState.currentGameState.player.currentHp <= 0) playerWon = (clientState.myPlayerId === clientState.currentGameState.opponent.id); + } + + console.log(`[CLIENT ${username} GP] Game received as 'Over' in ${eventName}. Calling showGameOver. PlayerWon: ${playerWon}`); + if (window.gameUI?.showGameOver) { + window.gameUI.showGameOver(playerWon, data.reason || "Игра завершена", clientState.opponentCharacterKey || data.loserCharacterKey, data); } - window.gameUI.showGameOver(playerWon, data.reason || "Игра завершена", clientState.opponentCharacterKey || data.loserCharacterKey, { finalGameState: clientState.currentGameState, ...data }); } if (returnToMenuButton) returnToMenuButton.disabled = false; } @@ -266,14 +279,14 @@ export function initGameplay(dependencies) { handleGameDataReceived(data, 'gameStarted'); }); - socket.on('gameState', (data) => { + socket.on('gameState', (data) => { // Для совместимости со старым событием реконнекта handleGameDataReceived(data, 'gameState (reconnect)'); }); socket.on('gameStateUpdate', (data) => { if (!clientState.isLoggedIn || !clientState.isInGame || !clientState.currentGameId || !window.GAME_CONFIG) return; const username = clientState.loggedInUsername || 'N/A'; - console.log(`[CLIENT ${username}] Event: gameStateUpdate. GS.isPlayerTurn: ${data.gameState?.isPlayerTurn}`); + console.log(`[CLIENT ${username} GP] Event: gameStateUpdate. GS.isPlayerTurn: ${data.gameState?.isPlayerTurn}. ActionInProgress before reset: ${clientState.isActionInProgress}`); clientState.isActionInProgress = false; // <--- СБРОС ФЛАГА clientState.currentGameState = data.gameState; @@ -281,14 +294,14 @@ export function initGameplay(dependencies) { if (window.gameUI?.updateUI) { requestAnimationFrame(() => { - window.gameUI.updateUI(); + window.gameUI.updateUI(); // Обновляем панели, эффекты и т.д. if (clientState.isInGame && clientState.currentGameState && !clientState.currentGameState.isGameOver && window.GAME_CONFIG) { const config = window.GAME_CONFIG; const isMyActualTurn = clientState.myPlayerId && ((clientState.currentGameState.isPlayerTurn && clientState.myPlayerId === config.PLAYER_ID) || (!clientState.currentGameState.isPlayerTurn && clientState.myPlayerId === config.OPPONENT_ID)); - console.log(`[CLIENT ${username}] gameStateUpdate - Determining controls. isMyActualTurn: ${isMyActualTurn}`); + console.log(`[CLIENT ${username} GP] gameStateUpdate - Determining controls. isMyActualTurn: ${isMyActualTurn}`); if (isMyActualTurn) { enableGameControls(); } else { @@ -297,13 +310,13 @@ export function initGameplay(dependencies) { const statusMsgElement = document.getElementById('game-status-message'); const currentStatusText = statusMsgElement ? statusMsgElement.textContent : ""; - if (!currentStatusText.toLowerCase().includes("отключился")) { - ui.setGameStatusMessage(""); + if (!currentStatusText.toLowerCase().includes("отключился")) { // Не стираем сообщение о дисконнекте оппонента + ui.setGameStatusMessage(""); // Очищаем общий статус, если игра активна } } else if (clientState.currentGameState && clientState.currentGameState.isGameOver) { - console.log(`[CLIENT ${username}] gameStateUpdate - Game is over, disabling controls.`); - disableGameControls(); + console.log(`[CLIENT ${username} GP] gameStateUpdate resulted in GameOver. Disabling controls.`); + disableGameControls(); // Отключаем управление, если игра закончилась этим обновлением } }); } @@ -315,8 +328,6 @@ export function initGameplay(dependencies) { socket.on('logUpdate', (data) => { if (!clientState.isLoggedIn || !clientState.isInGame || !clientState.currentGameId || !window.GAME_CONFIG) return; - // const username = clientState.loggedInUsername || 'N/A'; - // console.log(`[CLIENT ${username}] Event: logUpdate. Logs:`, data.log); if (window.gameUI?.addToLog && data.log) { data.log.forEach(log => window.gameUI.addToLog(log.message, log.type)); } @@ -329,7 +340,7 @@ export function initGameplay(dependencies) { return; } const username = clientState.loggedInUsername || 'N/A'; - console.log(`[CLIENT ${username}] Event: gameOver. WinnerID: ${data.winnerId}, Reason: ${data.reason}`); + console.log(`[CLIENT ${username} GP] Event: gameOver. WinnerID: ${data.winnerId}, Reason: ${data.reason}`); clientState.isActionInProgress = false; // <--- СБРОС ФЛАГА const playerWon = data.winnerId === clientState.myPlayerId; @@ -357,7 +368,7 @@ export function initGameplay(dependencies) { socket.on('opponentDisconnected', (data) => { if (!clientState.isLoggedIn || !clientState.isInGame || !clientState.currentGameId || !window.GAME_CONFIG) return; const username = clientState.loggedInUsername || 'N/A'; - console.log(`[CLIENT ${username}] Event: opponentDisconnected. PlayerID: ${data.disconnectedPlayerId}`); + console.log(`[CLIENT ${username} GP] Event: opponentDisconnected. PlayerID: ${data.disconnectedPlayerId}`); const name = data.disconnectedCharacterName || clientState.opponentBaseStatsServer?.name || 'Противник'; if (clientState.currentGameState && !clientState.currentGameState.isGameOver) { @@ -366,28 +377,29 @@ export function initGameplay(dependencies) { } }); - socket.on('playerReconnected', (data) => { // Обработчик события, что оппонент переподключился + socket.on('playerReconnected', (data) => { if (!clientState.isLoggedIn || !clientState.isInGame || !clientState.currentGameId || !window.GAME_CONFIG) return; const username = clientState.loggedInUsername || 'N/A'; - console.log(`[CLIENT ${username}] Event: playerReconnected. PlayerID: ${data.reconnectedPlayerId}, Name: ${data.reconnectedPlayerName}`); - // const name = data.reconnectedPlayerName || clientState.opponentBaseStatsServer?.name || 'Противник'; + console.log(`[CLIENT ${username} GP] Event: playerReconnected. PlayerID: ${data.reconnectedPlayerId}, Name: ${data.reconnectedPlayerName}`); if (clientState.currentGameState && !clientState.currentGameState.isGameOver) { - // Сообщение о переподключении оппонента обычно приходит через 'logUpdate' - // Но если нужно немедленно убрать статус "Ожидание...", можно сделать здесь: const statusMsgElement = document.getElementById('game-status-message'); const currentStatusText = statusMsgElement ? statusMsgElement.textContent : ""; if (currentStatusText.toLowerCase().includes("отключился")) { ui.setGameStatusMessage(""); // Очищаем сообщение об ожидании } - // Логика enable/disableGameControls будет вызвана следующим gameStateUpdate или turnTimerUpdate + // После этого должен прийти gameStateUpdate, который правильно установит контролы + // или turnTimerUpdate. Можно не вызывать enable/disable здесь. } }); socket.on('turnTimerUpdate', (data) => { + const username = clientState.loggedInUsername || 'N/A'; + // console.log(`[CLIENT ${username} GP] Event: turnTimerUpdate RECEIVED. Data:`, JSON.stringify(data), `Current isInGame: ${clientState.isInGame}, GS exists: ${!!clientState.currentGameState}`); + if (!clientState.isInGame || !clientState.currentGameState || !window.GAME_CONFIG) { - if (window.gameUI?.updateTurnTimerDisplay && clientState.currentGameState && !clientState.currentGameState.isGameOver) { + if (window.gameUI?.updateTurnTimerDisplay && clientState.currentGameState && clientState.currentGameState.isGameOver === false) { // Если не в игре, но gameState еще не gameOver window.gameUI.updateTurnTimerDisplay(null, false, clientState.currentGameState.gameMode); } return; @@ -397,48 +409,41 @@ export function initGameplay(dependencies) { if (window.gameUI?.updateTurnTimerDisplay) { window.gameUI.updateTurnTimerDisplay(null, false, clientState.currentGameState.gameMode); } - // disableGameControls() уже должен быть вызван в gameOver - return; + return; // Не обновляем контролы, если игра окончена } - // const username = clientState.loggedInUsername || 'N/A'; - // console.log(`[CLIENT ${username}] Event: turnTimerUpdate. Remaining: ${data.remainingTime}, isPlayerTurnForTimer: ${data.isPlayerTurn}, isPaused: ${data.isPaused}`); if (window.gameUI && typeof window.gameUI.updateTurnTimerDisplay === 'function') { const config = window.GAME_CONFIG; - const isMyTurnForTimer = clientState.myPlayerId && clientState.currentGameState && - ((data.isPlayerTurn && clientState.myPlayerId === config.PLAYER_ID) || // Серверное data.isPlayerTurn здесь авторитетно для таймера + // data.isPlayerTurn здесь - это isPlayerTurnForTimer от сервера (чей ход с точки зрения таймера) + const isMyTurnAccordingToTimer = clientState.myPlayerId && + ((data.isPlayerTurn && clientState.myPlayerId === config.PLAYER_ID) || (!data.isPlayerTurn && clientState.myPlayerId === config.OPPONENT_ID)); - window.gameUI.updateTurnTimerDisplay(data.remainingTime, isMyTurnForTimer, clientState.currentGameState.gameMode); + window.gameUI.updateTurnTimerDisplay(data.remainingTime, isMyTurnAccordingToTimer, clientState.currentGameState.gameMode); - // Если игра НЕ на паузе (серверной или клиентской из-за дисконнекта оппонента) - if (!data.isPaused) { - // Управление кнопками должно быть на основе isPlayerTurn из gameState, а не из turnTimerUpdate - // gameStateUpdate обработает это. Здесь только если нужно немедленно реагировать на isPlayerTurn из таймера, - // но это может привести к конфликтам с gameState.isPlayerTurn. - // Лучше положиться на gameStateUpdate. - // Однако, если ТАЙМЕР НЕ ПРИОСТАНОВЛЕН и это МОЙ ХОД по таймеру, то кнопки должны быть активны. - // Это может быть полезно, если gameStateUpdate запаздывает. - if (isMyTurnForTimer && !clientState.currentGameState.isGameOver) { // Дополнительная проверка на GameOver + // Управление кнопками на основе информации из ТАЙМЕРА. + // Это может быть полезно, если gameStateUpdate запаздывает. + // Но нужно быть осторожным, чтобы не конфликтовать с gameState.isPlayerTurn. + // Если игра НЕ на паузе (по данным таймера), то можно обновить кнопки. + if (!data.isPaused) { // isPaused от сервера (isTimerLogicPaused || game.isGameEffectivelyPaused()) + if (isMyTurnAccordingToTimer && !clientState.currentGameState.isGameOver) { enableGameControls(); - } else if (!isMyTurnForTimer && !clientState.currentGameState.isGameOver){ // Иначе, если не мой ход + } else if (!isMyTurnAccordingToTimer && !clientState.currentGameState.isGameOver){ disableGameControls(); } const statusMsgElement = document.getElementById('game-status-message'); const currentStatusText = statusMsgElement ? statusMsgElement.textContent : ""; if (!currentStatusText.toLowerCase().includes("отключился") && !clientState.currentGameState.isGameOver) { - // console.log(`[CLIENT ${username}] turnTimerUpdate - Clearing game status message as timer is active and not paused.`); ui.setGameStatusMessage(""); } } else { // Если игра на паузе (по данным таймера) - // console.log(`[CLIENT ${username}] turnTimerUpdate - Game is paused, disabling controls.`); - disableGameControls(); // Отключаем управление, если таймер говорит, что игра на паузе + disableGameControls(); } } }); - // Начальная деактивация (на всякий случай, хотя showAuthScreen/showGameSelectionScreen должны это делать) + // Начальная деактивация (на случай, если UI не скрыт изначально) disableGameControls(); } \ No newline at end of file