diff --git a/bc.js b/bc.js
index c280e5e..b28f6d7 100644
--- a/bc.js
+++ b/bc.js
@@ -1,7 +1,7 @@
// bc.js - Главный файл сервера Battle Club
const express = require('express');
-const http = require('http'); // Используем HTTP, так как SSL будет на Node.js прокси (server.js)
+const http = require('http');
const { Server } = require('socket.io');
const path = require('path');
@@ -16,13 +16,10 @@ const app = express();
const server = http.createServer(app);
// Настройка Socket.IO
+// cors options могут потребоваться, если клиент и сервер работают на разных портах/доменах
const io = new Server(server, {
cors: {
- origin: "https://pavel-chagovsky.com:3200", // Указываем точный origin, включая порт, откуда придет запрос К ПРОКСИ
- // Если доступ будет с нескольких доменов или портов, можно использовать массив:
- // origin: ["https://pavel-chagovsky.com:3200", "https://oleg-okhotnikov.ru:3200"],
- // Или для разработки можно временно использовать "*", но это менее безопасно:
- // origin: "*",
+ origin: "*", // Разрешить подключение с любого домена (для разработки). В продакшене лучше указать конкретный домен клиента.
methods: ["GET", "POST"]
}
});
@@ -34,132 +31,198 @@ app.use(express.static(path.join(__dirname, 'public')));
const gameManager = new GameManager(io);
// Хранилище информации о залогиненных пользователях по socket.id
+// В более сложном приложении здесь может быть Redis или другое внешнее хранилище сессий
const loggedInUsers = {}; // { socket.id: { userId: ..., username: ... } }
// Обработка подключений Socket.IO
io.on('connection', (socket) => {
- console.log(`[BC App HTTP] Socket.IO User connected: ${socket.id}`);
+ console.log(`[Socket.IO] Пользователь подключился: ${socket.id}`);
- socket.userData = null;
+ // Привязываем user data к сокету (пока пустые)
+ socket.userData = null; // { userId: ..., username: ... }
+ // При подключении клиента, если он уже залогинен (например, по cookie/token, что здесь не реализовано,
+ // но может быть добавлено), нужно восстановить его user data и проверить, не в игре ли он.
+ // В текущей простой реализации, мы полагаемся на то, что клиент после коннекта сам отправит логин,
+ // если он был залогинен. Но если бы была проверка сессии, логика была бы тут.
+ // Добавляем вызов handleRequestGameState при коннекте, если есть user data (для примера,
+ // но для полной реализации нужны cookies/токены)
+ // if (socket.userData?.userId) { // Эта проверка сработает только после успешного логина в текущей сессии
+ // gameManager.handleRequestGameState(socket, socket.userData.userId); // Передаем объект socket
+ // }
+
+
+ // --- Обработчики событий Аутентификации ---
socket.on('register', async (data) => {
- console.log(`[BC App HTTP Socket.IO] Register attempt for username: "${data?.username}" from ${socket.id}`);
+ console.log(`[Socket.IO] Register attempt for username: "${data?.username}" from ${socket.id}`);
const result = await auth.registerUser(data?.username, data?.password);
if (result.success) {
- console.log(`[BC App HTTP Socket.IO] Registration successful for ${result.username} (${result.userId})`);
+ console.log(`[Socket.IO] Registration successful for ${result.username} (${result.userId})`);
} else {
- console.warn(`[BC App HTTP Socket.IO] Registration failed for "${data?.username}": ${result.message}`);
+ console.warn(`[Socket.IO] Registration failed for "${data?.username}": ${result.message}`);
}
socket.emit('registerResponse', result);
});
socket.on('login', async (data) => {
- console.log(`[BC App HTTP Socket.IO] Login attempt for username: "${data?.username}" from ${socket.id}`);
+ console.log(`[Socket.IO] Login attempt for username: "${data?.username}" from ${socket.id}`);
const result = await auth.loginUser(data?.username, data?.password);
if (result.success) {
- console.log(`[BC App HTTP Socket.IO] Login successful for ${result.username} (${result.userId}). Assigning to socket ${socket.id}.`);
+ console.log(`[Socket.IO] Login successful for ${result.username} (${result.userId}). Assigning to socket ${socket.id}.`);
+ // Сохраняем информацию о пользователе в сессии сокета
socket.userData = { userId: result.userId, username: result.username };
loggedInUsers[socket.id] = socket.userData;
+
+ // Проверяем, есть ли у пользователя активная игра при логине (если он был отключен)
+ // ИСПРАВЛЕНИЕ: Передаем объект socket
gameManager.handleRequestGameState(socket, socket.userData.userId);
+
} else {
- console.warn(`[BC App HTTP Socket.IO] Login failed for "${data?.username}": ${result.message}`);
- socket.userData = null;
+ console.warn(`[Socket.IO] Login failed for "${data?.username}": ${result.message}`);
+ socket.userData = null; // Убеждаемся, что данные пользователя на сокете сброшены
if (loggedInUsers[socket.id]) delete loggedInUsers[socket.id];
}
socket.emit('loginResponse', result);
});
socket.on('logout', () => {
- console.log(`[BC App HTTP Socket.IO] Logout for user ${socket.userData?.username || socket.id}`);
+ console.log(`[Socket.IO] Logout for user ${socket.userData?.username || socket.id}`);
+ // Уведомляем gameManager о дисконнекте (для корректного выхода из игры, если в ней был)
+ // Game Manager сам очистит ссылку socketToGame[socket.id] при handleDisconnect
+ // ИСПРАВЛЕНИЕ: Передаем userId или socket.id в handleDisconnect
gameManager.handleDisconnect(socket.id, socket.userData?.userId || socket.id);
+
+ // Очищаем информацию о пользователе на сокете и в хранилище
socket.userData = null;
if (loggedInUsers[socket.id]) delete loggedInUsers[socket.id];
+
+ // Клиент должен сам переключиться на экран аутентификации
});
+ // --- Обработчики событий Управления Играми ---
+
socket.on('createGame', (data) => {
- const identifier = socket.userData?.userId || socket.id;
+ // Пользователь, даже не залогиненный, может создать AI игру (идентифицируется по socket.id)
+ // Для PvP игры нужна аутентификация (идентификация по userId)
+ const identifier = socket.userData?.userId || socket.id; // Используем userId для залогиненных, socket.id для гостей
const mode = data?.mode || 'ai';
+
if (mode === 'pvp' && !socket.userData) {
socket.emit('gameError', { message: 'Необходимо войти в систему для создания PvP игры.' });
return;
}
- console.log(`[BC App HTTP Socket.IO] Create Game request from ${socket.userData?.username || socket.id} (Identifier: ${identifier}). Mode: ${mode}, Character: ${data?.characterKey}`);
- const characterKey = data?.characterKey || 'elena';
- gameManager.createGame(socket, mode, characterKey, identifier);
+
+ console.log(`[Socket.IO] Create Game request from ${socket.userData?.username || socket.id} (Identifier: ${identifier}). Mode: ${mode}, Character: ${data?.characterKey}`);
+
+ const characterKey = data?.characterKey || 'elena'; // По умолчанию Елена
+ gameManager.createGame(socket, mode, characterKey, identifier); // Передаем идентификатор
+
});
socket.on('joinGame', (data) => {
- if (!socket.userData) {
+ if (!socket.userData) { // Проверяем, залогинен ли пользователь
socket.emit('gameError', { message: 'Необходимо войти в систему для присоединения к игре.' });
return;
}
- console.log(`[BC App HTTP Socket.IO] Join Game request from ${socket.userData.username} (${socket.id}). Game ID: ${data?.gameId}`);
+ console.log(`[Socket.IO] Join Game request from ${socket.userData.username} (${socket.id}). Game ID: ${data?.gameId}`);
const gameId = data?.gameId;
- const identifier = socket.userData.userId;
+ const identifier = socket.userData.userId; // Присоединиться может только залогиненный
+
if (gameId) {
- gameManager.joinGame(socket, gameId, identifier);
+ gameManager.joinGame(socket, gameId, identifier); // Передаем идентификатор
} else {
socket.emit('gameError', { message: 'Не указан ID игры для присоединения.' });
}
});
socket.on('findRandomGame', (data) => {
- if (!socket.userData) {
+ if (!socket.userData) { // Проверяем, залогинен ли пользователь
socket.emit('gameError', { message: 'Необходимо войти в систему для поиска игры.' });
return;
}
- console.log(`[BC App HTTP Socket.IO] Find Random Game request from ${socket.userData.username} (${socket.id}). Preferred Character: ${data?.characterKey}`);
- const characterKey = data?.characterKey || 'elena';
- const identifier = socket.userData.userId;
- gameManager.findAndJoinRandomPvPGame(socket, characterKey, identifier);
+ console.log(`[Socket.IO] Find Random Game request from ${socket.userData.username} (${socket.id}). Preferred Character: ${data?.characterKey}`);
+ const characterKey = data?.characterKey || 'elena'; // Предпочитаемый персонаж для создания, если не найдено
+ const identifier = socket.userData.userId; // Ищет и создает только залогиненный
+
+ gameManager.findAndJoinRandomPvPGame(socket, characterKey, identifier); // Передаем идентификатор
});
socket.on('requestPvPGameList', () => {
- console.log(`[BC App HTTP Socket.IO] Request PvP Game List from ${socket.userData?.username || socket.id}`);
+ // Список игр доступен всем, даже не залогиненным, но присоединиться можно только залогиненным
+ // if (!socket.userData) {
+ // socket.emit('gameError', { message: 'Необходимо войти в систему для просмотра игр.' });
+ // return;
+ // }
+ console.log(`[Socket.IO] Request PvP Game List from ${socket.userData?.username || socket.id}`);
const availableGames = gameManager.getAvailablePvPGamesListForClient();
socket.emit('availablePvPGamesList', availableGames);
});
+ // Обработчик для клиента, запрашивающего состояние игры (например, при переподключении)
socket.on('requestGameState', () => {
+ // Запрашивать состояние игры может только залогиненный пользователь, т.к. только у них есть userId для идентификации
if (!socket.userData) {
- console.log(`[BC App HTTP Socket.IO] Request Game State from unauthenticated socket ${socket.id}.`);
+ console.log(`[Socket.IO] Request Game State from unauthenticated socket ${socket.id}.`);
socket.emit('gameNotFound', { message: 'Необходимо войти для восстановления игры.' });
return;
}
- console.log(`[BC App HTTP Socket.IO] Request Game State from ${socket.userData.username} (${socket.id}).`);
+ console.log(`[Socket.IO] Request Game State from ${socket.userData.username} (${socket.id}).`);
+ // ИСПРАВЛЕНИЕ: Передаем объект socket и identifier (userId)
gameManager.handleRequestGameState(socket, socket.userData.userId);
});
+
+ // --- Обработчик события Игрового Действия ---
socket.on('playerAction', (actionData) => {
+ // Действие в игре может совершить как залогиненный (PvP), так и не залогиненный (AI) игрок.
+ // Используем userId для залогиненных, socket.id для гостей.
const identifier = socket.userData?.userId || socket.id;
- gameManager.handlePlayerAction(identifier, actionData);
+
+ // Game Manager сам проверит, находится ли идентификатор в игре и его ли сейчас ход
+ // ИСПРАВЛЕНИЕ: Передаем идентификатор вместо socket.id
+ gameManager.handlePlayerAction(identifier, actionData); // Передаем идентификатор
});
+
+ // --- Обработчик отключения сокета ---
socket.on('disconnect', (reason) => {
- const identifier = socket.userData?.userId || socket.id;
- console.log(`[BC App HTTP Socket.IO] User disconnected: ${socket.id} (Причина: ${reason}). Identifier: ${identifier}`);
- gameManager.handleDisconnect(socket.id, identifier);
+ const identifier = socket.userData?.userId || socket.id; // Используем userId для залогиненных, socket.id для гостей
+ console.log(`[Socket.IO] Пользователь отключился: ${socket.id} (Причина: ${reason}). Identifier: ${identifier}`);
+
+ // Уведомляем gameManager о дисконнекте, чтобы он обновил состояние игры.
+ // Передаем идентификатор пользователя.
+ gameManager.handleDisconnect(socket.id, identifier); // Передаем как socketId, так и identifier
+
+ // Удаляем пользователя из списка залогиненных, если был там
if (loggedInUsers[socket.id]) {
delete loggedInUsers[socket.id];
}
+ // Если сокет не был залогинен, его identifier был socket.id.
+ // Связь userIdentifierToGameId будет очищена в gameManager.handleDisconnect, если игра пуста.
});
+
+ // Опционально: отправка списка активных игр на сервере для отладки (по запросу с консоли или админки)
+ // global.getActiveGames = () => gameManager.getActiveGamesList();
+ // console.log("Type getActiveGames() in server console to list games.");
});
// Запуск HTTP сервера
-const PORT = process.env.BC_INTERNAL_PORT || 3200; // Внутренний порт для bc.js
-const HOSTNAME = '127.0.0.1'; // Слушать ТОЛЬКО на localhost
-
-server.listen(PORT, HOSTNAME, () => { // Явно указываем HOSTNAME
- console.log(`Battle Club HTTP Application Server running at http://${HOSTNAME}:${PORT}`);
- console.log(`This server should only be accessed locally by the reverse proxy.`);
+const PORT = process.env.PORT || 3200; // Использовать порт из переменных окружения или 3000 по умолчанию
+server.listen(PORT, () => {
+ console.log(`Server running on port ${PORT}`);
console.log(`Serving static files from: ${path.join(__dirname, 'public')}`);
+ // console.log("Database connection pool created/checked (from db.js require)."); // db.js уже логирует
});
// Обработка необработанных промис-ошибок
process.on('unhandledRejection', (reason, promise) => {
- console.error('[BC App HTTP UNHANDLED REJECTION] Unhandled Rejection at:', promise, 'reason:', reason);
+ console.error('[UNHANDLED REJECTION] Unhandled Rejection at:', promise, 'reason:', reason);
+ // Логировать ошибку, возможно, завершить процесс в продакшене
});
process.on('uncaughtException', (err) => {
- console.error('[BC App HTTP UNCAUGHT EXCEPTION] Caught exception:', err);
+ console.error('[UNCAUGHT EXCEPTION] Caught exception:', err);
+ // Логировать ошибку, выполнить очистку ресурсов, и завершить процесс
+ // В продакшене здесь может быть более сложная логика, например, graceful shutdown
+ // process.exit(1); // Аварийное завершение процесса - можно раскомментировать в продакшене
});
\ No newline at end of file
diff --git a/public/js/ui.js b/public/js/ui.js
index bf2149d..ae35db4 100644
--- a/public/js/ui.js
+++ b/public/js/ui.js
@@ -14,6 +14,7 @@
status: document.getElementById('player-status'),
effectsContainer: document.getElementById('player-effects'),
buffsList: document.getElementById('player-effects')?.querySelector('.player-buffs'),
+ // ИСПРАВЛЕНО: Селектор для списка дебаффов игрока
debuffsList: document.getElementById('player-effects')?.querySelector('.player-debuffs')
},
opponent: { // Панель для персонажа-противника ЭТОГО клиента
@@ -24,7 +25,9 @@
resourceFill: document.getElementById('opponent-resource-fill'), resourceText: document.getElementById('opponent-resource-text'),
status: document.getElementById('opponent-status'),
effectsContainer: document.getElementById('opponent-effects'),
+ // ИСПРАВЛЕНО: Селектор для списка баффов оппонента
buffsList: document.getElementById('opponent-effects')?.querySelector('.opponent-buffs'),
+ // ИСПРАВЛЕНО: Селектор для списка дебаффов оппонента
debuffsList: document.getElementById('opponent-effects')?.querySelector('.opponent-debuffs')
},
controls: {
@@ -39,8 +42,7 @@
gameOver: {
screen: document.getElementById('game-over-screen'),
message: document.getElementById('result-message'),
- // restartButton: document.getElementById('restart-game-button'), // Старый ID, заменен
- returnToMenuButton: document.getElementById('return-to-menu-button'), // Новый ID
+ returnToMenuButton: document.getElementById('return-to-menu-button'),
modalContent: document.getElementById('game-over-screen')?.querySelector('.modal-content')
},
gameHeaderTitle: document.querySelector('.game-header h1'),
@@ -55,7 +57,7 @@
if (!logListElement) return;
const li = document.createElement('li');
li.textContent = message;
- const config = window.GAME_CONFIG || {}; // Получаем конфиг из глобальной области
+ const config = window.GAME_CONFIG || {};
// Формируем класс для лога на основе типа (используем константы из конфига или фоллбэк)
const logTypeClass = config[`LOG_TYPE_${type.toUpperCase()}`] ? `log-${config[`LOG_TYPE_${type.toUpperCase()}`]}` : `log-${type}`;
li.className = logTypeClass;
@@ -70,63 +72,69 @@
// Базовая проверка наличия необходимых элементов и данных
if (!elements || !elements.hpFill || !elements.hpText || !elements.resourceFill || !elements.resourceText || !elements.status || !fighterState || !fighterBaseStats) {
- // console.warn(`updateFighterPanelUI: Отсутствуют элементы UI, состояние бойца или базовые статы для панели ${panelRole}.`);
// Если панель должна быть видима, но нет данных, можно ее скрыть или показать плейсхолдер
if (elements && elements.panel && elements.panel.style.display !== 'none') {
// console.warn(`updateFighterPanelUI: Нет данных для видимой панели ${panelRole}.`);
// elements.panel.style.opacity = '0.5'; // Пример: сделать полупрозрачной, если нет данных
}
+ // ВАЖНО: Очистить содержимое панели, если данных нет.
+ if (elements) {
+ if(elements.name) elements.name.innerHTML = (panelRole === 'player') ? ' Ожидание данных...' : ' Ожидание игрока...';
+ if(elements.hpText) elements.hpText.textContent = 'N/A';
+ if(elements.resourceText) elements.resourceText.textContent = 'N/A';
+ if(elements.status) elements.status.textContent = 'Неизвестно';
+ if(elements.buffsList) elements.buffsList.innerHTML = 'Нет';
+ if(elements.debuffsList) elements.debuffsList.innerHTML = 'Нет';
+ if(elements.avatar) elements.avatar.src = 'images/default_avatar.png';
+ if(panelRole === 'player' && uiElements.playerResourceTypeIcon) uiElements.playerResourceTypeIcon.className = 'fas fa-question';
+ if(panelRole === 'opponent' && uiElements.opponentResourceTypeIcon) uiElements.opponentResourceTypeIcon.className = 'fas fa-question';
+ if(panelRole === 'player' && uiElements.playerResourceBarContainer) uiElements.playerResourceBarContainer.classList.remove('mana', 'stamina', 'dark-energy');
+ if(panelRole === 'opponent' && uiElements.opponentResourceBarContainer) uiElements.opponentResourceBarContainer.classList.remove('mana', 'stamina', 'dark-energy');
+ if(elements.panel) elements.panel.style.opacity = '0.5'; // Затемняем
+ }
return;
}
- // Если панель была полупрозрачной (из-за отсутствия данных), а теперь данные есть, делаем ее полностью видимой
- // if (elements.panel && elements.panel.style.opacity !== '1' && fighterState && fighterBaseStats) {
- // elements.panel.style.opacity = '1';
- // }
+ if (elements.panel) elements.panel.style.opacity = '1'; // Делаем видимой, если данные есть
// Обновление имени и иконки персонажа
if (elements.name) {
let iconClass = 'fa-question'; // Иконка по умолчанию
- // let accentColor = 'var(--text-muted)'; // Цвет по умолчанию - теперь берется из CSS через классы иконок
const characterKey = fighterBaseStats.characterKey;
// Определяем класс иконки в зависимости от персонажа
- if (characterKey === 'elena') { iconClass = 'fa-hat-wizard icon-player'; } // icon-player имеет цвет через CSS
- else if (characterKey === 'almagest') { iconClass = 'fa-staff-aesculapius icon-almagest'; } // icon-almagest имеет цвет через CSS
- else if (characterKey === 'balard') { iconClass = 'fa-khanda icon-opponent'; } // icon-opponent имеет цвет через CSS
+ if (characterKey === 'elena') { iconClass = 'fa-hat-wizard icon-player'; }
+ else if (characterKey === 'almagest') { iconClass = 'fa-staff-aesculapius icon-almagest'; }
+ else if (characterKey === 'balard') { iconClass = 'fa-khanda icon-opponent'; }
else { /* console.warn(`updateFighterPanelUI: Неизвестный characterKey "${characterKey}" для иконки имени.`); */ }
- // Обновляем innerHTML имени, включая иконку и текст. Добавляем "(Вы)" для управляемого персонажа.
let nameHtml = ` ${fighterBaseStats.name || 'Неизвестно'}`;
if (isControlledByThisClient) nameHtml += " (Вы)";
elements.name.innerHTML = nameHtml;
- // Цвет имени теперь задается CSS через классы icon-player/opponent/almagest, примененные к самой иконке
- // elements.name.style.color = accentColor; // Эту строку можно удалить, если цвет задан через CSS
}
// Обновление аватара
if (elements.avatar && fighterBaseStats.avatarPath) {
elements.avatar.src = fighterBaseStats.avatarPath;
// Обновляем рамку аватара в зависимости от персонажа
- elements.avatar.classList.remove('avatar-elena', 'avatar-almagest', 'avatar-balard'); // Убираем старые классы
- elements.avatar.classList.add(`avatar-${fighterBaseStats.characterKey}`); // Добавляем класс для текущего персонажа
+ elements.avatar.classList.remove('avatar-elena', 'avatar-almagest', 'avatar-balard');
+ elements.avatar.classList.add(`avatar-${fighterBaseStats.characterKey}`);
} else if (elements.avatar) {
- elements.avatar.src = 'images/default_avatar.png'; // Запасной аватар
- elements.avatar.classList.remove('avatar-elena', 'avatar-almagest', 'avatar-balard'); // Убираем старые классы
+ elements.avatar.src = 'images/default_avatar.png';
+ elements.avatar.classList.remove('avatar-elena', 'avatar-almagest', 'avatar-balard');
}
// Обновление полос здоровья и ресурса
- const maxHp = Math.max(1, fighterBaseStats.maxHp); // Избегаем деления на ноль
+ const maxHp = Math.max(1, fighterBaseStats.maxHp);
const maxRes = Math.max(1, fighterBaseStats.maxResource);
const currentHp = Math.max(0, fighterState.currentHp);
const currentRes = Math.max(0, fighterState.currentResource);
elements.hpFill.style.width = `${(currentHp / maxHp) * 100}%`;
- elements.hpText.textContent = `${Math.round(currentHp)} / ${fighterBaseStats.maxHp}`;
- // ИСПРАВЛЕНО: Убрано округление для отображения текущего ресурса
+ elements.hpText.textContent = `${Math.round(currentHp)} / ${fighterBaseStats.maxHp}`; // Здоровье округляем
elements.resourceFill.style.width = `${(currentRes / maxRes) * 100}%`;
- elements.resourceText.textContent = `${currentRes} / ${fighterBaseStats.maxResource}`; // <-- ИСПРАВЛЕНО
+ elements.resourceText.textContent = `${currentRes} / ${fighterBaseStats.maxResource}`; // Ресурс не округляем
// Обновление типа ресурса и иконки (mana/stamina/dark-energy)
@@ -134,124 +142,158 @@
const resourceIconElementToUpdate = (panelRole === 'player') ? uiElements.playerResourceTypeIcon : uiElements.opponentResourceTypeIcon;
if (resourceBarContainerToUpdate && resourceIconElementToUpdate) {
- resourceBarContainerToUpdate.classList.remove('mana', 'stamina', 'dark-energy'); // Сначала удаляем все классы ресурсов
- let resourceClass = 'mana'; let iconClass = 'fa-flask'; // Значения по умолчанию (для Маны)
+ resourceBarContainerToUpdate.classList.remove('mana', 'stamina', 'dark-energy');
+ let resourceClass = 'mana'; let iconClass = 'fa-flask';
if (fighterBaseStats.resourceName === 'Ярость') { resourceClass = 'stamina'; iconClass = 'fa-fire-alt'; }
- else if (fighterBaseStats.resourceName === 'Темная Энергия') { resourceClass = 'dark-energy'; iconClass = 'fa-skull'; } // Или другую иконку для темной энергии
+ else if (fighterBaseStats.resourceName === 'Темная Энергия') { resourceClass = 'dark-energy'; iconClass = 'fa-skull'; } // или fa-wand-magic-sparkles, fa-star-half-alt и т.д.
+ else { console.warn(`updateFighterPanelUI: Unknown resource name "${fighterBaseStats.resourceName}" for icon/color.`); iconClass = 'fa-question-circle'; }
resourceBarContainerToUpdate.classList.add(resourceClass);
- resourceIconElementToUpdate.className = `fas ${iconClass}`; // Обновляем класс иконки
+ resourceIconElementToUpdate.className = `fas ${iconClass}`;
}
// Обновление статуса (Готов/Защищается)
const statusText = fighterState.isBlocking ? (config.STATUS_BLOCKING || 'Защищается') : (config.STATUS_READY || 'Готов(а)');
elements.status.textContent = statusText;
- elements.status.classList.toggle(config.CSS_CLASS_BLOCKING || 'blocking', fighterState.isBlocking); // Применяем класс для стилизации статуса "Защищается"
+ elements.status.classList.toggle(config.CSS_CLASS_BLOCKING || 'blocking', fighterState.isBlocking);
// Обновление подсветки и рамки панели (в зависимости от персонажа)
if (elements.panel) {
- let borderColorVar = 'var(--panel-border)'; // Цвет по умолчанию
- // Снимаем все старые классы для рамки
+ let borderColorVar = 'var(--panel-border)';
elements.panel.classList.remove('panel-elena', 'panel-almagest', 'panel-balard');
- // Применяем класс для рамки в зависимости от персонажа
- if (fighterBaseStats.characterKey === 'elena') { elements.panel.classList.add('panel-elena'); borderColorVar = 'var(--accent-player)'; } // Цвет рамки через CSS переменную
- else if (fighterBaseStats.characterKey === 'almagest') { elements.panel.classList.add('panel-almagest'); borderColorVar = 'var(--accent-almagest)'; } // Цвет рамки через CSS переменную
- else if (fighterBaseStats.characterKey === 'balard') { elements.panel.classList.add('panel-balard'); borderColorVar = 'var(--accent-opponent)'; } // Цвет рамки через CSS переменную
+ if (fighterBaseStats.characterKey === 'elena') { elements.panel.classList.add('panel-elena'); borderColorVar = 'var(--accent-player)'; }
+ else if (fighterBaseStats.characterKey === 'almagest') { elements.panel.classList.add('panel-almagest'); borderColorVar = 'var(--accent-almagest)'; }
+ else if (fighterBaseStats.characterKey === 'balard') { elements.panel.classList.add('panel-balard'); borderColorVar = 'var(--accent-opponent)'; }
+ else { console.warn(`updateFighterPanelUI: Unknown character key "${fighterBaseStats.characterKey}" for panel border color.`); }
+
- // Обновляем тень (свечение). Цвет свечения тоже может быть переменной.
let glowColorVar = 'rgba(0, 0, 0, 0.4)'; // Базовая тень
if (fighterBaseStats.characterKey === 'elena') glowColorVar = 'var(--panel-glow-player)';
- else if (fighterBaseStats.characterKey === 'almagest' || fighterBaseStats.characterKey === 'balard') glowColorVar = 'var(--panel-glow-opponent)'; // Используем одну тень для всех оппонентов (Балард/Альмагест)
+ // В твоем CSS --panel-glow-opponent используется для обоих Баларда и Альмагест
+ else if (fighterBaseStats.characterKey === 'almagest' || fighterBaseStats.characterKey === 'balard') glowColorVar = 'var(--panel-glow-opponent)';
- // Устанавливаем рамку и тень
elements.panel.style.borderColor = borderColorVar;
- // Используем переменную для свечения. Базовая тень inset оставлена как есть.
elements.panel.style.boxShadow = `0 0 15px ${glowColorVar}, inset 0 0 10px rgba(0, 0, 0, 0.3)`;
}
}
+ /**
+ * Генерирует HTML для списка эффектов.
+ * @param {Array