bc/public/js/gameplay.js

362 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/gameplay.js (Откаченная версия, совместимая с последним GameInstance.js)
export function initGameplay(dependencies) {
const { socket, clientState, ui } = dependencies;
const { returnToMenuButton } = ui.elements;
const attackButton = document.getElementById('button-attack');
const abilitiesGrid = document.getElementById('abilities-grid');
// --- Вспомогательные функции ---
function enableGameControls(enableAttack = true, enableAbilities = true) {
if (attackButton) attackButton.disabled = !enableAttack;
if (abilitiesGrid) {
const config = window.GAME_CONFIG || {};
const cls = config.CSS_CLASS_ABILITY_BUTTON || 'ability-button';
abilitiesGrid.querySelectorAll(`.${cls}`).forEach(b => { b.disabled = !enableAbilities; });
}
if (window.gameUI?.updateUI) {
requestAnimationFrame(() => window.gameUI.updateUI());
}
}
function disableGameControls() {
enableGameControls(false, false);
}
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 = clientState.playerAbilitiesServer;
const baseStatsForResource = clientState.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 handleAbilityButtonClick(event) {
const abilityId = event.currentTarget.dataset.abilityId;
if (clientState.isLoggedIn &&
clientState.isInGame &&
clientState.currentGameId &&
abilityId &&
clientState.currentGameState &&
!clientState.currentGameState.isGameOver) {
socket.emit('playerAction', { actionType: 'ability', abilityId: abilityId });
disableGameControls();
} else {
console.warn("Cannot perform ability action, invalid state");
}
}
// --- Обработчики событий DOM ---
if (attackButton) {
attackButton.addEventListener('click', () => {
if (clientState.isLoggedIn &&
clientState.isInGame &&
clientState.currentGameId &&
clientState.currentGameState &&
!clientState.currentGameState.isGameOver) {
socket.emit('playerAction', { actionType: 'attack' });
disableGameControls();
} else {
console.warn("Cannot perform attack action, invalid state.");
}
});
}
if (returnToMenuButton) {
returnToMenuButton.addEventListener('click', () => {
if (!clientState.isLoggedIn) {
ui.showAuthScreen();
return;
}
returnToMenuButton.disabled = true;
clientState.isInGame = false;
disableGameControls();
ui.showGameSelectionScreen(clientState.loggedInUsername);
});
}
// --- ОБЩИЙ ОБРАБОТЧИК ДЛЯ ЗАПУСКА/ВОССТАНОВЛЕНИЯ ИГРЫ ---
function handleGameDataReceived(data, eventName = "unknown") {
if (!clientState.isLoggedIn) return;
const username = clientState.loggedInUsername || 'N/A'; // Для логов
console.log(`[CLIENT ${username}] ${eventName} received.`);
// if (data.log) console.log(`[CLIENT ${username}] ${eventName} log content:`, JSON.parse(JSON.stringify(data.log)));
clientState.currentGameId = data.gameId;
clientState.myPlayerId = data.yourPlayerId;
clientState.currentGameState = data.initialGameState || data.gameState;
clientState.playerBaseStatsServer = data.playerBaseStats;
clientState.opponentBaseStatsServer = data.opponentBaseStats;
clientState.playerAbilitiesServer = data.playerAbilities;
clientState.opponentAbilitiesServer = data.opponentAbilities;
clientState.myCharacterKey = data.playerBaseStats?.characterKey;
clientState.opponentCharacterKey = data.opponentBaseStats?.characterKey;
if (clientState.currentGameState && !clientState.currentGameState.isGameOver) {
clientState.isInGame = true;
} else if (clientState.currentGameState && clientState.currentGameState.isGameOver) {
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' };
}
ui.updateGlobalWindowVariablesForUI();
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();
}
}
initializeAbilityButtons();
if (window.gameUI?.uiElements?.log?.list) {
// console.log(`[CLIENT ${username}] Log BEFORE clear in ${eventName}:`, window.gameUI.uiElements.log.list.innerHTML.substring(0,100));
window.gameUI.uiElements.log.list.innerHTML = ''; // Очищаем UI-лог перед добавлением новых
// console.log(`[CLIENT ${username}] Log AFTER clear in ${eventName}:`, window.gameUI.uiElements.log.list.innerHTML);
}
if (window.gameUI?.addToLog && data.log) {
data.log.forEach(logEntry => {
// console.log(`[CLIENT ${username}] Adding to UI log from ${eventName}: "${logEntry.message}"`);
window.gameUI.addToLog(logEntry.message, logEntry.type);
});
}
requestAnimationFrame(() => {
if (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));
if (isMyActualTurn) {
enableGameControls();
} else {
disableGameControls();
}
}
});
// Управление gameStatusMessage
if (clientState.currentGameState && clientState.currentGameState.isGameOver) {
// gameOver имеет свой обработчик статуса (внутри socket.on('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.`);
ui.setGameStatusMessage("");
} else {
// Для gameStateUpdate и других событий, не являющихся полной перезагрузкой,
// gameStatusMessage будет управляться в их обработчиках или через turnTimerUpdate.
// Если игра продолжается и не gameOver, общее сообщение "Ожидание" должно сниматься.
if (clientState.isInGame) {
ui.setGameStatusMessage("");
}
}
// Если игра пришла завершенной, то showGameOver должен быть вызван.
if (clientState.currentGameState && clientState.currentGameState.isGameOver) {
if (window.gameUI?.showGameOver && !document.getElementById('game-over-screen').classList.contains('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) {
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;
}
}
window.gameUI.showGameOver(playerWon, data.reason || "Игра завершена", clientState.opponentCharacterKey, { finalGameState: clientState.currentGameState, ...data });
}
if (returnToMenuButton) returnToMenuButton.disabled = false;
}
}
// --- Обработчики событий Socket.IO ---
socket.on('gameStarted', (data) => {
handleGameDataReceived(data, 'gameStarted');
});
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.`);
clientState.currentGameState = data.gameState;
ui.updateGlobalWindowVariablesForUI();
if (window.gameUI?.updateUI) {
requestAnimationFrame(() => {
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));
if (isMyActualTurn) {
enableGameControls();
} else {
disableGameControls();
}
console.log(`[CLIENT ${username}] gameStateUpdate - Clearing game status message as game is active.`);
ui.setGameStatusMessage("");
} else if (clientState.currentGameState && clientState.currentGameState.isGameOver) {
disableGameControls();
}
});
}
if (window.gameUI?.addToLog && data.log) {
data.log.forEach(log => window.gameUI.addToLog(log.message, log.type));
}
});
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));
}
});
socket.on('gameOver', (data) => {
if (!clientState.isLoggedIn || !clientState.currentGameId || !window.GAME_CONFIG) {
if (!clientState.currentGameId && clientState.isLoggedIn) socket.emit('requestGameState');
else if (!clientState.isLoggedIn) ui.showAuthScreen();
return;
}
const username = clientState.loggedInUsername || 'N/A';
console.log(`[CLIENT ${username}] Event: gameOver.`);
const playerWon = data.winnerId === clientState.myPlayerId;
clientState.currentGameState = data.finalGameState;
clientState.isInGame = false;
ui.updateGlobalWindowVariablesForUI();
if (window.gameUI?.updateUI) requestAnimationFrame(() => 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 = clientState.opponentBaseStatsServer?.characterKey;
window.gameUI.showGameOver(playerWon, data.reason, oppKey, data);
}
if (returnToMenuButton) returnToMenuButton.disabled = false;
// `ui.setGameStatusMessage` будет установлено специфичным сообщением о результате игры
// ui.setGameStatusMessage("Игра окончена. " + (playerWon ? "Вы победили!" : "Вы проиграли."));
if (window.gameUI?.updateTurnTimerDisplay) {
window.gameUI.updateTurnTimerDisplay(null, false, clientState.currentGameState?.gameMode);
}
disableGameControls();
});
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.`);
const name = data.disconnectedCharacterName || clientState.opponentBaseStatsServer?.name || 'Противник';
// Сообщение об отключении оппонента должно приходить через 'logUpdate' от сервера
// if (window.gameUI?.addToLog) {
// window.gameUI.addToLog(`🔌 Противник (${name}) отключился.`, 'system');
// }
if (clientState.currentGameState && !clientState.currentGameState.isGameOver) {
ui.setGameStatusMessage(`Противник (${name}) отключился. Ожидание...`, true);
disableGameControls();
}
});
socket.on('turnTimerUpdate', (data) => {
if (!clientState.isInGame || !clientState.currentGameState || !window.GAME_CONFIG) {
if (window.gameUI?.updateTurnTimerDisplay && clientState.currentGameState && !clientState.currentGameState.isGameOver) {
window.gameUI.updateTurnTimerDisplay(null, false, clientState.currentGameState.gameMode);
}
return;
}
if (clientState.currentGameState.isGameOver) {
if (window.gameUI?.updateTurnTimerDisplay) {
window.gameUI.updateTurnTimerDisplay(null, false, clientState.currentGameState.gameMode);
}
disableGameControls();
return;
}
const username = clientState.loggedInUsername || 'N/A';
// console.log(`[CLIENT ${username}] Event: turnTimerUpdate.`);
if (window.gameUI && typeof window.gameUI.updateTurnTimerDisplay === 'function') {
const config = window.GAME_CONFIG;
const isMyActualTurn = clientState.myPlayerId && clientState.currentGameState &&
((clientState.currentGameState.isPlayerTurn && clientState.myPlayerId === config.PLAYER_ID) ||
(!clientState.currentGameState.isPlayerTurn && clientState.myPlayerId === config.OPPONENT_ID));
window.gameUI.updateTurnTimerDisplay(data.remainingTime, isMyActualTurn, clientState.currentGameState.gameMode);
if (isMyActualTurn) {
enableGameControls();
} else {
disableGameControls();
}
if (!clientState.currentGameState.isGameOver) {
console.log(`[CLIENT ${username}] turnTimerUpdate - Clearing game status message as timer is active.`);
ui.setGameStatusMessage("");
}
}
});
// Начальная деактивация
disableGameControls();
}