This repository has been archived on 2026-05-04. You can view files and clone it, but cannot push or open issues or pull requests.
Galaxy-Strike-Online/Galaxy-Strike-Online-main/Client/js/ui/UIManager.js

2733 lines
112 KiB
JavaScript

/**
* Galaxy Strike Online - UI Manager
* Handles all user interface interactions and updates
*/
class UIManager {
constructor(gameEngine) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.constructor', {
gameEngineProvided: !!gameEngine
});
this.game = gameEngine;
// UI state
this.currentTab = 'dashboard';
this.modalOpen = false;
this.notifications = [];
// Note: setupEventListeners() called in proceedWithInitialization() to prevent duplicates
if (debugLogger) debugLogger.endStep('UIManager.constructor', {
currentTab: this.currentTab,
modalOpen: this.modalOpen,
notificationsCount: this.notifications.length,
gameEngineSet: !!this.game,
eventListenersSetup: true
});
}
async initialize() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.initialize', {
currentTab: this.currentTab,
modalOpen: this.modalOpen,
notificationsCount: this.notifications.length
});
// Wait for DOM to be ready and game interface to be visible
const waitForDOM = () => {
const gameInterface = document.getElementById('gameInterface');
const navButtons = document.querySelectorAll('.nav-btn');
console.log('[UI MANAGER] DOM Check:', {
gameInterfaceExists: !!gameInterface,
gameInterfaceHidden: gameInterface?.classList.contains('hidden'),
navButtonsFound: navButtons.length,
documentReady: document.readyState
});
// Less strict condition - proceed if we have nav buttons, even if interface is still hidden
if (navButtons.length > 0) {
console.log('[UI MANAGER] Navigation buttons found, proceeding with initialization');
this.proceedWithInitialization();
} else {
console.log('[UI MANAGER] Waiting for navigation buttons...');
setTimeout(waitForDOM, 100);
}
};
// Start the DOM check
waitForDOM();
}
proceedWithInitialization() {
const debugLogger = window.debugLogger;
// Setup navigation
// if (debugLogger) debugLogger.logStep('Setting up navigation');
this.setupNavigation();
// Setup event listeners
// if (debugLogger) debugLogger.logStep('Setting up event listeners');
this.setupEventListeners();
// Setup resource display
// if (debugLogger) debugLogger.logStep('Setting up resource display');
this.setupResourceDisplay();
if (debugLogger) debugLogger.endStep('UIManager.initialize', {
currentTab: this.currentTab,
modalOpen: this.modalOpen,
notificationsCount: this.notifications.length,
navigationSetup: true,
eventListenersSetup: true,
resourceDisplaySetup: true
});
}
setupNavigation() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.setupNavigation');
// Navigation setup moved to setupEventListeners to fix scope issues
if (debugLogger) debugLogger.endStep('UIManager.setupNavigation', {
success: true
});
const craftingCatButtons = document.querySelectorAll('.crafting-cat-btn');
if (debugLogger) debugLogger.logStep('Setting up crafting category navigation', {
craftingCatButtonsCount: craftingCatButtons.length
});
craftingCatButtons.forEach(btn => {
btn.addEventListener('click', (e) => {
const category = e.currentTarget.dataset.category;
if (debugLogger) debugLogger.log('Crafting category button clicked', {
buttonElement: btn.tagName,
buttonText: btn.textContent,
category: category
});
this.switchCraftingCategory(category);
});
});
if (debugLogger) debugLogger.endStep('UIManager.setupNavigation', {
craftingCatButtonsSetup: craftingCatButtons.length
});
}
setupEventListeners() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.setupEventListeners');
// Setup tab navigation here to fix scope issues with endStep
const navButtons = document.querySelectorAll('.nav-btn');
console.log(`[UIManager] Found ${navButtons.length} navigation buttons`);
if (debugLogger) debugLogger.logStep('Setting up tab navigation', {
navButtonsCount: navButtons.length
});
if (navButtons.length === 0) {
console.warn('[UIManager] No navigation buttons found!');
if (debugLogger) debugLogger.logStep('No navigation buttons found');
return;
}
navButtons.forEach((btn, index) => {
console.log(`[UIManager] Setting up button ${index}:`, btn.dataset.tab, btn);
// Check if button already has a listener to prevent duplicates
if (btn.getAttribute('data-has-listener') === 'true') {
console.log(`[UIManager] Button ${btn.dataset.tab} already has listener, skipping`);
return;
}
btn.addEventListener('click', (e) => {
console.log(`[UIManager] Navigation button clicked: ${btn.dataset.tab}`, e);
const tab = btn.dataset.tab;
if (tab) {
this.switchTab(tab);
} else {
console.warn('[UIManager] Button clicked but no tab data found');
}
});
// Mark as having listener
btn.setAttribute('data-has-listener', 'true');
console.log(`[UIManager] Event listener added to button: ${btn.dataset.tab}`);
});
// Add a global fallback click handler for navigation buttons
document.addEventListener('click', (e) => {
if (e.target.classList.contains('nav-btn') || e.target.closest('.nav-btn')) {
const button = e.target.classList.contains('nav-btn') ? e.target : e.target.closest('.nav-btn');
const tab = button.dataset.tab;
if (tab && window.game && window.game.systems && window.game.systems.ui) {
console.log('[UI MANAGER] Global fallback: Navigation button clicked:', tab);
window.game.systems.ui.switchTab(tab);
}
}
});
// Set up UI update event listener from GameEngine
if (this.game) {
this.game.addEventListener('uiUpdate', (event) => {
this.handleUIUpdate(event.detail);
});
// if (debugLogger) debugLogger.logStep('UI update event listener set up');
}
// Modal controls
const modalClose = document.getElementById('modalClose');
const modalOverlay = document.getElementById('modalOverlay');
if (debugLogger) debugLogger.logStep('Setting up modal controls', {
modalCloseExists: !!modalClose,
modalOverlayExists: !!modalOverlay
});
if (modalClose) {
// Check if already has listener
if (modalClose.getAttribute('data-has-listener') === 'true') {
if (debugLogger) debugLogger.log('Modal close button already has listener, skipping');
} else {
modalClose.addEventListener('click', () => {
if (debugLogger) debugLogger.log('Modal close button clicked');
this.closeModal();
});
modalClose.setAttribute('data-has-listener', 'true');
}
}
if (modalOverlay) {
// Check if already has listener
if (modalOverlay.getAttribute('data-has-listener') === 'true') {
if (debugLogger) debugLogger.log('Modal overlay already has listener, skipping');
} else {
modalOverlay.addEventListener('click', (e) => {
if (e.target === modalOverlay) {
if (debugLogger) debugLogger.log('Modal overlay clicked - closing modal');
this.closeModal();
}
});
modalOverlay.setAttribute('data-has-listener', 'true');
}
}
// Quick action buttons
const claimOfflineBtn = document.getElementById('claimOfflineBtn');
if (claimOfflineBtn) {
// Check if already has listener
if (claimOfflineBtn.getAttribute('data-has-listener') === 'true') {
if (debugLogger) debugLogger.log('Claim offline button already has listener, skipping');
} else {
// if (debugLogger) debugLogger.logStep('Setting up claim offline rewards button');
claimOfflineBtn.addEventListener('click', () => {
if (debugLogger) debugLogger.log('Claim offline rewards button clicked');
this.game.systems.idleSystem.claimOfflineRewards();
});
claimOfflineBtn.setAttribute('data-has-listener', 'true');
}
}
const quickDungeonBtn = document.getElementById('quickDungeonBtn');
if (quickDungeonBtn) {
// if (debugLogger) debugLogger.logStep('Setting up quick dungeon button');
quickDungeonBtn.addEventListener('click', () => {
if (debugLogger) debugLogger.log('Quick dungeon button clicked', {
currentTab: this.currentTab,
targetTab: 'dungeons'
});
this.switchTab('dungeons');
});
}
// Settings and Discord buttons
const settingsBtn = document.getElementById('settingsBtn');
if (settingsBtn) {
// Check if already has listener
if (settingsBtn.getAttribute('data-has-listener') === 'true') {
if (debugLogger) debugLogger.log('Settings button already has listener, skipping');
} else {
// if (debugLogger) debugLogger.logStep('Setting up settings button');
settingsBtn.addEventListener('click', () => {
if (debugLogger) debugLogger.log('Settings button clicked');
this.showSettingsMenu();
});
settingsBtn.setAttribute('data-has-listener', 'true');
}
}
const discordBtn = document.getElementById('discordBtn');
if (discordBtn) {
// if (debugLogger) debugLogger.logStep('Setting up Discord button');
discordBtn.addEventListener('click', () => {
if (debugLogger) debugLogger.log('Discord button clicked');
this.showDiscordIntegration();
});
}
// Local Server button
const localServerBtn = document.getElementById('localServerBtn');
if (localServerBtn) {
// if (debugLogger) debugLogger.logStep('Setting up local server button');
localServerBtn.addEventListener('click', () => {
if (debugLogger) debugLogger.log('Local server button clicked');
this.showLocalServerControls();
});
}
// Return to Main Menu button
const returnToMenuBtn = document.getElementById('returnToMenuBtn');
if (returnToMenuBtn) {
// Check if button already has a listener to prevent duplicates
if (returnToMenuBtn.getAttribute('data-has-listener') === 'true') {
if (debugLogger) debugLogger.log('Return to menu button already has listener, skipping');
return;
}
// if (debugLogger) debugLogger.logStep('Setting up return to menu button');
returnToMenuBtn.addEventListener('click', () => {
if (debugLogger) debugLogger.log('Return to menu button clicked');
this.showReturnToMainMenuModal();
});
// Mark as having listener
returnToMenuBtn.setAttribute('data-has-listener', 'true');
}
// Setup sub-panel category buttons
const skillCatButtons = document.querySelectorAll('.skill-cat-btn');
if (debugLogger) debugLogger.logStep('Setting up skill category navigation', {
skillCatButtonsCount: skillCatButtons.length
});
skillCatButtons.forEach(btn => {
// Check if button already has a listener to prevent duplicates
if (btn.getAttribute('data-has-listener') === 'true') {
return;
}
btn.addEventListener('click', (e) => {
const category = btn.dataset.category;
if (debugLogger) debugLogger.log('Skill category button clicked', {
buttonElement: btn.tagName,
buttonText: btn.textContent,
category: category
});
this.switchSkillCategory(category);
});
// Mark button as having a listener
btn.setAttribute('data-has-listener', 'true');
});
const questTabButtons = document.querySelectorAll('.quest-tab-btn');
if (debugLogger) debugLogger.logStep('Setting up quest tab navigation', {
questTabButtonsCount: questTabButtons.length
});
questTabButtons.forEach(btn => {
// Check if button already has a listener to prevent duplicates
if (btn.getAttribute('data-has-listener') === 'true') {
return;
}
btn.addEventListener('click', (e) => {
const type = btn.dataset.type;
if (debugLogger) debugLogger.log('Quest tab button clicked', {
buttonElement: btn.tagName,
buttonText: btn.textContent,
type: type
});
this.switchQuestType(type);
});
// Mark button as having a listener
btn.setAttribute('data-has-listener', 'true');
});
const craftingCatButtons = document.querySelectorAll('.crafting-cat-btn');
if (debugLogger) debugLogger.logStep('Setting up crafting category navigation', {
craftingCatButtonsCount: craftingCatButtons.length
});
craftingCatButtons.forEach(btn => {
// Check if button already has a listener to prevent duplicates
if (btn.getAttribute('data-has-listener') === 'true') {
return;
}
btn.addEventListener('click', (e) => {
const category = e.currentTarget.dataset.category;
if (debugLogger) debugLogger.log('Crafting category button clicked', {
buttonElement: btn.tagName,
buttonText: btn.textContent,
category: category
});
this.switchCraftingCategory(category);
});
// Mark button as having a listener
btn.setAttribute('data-has-listener', 'true');
});
const shopCatButtons = document.querySelectorAll('.shop-cat-btn');
if (debugLogger) debugLogger.logStep('Setting up shop category navigation', {
shopCatButtonsCount: shopCatButtons.length
});
shopCatButtons.forEach(btn => {
// Check if button already has a listener to prevent duplicates
if (btn.getAttribute('data-has-listener') === 'true') {
return;
}
btn.addEventListener('click', (e) => {
const category = btn.dataset.category;
if (debugLogger) debugLogger.log('Shop category button clicked', {
buttonElement: btn.tagName,
buttonText: btn.textContent,
category: category
});
this.switchShopCategory(category);
});
// Mark button as having a listener
btn.setAttribute('data-has-listener', 'true');
});
// Keyboard shortcuts for tab switching removed
if (debugLogger) debugLogger.endStep('UIManager.setupEventListeners', {
modalControlsSetup: !!(modalClose || modalOverlay),
quickActionButtonsSetup: !!(claimOfflineBtn || quickDungeonBtn),
settingsButtonsSetup: !!(settingsBtn || discordBtn),
returnMenuButtonSetup: !!returnToMenuBtn,
navigationButtonsSetup: navButtons.length > 0,
navigationButtonsCount: navButtons.length,
skillCategoryButtonsSetup: skillCatButtons.length,
questTabButtonsSetup: questTabButtons.length,
craftingCategoryButtonsSetup: craftingCatButtons.length,
shopCategoryButtonsSetup: shopCatButtons.length
});
}
async saveGame() {
const debugLogger = window.debugLogger;
// if (debugLogger) debugLogger.startStep('UIManager.saveGame');
try {
// Show saving notification
// this.game.showNotification('Saving game...', 'info', 2000);
// Call the game engine's save method
await this.game.save();
// Show success notification
// this.game.showNotification('Game saved successfully!', 'success', 3000);
// if (debugLogger) debugLogger.endStep('UIManager.saveGame', {
// success: true
// });
} catch (error) {
// Show error notification
this.game.showNotification('Failed to save game!', 'error', 3000);
if (debugLogger) debugLogger.errorEvent(error, 'UIManager.saveGame');
}
}
showLocalServerControls() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.showLocalServerControls');
// Get current server status
const serverInfo = window.localServerManager ? window.localServerManager.getServerInfo() : {
isRunning: false,
status: 'Unknown',
port: null,
url: null,
connectedClients: 0,
uptime: 0
};
let content = '<div class="local-server-controls">';
content += '<div class="server-status">';
content += `<p><strong>Status:</strong> <span class="status-${serverInfo.isRunning ? 'online' : 'offline'}">${serverInfo.status}</span></p>`;
if (serverInfo.isRunning) {
content += `<p><strong>Port:</strong> ${serverInfo.port}</p>`;
content += `<p><strong>URL:</strong> <a href="${serverInfo.url}" target="_blank">${serverInfo.url}</a></p>`;
content += `<p><strong>Connected Clients:</strong> ${serverInfo.connectedClients}</p>`;
content += `<p><strong>Uptime:</strong> ${Math.floor(serverInfo.uptime)}s</p>`;
}
content += '</div>';
// Control buttons
content += '<div class="server-actions">';
if (serverInfo.isRunning) {
content += '<button class="btn btn-warning" onclick="if(window.game && window.game.systems && window.game.systems.ui) { window.game.systems.ui.stopLocalServer(); }">';
content += '<i class="fas fa-stop"></i> Stop Server';
content += '</button>';
content += '<button class="btn btn-secondary" onclick="if(window.game && window.game.systems && window.game.systems.ui) { window.game.systems.ui.restartLocalServer(); }">';
content += '<i class="fas fa-redo"></i> Restart Server';
content += '</button>';
} else {
content += '<button class="btn btn-success" onclick="if(window.game && window.game.systems && window.game.systems.ui) { window.game.systems.ui.startLocalServer(); }">';
content += '<i class="fas fa-play"></i> Start Server';
content += '</button>';
}
content += '</div>';
// Information section
content += '<div class="server-info">';
content += '<p>The local server enables singleplayer mode when external servers are unavailable. Save data is stored locally on your computer. No internet connection required for local gameplay.</p>';
content += '</div>';
content += '</div>';
this.showModal('Local Server', content);
if (debugLogger) debugLogger.endStep('UIManager.showLocalServerControls', {
serverRunning: serverInfo.isRunning,
serverPort: serverInfo.port
});
}
async startLocalServer() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.startLocalServer');
try {
if (!window.localServerManager) {
this.game.showNotification('Local server manager not available', 'error', 3000);
return;
}
const result = await window.localServerManager.startServer();
if (result.success) {
this.game.showNotification(`Local server started on port ${result.port}`, 'success', 3000);
this.closeModal();
// Refresh the modal to show updated status
setTimeout(() => {
this.showLocalServerControls();
}, 500);
} else {
this.game.showNotification(`Failed to start server: ${result.error}`, 'error', 3000);
}
if (debugLogger) debugLogger.endStep('UIManager.startLocalServer', {
success: result.success,
port: result.port
});
} catch (error) {
this.game.showNotification('Error starting local server', 'error', 3000);
if (debugLogger) debugLogger.errorEvent(error, 'UIManager.startLocalServer');
}
}
async stopLocalServer() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.stopLocalServer');
try {
if (!window.localServerManager) {
this.game.showNotification('Local server manager not available', 'error', 3000);
return;
}
const result = await window.localServerManager.stopServer();
if (result.success) {
this.game.showNotification('Local server stopped', 'success', 3000);
this.closeModal();
// Refresh the modal to show updated status
setTimeout(() => {
this.showLocalServerControls();
}, 500);
} else {
this.game.showNotification(`Failed to stop server: ${result.error}`, 'error', 3000);
}
if (debugLogger) debugLogger.endStep('UIManager.stopLocalServer', {
success: result.success
});
} catch (error) {
this.game.showNotification('Error stopping local server', 'error', 3000);
if (debugLogger) debugLogger.errorEvent(error, 'UIManager.stopLocalServer');
}
}
async restartLocalServer() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.restartLocalServer');
try {
if (!window.localServerManager) {
this.game.showNotification('Local server manager not available', 'error', 3000);
return;
}
// Stop first
await this.stopLocalServer();
// Wait a moment
await new Promise(resolve => setTimeout(resolve, 1000));
// Start again
await this.startLocalServer();
if (debugLogger) debugLogger.endStep('UIManager.restartLocalServer', {
success: true
});
} catch (error) {
this.game.showNotification('Error restarting local server', 'error', 3000);
if (debugLogger) debugLogger.errorEvent(error, 'UIManager.restartLocalServer');
}
}
returnToMainMenu() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.returnToMainMenu');
// Go directly to server selection without confirmation modal
this.showMainMenu();
this.closeModal();
if (debugLogger) debugLogger.endStep('UIManager.returnToMainMenu', {
success: true,
directToServerSelection: true
});
}
async stopLocalServer() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.stopLocalServer');
try {
if (!window.localServerManager) {
this.game.showNotification('Local server manager not available', 'error', 3000);
return;
}
const result = await window.localServerManager.stopServer();
if (result.success) {
this.game.showNotification('Local server stopped', 'success', 3000);
this.closeModal();
// Refresh the modal to show updated status
setTimeout(() => {
this.showLocalServerControls();
}, 500);
} else {
this.game.showNotification(`Failed to stop server: ${result.error}`, 'error', 3000);
}
if (debugLogger) debugLogger.endStep('UIManager.stopLocalServer', {
success: result.success
});
} catch (error) {
this.game.showNotification('Error stopping local server', 'error', 3000);
if (debugLogger) debugLogger.errorEvent(error, 'UIManager.stopLocalServer');
}
}
async restartLocalServer() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.restartLocalServer');
try {
if (!window.localServerManager) {
this.game.showNotification('Local server manager not available', 'error', 3000);
return;
}
// Stop first
await this.stopLocalServer();
// Wait a moment
await new Promise(resolve => setTimeout(resolve, 1000));
// Start again
await this.startLocalServer();
if (debugLogger) debugLogger.endStep('UIManager.restartLocalServer', {
success: true
});
} catch (error) {
this.game.showNotification('Error restarting local server', 'error', 3000);
if (debugLogger) debugLogger.errorEvent(error, 'UIManager.restartLocalServer');
}
}
showReturnToMainMenuModal() {
const debugLogger = window.debugLogger;
// Show confirmation modal instead of browser confirm dialog
let content = '<div class="confirmation-content">';
content += '<p>Are you sure you want to return to the main menu?</p>';
content += '<p><strong>Warning:</strong> Any unsaved progress will be lost.</p>';
content += '<div class="confirmation-actions">';
// Check if using fallback GameEngine
if (window.game && window.game.isFallback) {
content += '<button class="btn btn-primary" onclick="if(window.uiManager) { window.uiManager.confirmReturnToMainMenu(); }">Return to Menu</button>';
content += '<button class="btn btn-secondary" onclick="if(window.uiManager) { window.uiManager.closeModal(); }">Cancel</button>';
} else {
content += '<button class="btn btn-primary" onclick="if(window.game && window.game.systems && window.game.systems.ui) { window.game.systems.ui.confirmReturnToMainMenu(); }">Return to Menu</button>';
content += '<button class="btn btn-secondary" onclick="if(window.game && window.game.systems && window.game.systems.ui) { window.game.systems.ui.closeModal(); }">Cancel</button>';
}
content += '</div>';
content += '</div>';
this.showModal('Return to Main Menu', content);
if (debugLogger) debugLogger.endStep('UIManager.returnToMainMenu', {
success: true,
confirmationShown: true
});
}
async confirmReturnToMainMenu() {
try {
const debugLogger = window.debugLogger;
// Reset multiplayer mode when returning to main menu
if (window.smartSaveManager) {
window.smartSaveManager.setMultiplayerMode(false);
}
// Check if we're in multiplayer mode - if so, don't save locally
const isMultiplayer = window.smartSaveManager?.isMultiplayer;
// Always stop the game and clear timers regardless of mode
this.game.isRunning = false;
// Force save before stopping in multiplayer mode
if (isMultiplayer && this.game && this.game.save) {
console.log('[UI MANAGER] Force saving game before leaving multiplayer mode');
try {
await this.game.save();
console.log('[UI MANAGER] Game saved successfully before leaving server');
} catch (error) {
console.error('[UI MANAGER] Error saving game before leaving server:', error);
}
}
// Clear game logic timer
if (this.game.gameLogicTimer) {
clearInterval(this.game.gameLogicTimer);
this.game.gameLogicTimer = null;
}
// Clear auto-save timer
if (this.game.autoSaveTimer) {
clearInterval(this.game.autoSaveTimer);
this.game.autoSaveTimer = null;
}
// Stop economy system timers
if (this.game.systems.economy) {
this.game.systems.economy.stopShopRefreshTimer();
}
if (isMultiplayer) {
console.log('[UI MANAGER] Skipping local save - returning from multiplayer mode');
// Show main menu immediately
this.showMainMenu();
this.closeModal();
if (debugLogger) debugLogger.endStep('UIManager.confirmReturnToMainMenu', {
success: true,
multiplayerMode: true,
localSaveSkipped: true,
mainMenuShown: true
});
} else {
// Handle async stop properly for singleplayer mode
this.game.stop().then(() => {
try {
// if (debugLogger) debugLogger.logStep('Game saved successfully');
this.showMainMenu();
this.closeModal();
if (debugLogger) debugLogger.endStep('UIManager.confirmReturnToMainMenu', {
success: true,
gameStopped: true,
mainMenuShown: true
});
} catch (error) {
try {
if (debugLogger) debugLogger.errorEvent('UIManager.confirmReturnToMainMenu', error, {
phase: 'game_stop_and_save'
});
// Still return to menu even if save fails
this.showMainMenu();
this.closeModal();
if (debugLogger) debugLogger.endStep('UIManager.confirmReturnToMainMenu', {
success: true,
gameStopped: true,
saveError: true,
mainMenuShown: true
});
} catch (loggerError) {
console.error('[UI MANAGER] Debug logger error:', loggerError);
}
}
}).catch(error => {
try {
if (debugLogger) debugLogger.errorEvent('UIManager.confirmReturnToMainMenu', error, {
phase: 'game_stop_and_save'
});
// Still return to menu even if save fails
this.showMainMenu();
this.closeModal();
if (debugLogger) debugLogger.endStep('UIManager.confirmReturnToMainMenu', {
success: true,
gameStopped: true,
saveError: true,
mainMenuShown: true
});
} catch (loggerError) {
console.error('[UI MANAGER] Debug logger error:', loggerError);
}
});
}
} catch (error) {
console.error('[UI MANAGER] Error in confirmReturnToMainMenu:', error);
if (debugLogger) debugLogger.errorEvent('UIManager.confirmReturnToMainMenu', error, {
phase: 'main_logic'
});
}
// Always show main menu regardless of game state
try {
setTimeout(() => {
this.showMainMenu();
this.closeModal();
}, 5000); // 5 second delay to ensure full cleanup and menu readiness
if (debugLogger) debugLogger.endStep('UIManager.confirmReturnToMainMenu', {
success: true,
gameStopped: this.game ? !this.game.isRunning : false,
mainMenuShown: true
});
} catch (error) {
console.error('[UI MANAGER] Error during return to main menu:', error);
if (debugLogger) debugLogger.errorEvent('UIManager.confirmReturnToMainMenu', error, {
phase: 'return_to_main_menu'
});
}
}
showMainMenu() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.showMainMenu');
// Disconnect from server first
if (window.gameInitializer && window.gameInitializer.socket) {
window.gameInitializer.socket.disconnect();
}
const gameInterface = document.getElementById('gameInterface');
if (gameInterface) {
gameInterface.classList.add('hidden');
// if (debugLogger) debugLogger.logStep('Game interface hidden');
} else {
if (debugLogger) debugLogger.log('Game interface element not found');
}
// Show main menu
if (window.liveMainMenu) {
// if (debugLogger) debugLogger.logStep('About to show main menu using LiveMainMenu');
try {
// Show the main menu DOM element and use LiveMainMenu to show appropriate section
const mainMenuElement = document.getElementById('mainMenu');
if (mainMenuElement) {
mainMenuElement.classList.remove('hidden');
}
// Show the server section (most appropriate for returning to menu)
if (window.liveMainMenu && typeof window.liveMainMenu.showServerSection === 'function') {
window.liveMainMenu.showServerSection();
} else {
// Fallback: manually show server section
console.warn('LiveMainMenu.showServerSection not available, using fallback');
// Ensure main menu is visible (using the already declared mainMenuElement)
if (mainMenuElement) {
mainMenuElement.classList.remove('hidden');
}
const serverSection = document.getElementById('serverSection');
const loginSection = document.getElementById('loginSection');
const serverConfirmSection = document.getElementById('serverConfirmSection');
const optionsSection = document.getElementById('optionsSection');
// Hide all sections first
if (loginSection) loginSection.classList.add('hidden');
if (serverConfirmSection) serverConfirmSection.classList.add('hidden');
if (optionsSection) optionsSection.classList.add('hidden');
// Show server section
if (serverSection) {
serverSection.classList.remove('hidden');
}
// Try to refresh server list if available
if (window.liveMainMenu && typeof window.liveMainMenu.refreshServerList === 'function') {
window.liveMainMenu.refreshServerList();
}
}
// if (debugLogger) debugLogger.logStep('LiveMainMenu showServerSection() completed successfully');
} catch (error) {
// if (debugLogger) debugLogger.logStep('Error in LiveMainMenu operations', { error: error.message });
}
// Show save section specifically for returning to menu
setTimeout(() => {
try {
// Note: LiveMainMenu doesn't have save methods, so we'll just log this
if (debugLogger) debugLogger.logStep('Save section operations skipped - LiveMainMenu doesn\'t have save methods');
} catch (error) {
if (debugLogger) debugLogger.logStep('Error in save section operations', { error: error.message });
}
}, 500);
} else if (window.mainMenu) {
// Fallback to just showing the DOM element
// if (debugLogger) debugLogger.logStep('Using fallback DOM display for main menu');
try {
const mainMenuElement = document.getElementById('mainMenu');
if (mainMenuElement) {
mainMenuElement.classList.remove('hidden');
// Also show server section in fallback mode
const serverSection = document.getElementById('serverSection');
const loginSection = document.getElementById('loginSection');
const serverConfirmSection = document.getElementById('serverConfirmSection');
const optionsSection = document.getElementById('optionsSection');
// Hide all sections first
if (loginSection) loginSection.classList.add('hidden');
if (serverConfirmSection) serverConfirmSection.classList.add('hidden');
if (optionsSection) optionsSection.classList.add('hidden');
// Show server section
if (serverSection) {
serverSection.classList.remove('hidden');
}
// if (debugLogger) debugLogger.logStep('Main menu DOM element shown via fallback with server section');
}
} catch (error) {
// if (debugLogger) debugLogger.logStep('Error in fallback main menu display', { error: error.message });
}
} else {
// Final fallback: directly manipulate DOM to show server section
console.warn('Neither LiveMainMenu nor mainMenu available, using final fallback');
try {
const mainMenuElement = document.getElementById('mainMenu');
const serverSection = document.getElementById('serverSection');
const loginSection = document.getElementById('loginSection');
const serverConfirmSection = document.getElementById('serverConfirmSection');
const optionsSection = document.getElementById('optionsSection');
const gameInterface = document.getElementById('gameInterface');
// Hide game interface
if (gameInterface) {
gameInterface.classList.add('hidden');
}
// Show main menu
if (mainMenuElement) {
mainMenuElement.classList.remove('hidden');
}
// Hide all sections first
if (loginSection) loginSection.classList.add('hidden');
if (serverConfirmSection) serverConfirmSection.classList.add('hidden');
if (optionsSection) optionsSection.classList.add('hidden');
// Show server section
if (serverSection) {
serverSection.classList.remove('hidden');
}
} catch (error) {
console.error('Error in final fallback server section display', error);
}
}
if (debugLogger) debugLogger.endStep('UIManager.showMainMenu', {
success: true,
mainMenuShown: !!window.mainMenu
});
}
// Tab management
switchTab(tabName) {
if (debugLogger) debugLogger.startStep('UIManager.switchTab', {
fromTab: this.currentTab,
toTab: tabName,
sameTab: this.currentTab === tabName
});
if (this.currentTab === tabName) {
if (debugLogger) debugLogger.log('Switching to same tab, no action needed', {
tabName: tabName
});
if (debugLogger) debugLogger.endStep('UIManager.switchTab', {
success: true,
action: 'no_change_needed',
currentTab: this.currentTab
});
return;
}
const oldTab = this.currentTab;
// Update navigation buttons
const navButtons = document.querySelectorAll('.nav-btn, .bottom-nav-btn, .nav-drawer-btn');
let navButtonsUpdated = 0;
navButtons.forEach(btn => {
const wasActive = btn.classList.contains('active');
const shouldBeActive = btn.dataset.tab === tabName;
btn.classList.toggle('active', shouldBeActive);
if (wasActive !== shouldBeActive) {
navButtonsUpdated++;
}
});
if (debugLogger) debugLogger.logStep('Navigation buttons updated', {
totalButtons: navButtons.length,
buttonsUpdated: navButtonsUpdated
});
// Update tab content
const tabContents = document.querySelectorAll('.tab-content');
let tabContentsUpdated = 0;
tabContents.forEach(content => {
const wasActive = content.classList.contains('active');
const shouldBeActive = content.id === `${tabName}-tab`;
content.classList.toggle('active', shouldBeActive);
if (wasActive !== shouldBeActive) {
tabContentsUpdated++;
}
});
if (debugLogger) debugLogger.logStep('Tab contents updated', {
totalContents: tabContents.length,
contentsUpdated: tabContentsUpdated
});
this.currentTab = tabName;
this.game.state.currentTab = tabName;
if (debugLogger) debugLogger.logStep('Tab state updated', {
oldTab: oldTab,
newTab: this.currentTab,
gameStateUpdated: true
});
// Update specific tab content only if in multiplayer mode or game is actively running
if (this.shouldUpdateUI()) {
this.updateTabContent(tabName);
}
if (debugLogger) debugLogger.endStep('UIManager.switchTab', {
success: true,
oldTab: oldTab,
newTab: this.currentTab,
navButtonsUpdated: navButtonsUpdated,
tabContentsUpdated: tabContentsUpdated,
tabContentUpdated: true
});
}
updateTabContent(tabName) {
if (!this.shouldUpdateUI()) {
return;
}
if (debugLogger) debugLogger.startStep('UIManager.updateTabContent', {
tabName: tabName,
currentTab: this.currentTab
});
let contentUpdated = false;
let updateError = null;
try {
switch(tabName) {
case 'dashboard':
// if (debugLogger) debugLogger.logStep('Updating dashboard tab content');
// Dashboard is the default view, no additional content to update
contentUpdated = true;
break;
case 'dungeons':
// if (debugLogger) debugLogger.logStep('Updating dungeons tab content');
// Ensure dungeon list is generated
if (window.game && window.game.systems && window.game.systems.dungeonSystem) {
// if (debugLogger) debugLogger.logStep('Ensuring dungeon list is generated...');
try {
window.game.systems.dungeonSystem.generateDungeonList();
} catch (error) {
if (debugLogger) debugLogger.errorEvent('UIManager.updateTabContent', error, {
tabName: tabName
});
}
}
// Only regenerate dungeon list if it's empty (first time load)
const dungeonListElement = document.getElementById('dungeonList');
if (dungeonListElement && dungeonListElement.children.length === 0) {
if (this.game && this.game.systems && this.game.systems.dungeonSystem) {
this.game.systems.dungeonSystem.generateDungeonList();
} else {
if (debugLogger) debugLogger.log('Dungeon system not available');
}
}
contentUpdated = true;
break;
case 'skills':
// if (debugLogger) debugLogger.logStep('Updating skills tab content');
if (this.game && this.game.systems && this.game.systems.skillSystem) {
this.game.systems.skillSystem.updateUI();
} else {
if (debugLogger) debugLogger.log('Skill system not available');
}
contentUpdated = true;
break;
case 'base':
// if (debugLogger) debugLogger.logStep('Updating base tab content');
if (this.game && this.game.systems && this.game.systems.baseSystem) {
this.game.systems.baseSystem.updateUI();
} else {
if (debugLogger) debugLogger.log('Base system not available');
}
contentUpdated = true;
break;
case 'quests':
// if (debugLogger) debugLogger.logStep('Updating quests tab content');
if (this.game && this.game.systems && this.game.systems.questSystem) {
this.game.systems.questSystem.updateUI();
} else {
if (debugLogger) debugLogger.log('Quest system not available');
}
contentUpdated = true;
break;
case 'inventory':
// if (debugLogger) debugLogger.logStep('Updating inventory tab content');
if (this.game && this.game.systems && this.game.systems.inventory) {
this.game.systems.inventory.updateUI();
} else {
if (debugLogger) debugLogger.log('Inventory system not available');
}
contentUpdated = true;
break;
case 'shop':
// if (debugLogger) debugLogger.logStep('Updating shop tab content');
if (this.game && this.game.systems && this.game.systems.economy) {
this.game.systems.economy.updateUI();
} else {
if (debugLogger) debugLogger.log('Economy system not available');
}
contentUpdated = true;
break;
case 'crafting':
// if (debugLogger) debugLogger.logStep('Updating crafting tab content');
if (this.game && this.game.systems && this.game.systems.crafting) {
this.game.systems.crafting.updateUI();
} else {
if (debugLogger) debugLogger.log('Crafting system not available');
}
contentUpdated = true;
break;
default:
// if (debugLogger) debugLogger.log('Unknown tab name, no content updated', {
// tabName: tabName
// });
contentUpdated = false;
}
} catch (error) {
updateError = error;
if (debugLogger) debugLogger.errorEvent('UIManager.updateTabContent', error, {
tabName: tabName
});
}
if (debugLogger) debugLogger.endStep('UIManager.updateTabContent', {
tabName: tabName,
contentUpdated: contentUpdated,
updateError: updateError ? updateError.message : null
});
}
// Category switching
switchSkillCategory(category) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.switchSkillCategory', {
category: category
});
const skillCatButtons = document.querySelectorAll('.skill-cat-btn');
let buttonsUpdated = 0;
skillCatButtons.forEach(btn => {
const wasActive = btn.classList.contains('active');
const shouldBeActive = btn.dataset.category === category;
btn.classList.toggle('active', shouldBeActive);
if (wasActive !== shouldBeActive) {
buttonsUpdated++;
}
});
if (debugLogger) debugLogger.logStep('Skill category buttons updated', {
totalButtons: skillCatButtons.length,
buttonsUpdated: buttonsUpdated
});
try {
this.game.systems.skillSystem.updateUI();
if (debugLogger) debugLogger.endStep('UIManager.switchSkillCategory', {
success: true,
category: category,
buttonsUpdated: buttonsUpdated,
skillUIUpdated: true
});
} catch (error) {
if (debugLogger) debugLogger.errorEvent('UIManager.switchSkillCategory', error, {
category: category
});
if (debugLogger) debugLogger.endStep('UIManager.switchSkillCategory', {
success: false,
category: category,
error: error.message
});
}
}
switchShopCategory(category) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.switchShopCategory', {
category: category
});
const shopCatButtons = document.querySelectorAll('.shop-cat-btn');
let buttonsUpdated = 0;
shopCatButtons.forEach(btn => {
const wasActive = btn.classList.contains('active');
const shouldBeActive = btn.dataset.category === category;
btn.classList.toggle('active', shouldBeActive);
if (wasActive !== shouldBeActive) {
buttonsUpdated++;
}
});
if (debugLogger) debugLogger.logStep('Shop category buttons updated', {
totalButtons: shopCatButtons.length,
buttonsUpdated: buttonsUpdated
});
try {
// Call economy system update
this.game.systems.economy.updateUI();
// Also call global shop display function for additional debugging
if (typeof updateShopDisplay === 'function') {
updateShopDisplay();
}
if (debugLogger) debugLogger.endStep('UIManager.switchShopCategory', {
success: true,
category: category,
buttonsUpdated: buttonsUpdated,
economyUIUpdated: true
});
} catch (error) {
if (debugLogger) debugLogger.errorEvent('UIManager.switchShopCategory', error, {
category: category
});
if (debugLogger) debugLogger.endStep('UIManager.switchShopCategory', {
success: false,
category: category,
error: error.message
});
}
}
updateQuestTabs(type) {
const questTabButtons = document.querySelectorAll('.quest-tab-btn');
let buttonsUpdated = 0;
questTabButtons.forEach(btn => {
const wasActive = btn.classList.contains('active');
const shouldBeActive = btn.dataset.type === type;
btn.classList.toggle('active', shouldBeActive);
if (wasActive !== shouldBeActive) {
buttonsUpdated++;
}
});
if (debugLogger) debugLogger.logStep('Quest tab buttons updated', {
totalButtons: questTabButtons.length,
buttonsUpdated: buttonsUpdated
});
try {
// REMOVED: Client-side QuestSystem is now server-driven
// this.game.systems.questSystem.updateUI();
// Call the global quest display update function instead
if (typeof updateQuestDisplay === 'function') {
updateQuestDisplay();
}
if (debugLogger) debugLogger.endStep('UIManager.switchQuestType', {
success: true,
type: type,
buttonsUpdated: buttonsUpdated,
questUIUpdated: true
});
} catch (error) {
if (debugLogger) debugLogger.errorEvent('UIManager.switchQuestType', error, {
type: type
});
if (debugLogger) debugLogger.endStep('UIManager.switchQuestType', {
success: false,
type: type,
error: error.message
});
}
}
switchCraftingCategory(category) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.switchCraftingCategory', {
category: category
});
const craftingCatButtons = document.querySelectorAll('.crafting-cat-btn');
let buttonsUpdated = 0;
craftingCatButtons.forEach(btn => {
const wasActive = btn.classList.contains('active');
const shouldBeActive = btn.dataset.category === category;
btn.classList.toggle('active', shouldBeActive);
if (wasActive !== shouldBeActive) {
buttonsUpdated++;
}
});
if (debugLogger) debugLogger.logStep('Crafting category buttons updated', {
totalButtons: craftingCatButtons.length,
buttonsUpdated: buttonsUpdated
});
try {
this.game.systems.crafting.switchCategory(category);
if (debugLogger) debugLogger.endStep('UIManager.switchCraftingCategory', {
success: true,
category: category,
buttonsUpdated: buttonsUpdated,
craftingUIUpdated: true
});
} catch (error) {
if (debugLogger) debugLogger.errorEvent('UIManager.switchCraftingCategory', error, {
category: category
});
if (debugLogger) debugLogger.endStep('UIManager.switchCraftingCategory', {
success: false,
category: category,
error: error.message
});
}
}
// Modal management
showModal(title, content) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.showModal', {
title: title,
contentLength: content.length,
modalWasOpen: this.modalOpen
});
const modalOverlay = document.getElementById('modalOverlay');
const modalTitle = document.getElementById('modalTitle');
const modalBody = document.getElementById('modalBody');
if (!modalOverlay || !modalTitle || !modalBody) {
const missingElements = {
modalOverlay: !modalOverlay,
modalTitle: !modalTitle,
modalBody: !modalBody
};
if (debugLogger) debugLogger.endStep('UIManager.showModal', {
success: false,
reason: 'missing_modal_elements',
missingElements: missingElements
});
return;
}
const oldTitle = modalTitle.textContent;
const oldContent = modalBody.innerHTML;
modalTitle.textContent = title;
modalBody.innerHTML = content;
modalOverlay.classList.remove('hidden');
this.modalOpen = true;
if (debugLogger) debugLogger.endStep('UIManager.showModal', {
success: true,
title: title,
contentLength: content.length,
oldTitle: oldTitle,
oldContentLength: oldContent.length,
modalNowOpen: true
});
}
closeModal() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.closeModal', {
modalWasOpen: this.modalOpen
});
const modalOverlay = document.getElementById('modalOverlay');
if (modalOverlay) {
const wasHidden = modalOverlay.classList.contains('hidden');
modalOverlay.classList.add('hidden');
if (debugLogger) debugLogger.logStep('Modal overlay hidden', {
wasAlreadyHidden: wasHidden
});
} else {
if (debugLogger) debugLogger.log('Modal overlay element not found');
}
const wasModalOpen = this.modalOpen;
this.modalOpen = false;
if (debugLogger) debugLogger.endStep('UIManager.closeModal', {
success: true,
modalOverlayFound: !!modalOverlay,
wasModalOpen: wasModalOpen,
modalNowClosed: !this.modalOpen
});
}
// Force refresh all UI elements (called when server data is updated)
forceRefreshAllUI() {
if (window.smartSaveManager?.isMultiplayer) {
console.log('[UI MANAGER] Force refreshing all UI elements with server data');
// Update all resource displays
this.updateResourceDisplay();
// Update current tab content
if (this.currentTab) {
this.updateTabContent(this.currentTab);
}
// Update quest system
if (this.game && this.game.systems && this.game.systems.questSystem) {
this.game.systems.questSystem.updateUI();
}
console.log('[UI MANAGER] UI refresh completed');
}
}
// Handle UI update events from game engine
handleUIUpdate(data) {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.handleUIUpdate', data);
if (debugLogger) debugLogger.log('Handling UI update event:', data);
switch (data.type) {
case 'full':
this.updateAllUI();
break;
case 'economy':
this.updateResourceDisplay();
break;
case 'player':
this.updateResourceDisplay();
break;
case 'quests':
if (this.game && this.game.systems && this.game.systems.questSystem) {
this.game.systems.questSystem.updateUI();
}
break;
case 'dailyCountdown':
if (this.game && this.game.systems && this.game.systems.questSystem) {
this.game.systems.questSystem.updateDailyCountdown();
}
break;
case 'weeklyCountdown':
if (this.game && this.game.systems && this.game.systems.questSystem) {
this.game.systems.questSystem.updateWeeklyCountdown();
}
break;
default:
if (debugLogger) debugLogger.log('Unknown update type:', data.type);
}
if (debugLogger) debugLogger.endStep('UIManager.handleUIUpdate', {
type: data.type,
handled: true
});
}
updateAllUI() {
if (!this.shouldUpdateUI()) {
return;
}
// if (debugLogger) debugLogger.log('Updating all UI elements');
this.updateResourceDisplay();
// Update other UI components as needed
if (this.game && this.game.systems) {
// Update quest system
if (this.game.systems.questSystem) {
this.game.systems.questSystem.updateUI();
}
// Update daily countdown
if (this.game.systems.questSystem) {
this.game.systems.questSystem.updateDailyCountdown();
this.game.systems.questSystem.updateWeeklyCountdown();
}
}
// if (debugLogger) debugLogger.log('All UI elements updated');
}
// Resource display
setupResourceDisplay() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.setupResourceDisplay');
// Resource display is handled in updateResourceDisplay method
if (debugLogger) debugLogger.endStep('UIManager.setupResourceDisplay', {
setupCompleted: true
});
}
// Centralized UI update control - blocks all UI updates when not in multiplayer mode
shouldUpdateUI() {
const shouldUpdate = window.smartSaveManager?.isMultiplayer || this.game?.isRunning;
if (!shouldUpdate) {
console.log('[UI MANAGER] Blocking UI update - not in multiplayer mode');
return false;
}
return true;
}
// Centralized update resource display - all calls should go through this
updateResourceDisplay() {
if (!this.shouldUpdateUI()) {
return;
}
// In multiplayer mode, ensure we're using server data
if (window.smartSaveManager?.isMultiplayer) {
// console.log('[UI MANAGER] Updating resource display in multiplayer mode - using server data');
}
// const debugLogger = window.debugLogger;
try {
// Safety checks - return early if systems aren't available
if (!this.game || !this.game.systems) {
// if (debugLogger) debugLogger.log('Game systems not available, skipping resource display update');
return;
}
if (!this.game.systems.player || !this.game.systems.economy) {
// if (debugLogger) debugLogger.log('Player or economy system not available, skipping resource display update');
return;
}
const player = this.game.systems.player;
const economy = this.game.systems.economy;
// Additional safety checks for player properties
if (!player.stats || !player.attributes || !player.ship) {
// if (debugLogger) debugLogger.log('Player properties not fully initialized, skipping resource display update');
return;
}
// if (debugLogger) debugLogger.startStep('UIManager.updateResourceDisplay', {
// gameSystemsAvailable: !!(this.game && this.game.systems),
// playerSystemAvailable: !!(this.game && this.game.systems && this.game.systems.player),
// economySystemAvailable: !!(this.game && this.game.systems && this.game.systems.economy),
// playerStatsAvailable: !!player.stats,
// playerAttributesAvailable: !!player.attributes,
// playerShipAvailable: !!player.ship
// });
let elementsUpdated = 0;
let elementsNotFound = 0;
// Update player level with safety checks
const playerLevelElement = document.getElementById('playerLevel');
if (playerLevelElement) {
const oldLevel = playerLevelElement.textContent;
const playerLevel = player.stats.level || 1;
// console.log('[UI MANAGER] Updating player level:', { oldLevel, newLevel: playerLevel, playerStats: player.stats });
playerLevelElement.textContent = `Lv. ${playerLevel}`;
elementsUpdated++;
if (debugLogger) debugLogger.logStep('Player level updated', {
oldLevel: oldLevel,
newLevel: playerLevelElement.textContent,
playerStatsLevel: player.stats.level
});
} else {
elementsNotFound++;
// if (debugLogger) debugLogger.log('Player level element not found');
}
// Update ship level with safety checks
const shipLevelElement = document.getElementById('currentShipLevel');
if (shipLevelElement) {
const oldShipLevel = shipLevelElement.textContent;
const shipLevel = player.ship.level || player.stats.level || 1;
shipLevelElement.textContent = shipLevel;
elementsUpdated++;
if (debugLogger) debugLogger.logStep('Ship level updated', {
oldShipLevel: oldShipLevel,
newShipLevel: shipLevelElement.textContent,
playerShipLevel: player.ship.level,
playerStatsLevel: player.stats.level
});
} else {
elementsNotFound++;
if (debugLogger) debugLogger.log('Ship level element not found');
}
// Update credits with safety checks
const creditsElement = document.getElementById('credits');
if (creditsElement) {
const oldCredits = creditsElement.textContent;
const creditsAmount = economy.credits || 0;
if (oldCredits !== this.game.formatNumber(creditsAmount)) {
console.log('[UI MANAGER] Credits updated:', this.game.formatNumber(creditsAmount));
}
creditsElement.textContent = this.game.formatNumber(creditsAmount);
elementsUpdated++;
if (debugLogger) debugLogger.logStep('Credits updated', {
oldCredits: oldCredits,
newCredits: creditsElement.textContent,
economyCredits: economy.credits,
formattedCredits: this.game.formatNumber(creditsAmount)
});
} else {
elementsNotFound++;
if (debugLogger) debugLogger.log('Credits element not found');
}
// Update gems with safety checks
const gemsElement = document.getElementById('gems');
if (gemsElement) {
const oldGems = gemsElement.textContent;
const gemsAmount = economy.gems || 0;
if (oldGems !== this.game.formatNumber(gemsAmount)) {
console.log('[UI MANAGER] Gems updated:', this.game.formatNumber(gemsAmount));
}
gemsElement.textContent = this.game.formatNumber(gemsAmount);
elementsUpdated++;
if (debugLogger) debugLogger.logStep('Gems updated', {
oldGems: oldGems,
newGems: gemsElement.textContent,
economyGems: economy.gems,
formattedGems: this.game.formatNumber(gemsAmount)
});
} else {
elementsNotFound++;
if (debugLogger) debugLogger.log('Gems element not found');
}
// Update energy with safety checks
const energyElement = document.getElementById('energy');
if (energyElement) {
const oldEnergy = energyElement.textContent;
const currentEnergy = player.attributes.currentEnergy || player.attributes.energy || 0;
const maxEnergy = player.attributes.maxEnergy || 100;
const newEnergy = `${currentEnergy}/${maxEnergy}`;
if (oldEnergy !== newEnergy) {
console.log('[UI MANAGER] Energy updated:', newEnergy);
}
energyElement.textContent = newEnergy;
elementsUpdated++;
if (debugLogger) debugLogger.logStep('Energy updated', {
oldEnergy: oldEnergy,
newEnergy: energyElement.textContent,
currentEnergy: currentEnergy,
maxEnergy: maxEnergy,
playerEnergy: player.attributes.energy,
playerMaxEnergy: player.attributes.maxEnergy
});
} else {
elementsNotFound++;
if (debugLogger) debugLogger.log('Energy element not found');
}
// Update play time (keep this here as it's part of the core resource display)
const playTimeElement = document.getElementById('playTime');
if (playTimeElement) {
const playTimeMs = player.stats.playTime || 0;
playTimeElement.textContent = this.formatPlayTime(playTimeMs);
elementsUpdated++;
}
// Update player stats panel
const playerLevelDisplayElement = document.getElementById('playerLevelDisplay');
if (playerLevelDisplayElement) {
playerLevelDisplayElement.textContent = player.stats.level || 1;
elementsUpdated++;
}
const playerXPElement = document.getElementById('playerXP');
if (playerXPElement) {
const currentXP = player.stats.experience || 0;
const xpToNext = player.stats.experienceToNext || 100;
playerXPElement.textContent = `${currentXP} / ${xpToNext}`;
elementsUpdated++;
}
const skillPointsElement = document.getElementById('skillPoints');
if (skillPointsElement) {
skillPointsElement.textContent = player.stats.skillPoints || 0;
elementsUpdated++;
}
const totalXPElement = document.getElementById('totalXP');
if (totalXPElement) {
totalXPElement.textContent = this.game.formatNumber(player.stats.totalXP || 0);
elementsUpdated++;
}
const questsCompletedElement = document.getElementById('questsCompleted');
if (questsCompletedElement) {
// Use player stats from server first, then quest system stats as fallback
const questsCompleted = player.stats.questsCompleted ||
(this.game.systems.questSystem?.stats?.questsCompleted) || 0;
questsCompletedElement.textContent = questsCompleted;
elementsUpdated++;
}
const lastLoginElement = document.getElementById('lastLogin');
if (lastLoginElement) {
const lastLogin = player.stats.lastLogin;
if (lastLogin) {
const loginDate = new Date(lastLogin);
lastLoginElement.textContent = loginDate.toLocaleDateString() + ' ' + loginDate.toLocaleTimeString();
} else {
lastLoginElement.textContent = 'Never';
}
elementsUpdated++;
}
// Update all player stats
this.updatePlayerStats();
if (debugLogger) debugLogger.endStep('UIManager.updateResourceDisplay', {
elementsUpdated: elementsUpdated,
elementsNotFound: elementsNotFound,
playerLevel: player.stats.level || 1,
shipLevel: player.ship.level || player.stats.level || 1,
credits: economy.credits || 0,
gems: economy.gems || 0,
currentEnergy: Math.floor(player.attributes.energy || 0),
maxEnergy: Math.floor(player.attributes.maxEnergy || 100)
});
} catch (error) {
if (debugLogger) debugLogger.log('Error in updateResourceDisplay', {
error: error.message,
stack: error.stack
});
}
}
formatPlayTime(milliseconds) {
// Format play time showing only the two most relevant units
const totalSeconds = Math.floor(milliseconds / 1000);
if (totalSeconds < 60) {
// Less than 1 minute: show seconds only
return `${totalSeconds}s`;
} else if (totalSeconds < 3600) {
// Less than 1 hour: show minutes and seconds
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
return `${minutes}m ${seconds}s`;
} else if (totalSeconds < 86400) {
// Less than 1 day: show hours and minutes
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
return `${hours}h ${minutes}m`;
} else {
// 1 day or more: show days and hours
const days = Math.floor(totalSeconds / 86400);
const hours = Math.floor((totalSeconds % 86400) / 3600);
return `${days}d ${hours}h`;
}
}
updatePlayerStats() {
// Update just the player stats panel (excluding those handled in updateResourceDisplay)
if (!this.game || !this.game.systems || !this.game.systems.player) {
return;
}
const player = this.game.systems.player;
let elementsUpdated = 0;
// Update player stats panel
const playerLevelDisplayElement = document.getElementById('playerLevelDisplay');
if (playerLevelDisplayElement) {
playerLevelDisplayElement.textContent = player.stats.level || 1;
elementsUpdated++;
}
const playerXPElement = document.getElementById('playerXP');
if (playerXPElement) {
const currentXP = player.stats.experience || 0;
const xpToNext = player.stats.experienceToNext || 100;
playerXPElement.textContent = `${currentXP} / ${xpToNext}`;
elementsUpdated++;
}
const skillPointsElement = document.getElementById('skillPoints');
if (skillPointsElement) {
skillPointsElement.textContent = player.stats.skillPoints || 0;
elementsUpdated++;
}
const totalXPElement = document.getElementById('totalXP');
if (totalXPElement) {
totalXPElement.textContent = this.game.formatNumber(player.stats.totalXP || 0);
elementsUpdated++;
}
const questsCompletedElement = document.getElementById('questsCompleted');
if (questsCompletedElement) {
// Use player stats from server first, then quest system stats as fallback
const questsCompleted = player.stats.questsCompleted ||
(this.game.systems.questSystem?.stats?.questsCompleted) || 0;
questsCompletedElement.textContent = questsCompleted;
elementsUpdated++;
}
const lastLoginElement = document.getElementById('lastLogin');
if (lastLoginElement) {
const lastLogin = player.stats.lastLogin;
if (lastLogin) {
const loginDate = new Date(lastLogin);
lastLoginElement.textContent = loginDate.toLocaleDateString() + ' ' + loginDate.toLocaleTimeString();
} else {
lastLoginElement.textContent = 'Never';
}
elementsUpdated++;
}
// Update stats moved from Idle Progress card
const totalKillsElement = document.getElementById('totalKills');
if (totalKillsElement) {
totalKillsElement.textContent = this.game.formatNumber(player.stats.totalKills || 0);
elementsUpdated++;
}
const dungeonsClearedElement = document.getElementById('dungeonsCleared');
if (dungeonsClearedElement) {
dungeonsClearedElement.textContent = player.stats.dungeonsCleared || 0;
elementsUpdated++;
}
// DISABLED: Reduce console spam for quest debugging
// console.log(`[UI MANAGER] Player stats updated: ${elementsUpdated} elements`);
}
updateUI() {
// const debugLogger = window.debugLogger;
// if (debugLogger) debugLogger.startStep('UIManager.updateUI', {
// currentTab: this.currentTab,
// modalOpen: this.modalOpen
// });
// Update resource display only if in multiplayer mode or game is actively running
if (this.shouldUpdateUI()) {
// if (debugLogger) debugLogger.logStep('Updating resource display');
this.updateResourceDisplay();
}
// Update tab content only if in multiplayer mode or game is actively running
if (this.shouldUpdateUI()) {
// if (debugLogger) debugLogger.logStep('Updating tab content', {
// currentTab: this.currentTab
// });
this.updateTabContent(this.currentTab);
}
// if (debugLogger) debugLogger.endStep('UIManager.updateUI', {
// resourceDisplayUpdated: true,
// tabContentUpdated: true,
// currentTab: this.currentTab
// });
}
// Menus
showSettingsMenu() {
// Clear any existing modal content first
const modalBody = document.getElementById('modalBody');
if (modalBody) {
modalBody.innerHTML = '';
}
// Get current auto-save setting
const currentAutoSaveInterval = localStorage.getItem('autoSaveInterval') || '5';
let content = '<div class="settings-menu">';
content += '<h3>Game Settings</h3>';
content += '<div class="settings-section">';
content += '<h4>Auto-Save Settings</h4>';
content += '<p>Configure how often your game automatically saves:</p>';
content += '<div class="setting-group">';
content += '<label for="autoSaveInterval">Auto-Save Interval:</label>';
content += '<select id="autoSaveInterval" class="setting-select">';
content += '<option value="1" ' + (currentAutoSaveInterval === '1' ? 'selected' : '') + '>1 minute</option>';
content += '<option value="5" ' + (currentAutoSaveInterval === '5' ? 'selected' : '') + '>5 minutes</option>';
content += '<option value="10" ' + (currentAutoSaveInterval === '10' ? 'selected' : '') + '>10 minutes</option>';
content += '<option value="15" ' + (currentAutoSaveInterval === '15' ? 'selected' : '') + '>15 minutes</option>';
content += '<option value="30" ' + (currentAutoSaveInterval === '30' ? 'selected' : '') + '>30 minutes</option>';
content += '</select>';
content += '</div>';
content += '<div class="setting-actions">';
content += '<button class="btn btn-primary" onclick="if(window.game && window.game.systems) window.game.systems.ui.saveAutoSaveSettings()">Save Settings</button>';
content += '</div>';
content += '</div>';
content += '<div class="settings-section">';
content += '<h4>Reset Options</h4>';
content += '<div class="settings-description">';
content += '<p>Choose your reset option:</p>';
content += '</div>';
content += '<div class="reset-options">';
content += `
<div class="reset-option">
<h4>Reset Character</h4>
<p>Reset your character to level 1 and clear progress, but keep your inventory items.</p>
<ul>
<li>Character level and stats reset</li>
<li>Skills and abilities reset</li>
<li>Quest progress cleared</li>
<li>Economy reset to starting values</li>
<li>Inventory items preserved</li>
</ul>
<button class="btn btn-danger" onclick="if(window.game && window.game.systems) window.game.systems.ui.confirmResetCharacter()">Reset Character</button>
</div>
<div class="reset-option">
<h4>Hard Reset</h4>
<p>Completely clear ALL saved data including inventory, settings, and local storage.</p>
<ul>
<li>Character progress and stats</li>
<li>Inventory and items</li>
<li>Quest progress</li>
<li>Settings and preferences</li>
<li>All local application data</li>
</ul>
<button class="btn btn-warning" onclick="if(window.game && window.game.systems) window.game.systems.ui.confirmHardReset()">Hard Reset</button>
</div>
`;
content += '</div>';
content += '<div class="settings-actions">';
content += '<button class="btn btn-secondary" onclick="if(window.game && window.game.systems) window.game.systems.ui.closeModal()">Close</button>';
content += '</div>';
content += '</div>';
// Force modal update with timestamp to prevent caching
const timestamp = Date.now();
this.showModal('Settings', content + `<!-- Updated: ${timestamp} -->`);
}
saveAutoSaveSettings() {
const autoSaveInterval = document.getElementById('autoSaveInterval');
if (autoSaveInterval) {
const selectedInterval = autoSaveInterval.value;
localStorage.setItem('autoSaveInterval', selectedInterval);
// Update game engine auto-save if game is running
if (window.game && window.game.isRunning) {
window.game.updateAutoSaveInterval(parseInt(selectedInterval));
}
this.game.showNotification(`Auto-save interval set to ${selectedInterval} minute(s)`, 'success', 3000);
}
this.closeModal();
}
confirmResetCharacter() {
let content = '<div class="confirmation-dialog">';
content += '<h3>Reset Character</h3>';
content += '<p><strong>Warning:</strong> This will reset your character to level 1 and clear all progress, but will keep your settings and preferences.</p>';
content += '<ul>';
content += '<li>Character level and stats reset</li>';
content += '<li>Skills and abilities reset</li>';
content += '<li>Quest progress cleared</li>';
content += '<li>Economy reset to starting values</li>';
content += '<li>Inventory items preserved</li>';
content += '</ul>';
content += '<div class="confirmation-actions">';
content += '<button class="btn btn-danger" onclick="if(window.game && window.game.systems) window.game.systems.ui.resetCharacter()">Reset Character</button>';
content += '<button class="btn btn-secondary" onclick="if(window.game && window.game.systems) window.game.systems.ui.closeModal()">Cancel</button>';
content += '</div>';
content += '</div>';
this.showModal('Confirm Reset', content);
}
confirmHardReset() {
let content = '<div class="reset-confirmation">';
content += '<h3>Hard Reset - Clear All Data</h3>';
content += '<p><strong>Warning:</strong> This will completely clear ALL saved data including:</p>';
content += '<ul>';
content += '<li>Character progress and stats</li>';
content += '<li>Inventory and items</li>';
content += '<li>Quest progress</li>';
content += '<li>Settings and preferences</li>';
content += '<li>All local application data</li>';
content += '</ul>';
content += '<p>This will completely reset the application to its initial state. This action cannot be undone!</p>';
content += '<div class="confirmation-actions">';
content += '<button class="btn btn-danger" onclick="if(window.game && window.game.systems) window.game.systems.ui.hardReset()">Hard Reset</button>';
content += '<button class="btn btn-secondary" onclick="if(window.game && window.game.systems) window.game.systems.ui.closeModal()">Cancel</button>';
content += '</div>';
content += '</div>';
this.showModal('Confirm Hard Reset', content);
}
resetCharacter() {
// Reset character data only
if (this.game && this.game.systems) {
// Reset player to initial state
if (this.game.systems.player) {
this.game.systems.player.resetToLevel1();
} else {
}
// Reset economy - only reset if not in multiplayer mode
if (this.game.systems.economy && !window.smartSaveManager?.isMultiplayer) {
this.game.systems.economy.credits = 1000;
this.game.systems.economy.gems = 10;
}
// Keep inventory intact for soft reset (only reset max slots if needed)
if (this.game.systems.inventory) {
// Preserve items, only reset max slots to default
if (this.game.systems.inventory.maxSlots !== 20) {
this.game.systems.inventory.maxSlots = 20;
}
}
// Clear quest progress
if (this.game.systems.questSystem) {
this.game.systems.questSystem.mainQuests.forEach(quest => {
quest.status = quest.id === 'tutorial_complete' ? 'available' : 'locked';
quest.objectives.forEach(obj => obj.current = 0);
});
this.game.systems.questSystem.activeQuests = [];
this.game.systems.questSystem.completedQuests = [];
this.game.systems.questSystem.failedQuests = [];
}
// Clear dungeon progress
if (this.game.systems.dungeonSystem) {
this.game.systems.dungeonSystem.stats = {
dungeonsAttempted: 0,
dungeonsCompleted: 0,
totalEnemiesDefeated: 0,
bestTime: Infinity,
totalLootEarned: 0
};
this.game.systems.dungeonSystem.currentDungeon = null;
this.game.systems.dungeonSystem.currentRoom = null;
this.game.systems.dungeonSystem.isExploring = false;
}
// Reset skills
if (this.game.systems.skillSystem) {
this.game.systems.skillSystem.skills = {};
}
// Force UI update after reset
if (this.game.systems.player) {
this.game.systems.player.updateUI();
}
// Save the reset state
this.game.saveGame();
this.game.showNotification('Character reset successfully!', 'success', 5000);
this.closeModal();
// Clear any modal content to prevent old panel showing
const modalBody = document.getElementById('modalBody');
if (modalBody) {
modalBody.innerHTML = '';
}
// Force close any open modals
const modalOverlay = document.getElementById('modalOverlay');
if (modalOverlay) {
modalOverlay.classList.add('hidden');
}
// Reload the page to refresh UI
setTimeout(() => {
window.location.reload();
}, 1000);
}
}
hardReset() {
// Force cache busting check
const version = Date.now();
// Clear in-memory game data first
if (this.game && this.game.systems) {
// Clear inventory data
if (this.game.systems.inventory) {
this.game.systems.inventory.items = [];
this.game.systems.inventory.equipment = {
weapon: null,
armor: null,
engine: null,
shield: null,
accessory: null
};
}
// Clear player data
if (this.game.systems.player) {
this.game.systems.player.stats = {
level: 1,
experience: 0,
experienceToNext: 100,
skillPoints: 0,
totalKills: 0,
dungeonsCleared: 0,
playTime: 0,
lastLogin: Date.now(),
tutorialDungeonCompleted: false
};
this.game.systems.player.attributes = {
health: 100,
maxHealth: 100,
energy: 100,
maxEnergy: 100,
attack: 10,
defense: 5,
speed: 10,
criticalChance: 0.05,
criticalDamage: 1.5
};
}
// Clear economy data - only reset if not in multiplayer mode
if (this.game.systems.economy && !window.smartSaveManager?.isMultiplayer) {
this.game.systems.economy.credits = 1000;
this.game.systems.economy.gems = 10;
}
// Clear quest progress including dailies
if (this.game.systems.questSystem) {
try {
// Reset main quests
if (this.game.systems.questSystem.mainQuests && Array.isArray(this.game.systems.questSystem.mainQuests)) {
this.game.systems.questSystem.mainQuests.forEach(quest => {
if (quest && quest.objectives) {
quest.status = quest.id === 'tutorial_complete' ? 'available' : 'locked';
quest.objectives.forEach(obj => obj.current = 0);
}
});
}
// Reset daily quests - force all to available regardless of current status
if (this.game.systems.questSystem.dailyQuests && Array.isArray(this.game.systems.questSystem.dailyQuests)) {
this.game.systems.questSystem.dailyQuests.forEach(quest => {
if (quest && quest.objectives) {
quest.status = 'available';
quest.objectives.forEach(obj => obj.current = 0);
}
});
}
// Reset procedural quests
if (this.game.systems.questSystem.proceduralQuests && Array.isArray(this.game.systems.questSystem.proceduralQuests)) {
this.game.systems.questSystem.proceduralQuests.forEach(quest => {
if (quest && quest.objectives) {
quest.status = 'available';
quest.objectives.forEach(obj => obj.current = 0);
}
});
}
} catch (error) {
// Continue with reset even if quest clearing fails
}
// Force clear all quest tracking arrays
this.game.systems.questSystem.activeQuests = [];
this.game.systems.questSystem.completedQuests = [];
this.game.systems.questSystem.failedQuests = [];
// Clear any quest timers or intervals
if (this.game.systems.questSystem.dailyCountdownInterval) {
clearInterval(this.game.systems.questSystem.dailyCountdownInterval);
this.game.systems.questSystem.dailyCountdownInterval = null;
}
if (this.game.systems.questSystem.weeklyCountdownInterval) {
clearInterval(this.game.systems.questSystem.weeklyCountdownInterval);
this.game.systems.questSystem.weeklyCountdownInterval = null;
}
// Reset quest statistics
this.game.systems.questSystem.stats = {
questsCompleted: 0,
dailyQuestsCompleted: 0,
totalRewardsEarned: { credits: 0, experience: 0, gems: 0 },
lastDailyReset: Date.now()
};
// Force quest availability check to ensure clean state
this.game.systems.questSystem.checkQuestAvailability();
}
}
// Clear ALL browser storage data
localStorage.clear();
sessionStorage.clear();
// Clear any IndexedDB data if present
if (window.indexedDB) {
const databases = ['galaxy_strike_online', 'game_data', 'player_data'];
databases.forEach(dbName => {
indexedDB.deleteDatabase(dbName);
});
}
// Clear any cookies for this domain
document.cookie.split(";").forEach(function(c) {
document.cookie = c.replace(/^ +/, "").replace(/=.*/, "=;expires=" + new Date().toUTCString() + ";path=/");
});
// Clear service workers if present (with proper error handling)
if ('serviceWorker' in navigator) {
try {
navigator.serviceWorker.getRegistrations().then(function(registrations) {
registrations.forEach(function(registration) {
try {
registration.unregister();
} catch (error) {
}
});
}).catch(function(error) {
});
} catch (error) {
}
}
// Clear application cache if present (with fallback for older browsers)
if ('caches' in window) {
try {
caches.keys().then(function(cacheNames) {
return Promise.all(cacheNames.map(function(cacheName) {
return caches.delete(cacheName);
}));
}).then(function() {
}).catch(function(error) {
});
} catch (error) {
}
}
// Force garbage collection to clear RAM cache
if (window.gc) {
window.gc();
}
// Clear any global variables that might hold game data
if (window.game) {
window.game = null;
}
this.game.showNotification('All data cleared successfully! The page will reload...', 'success', 5000);
this.closeModal();
// Force complete page reload with aggressive cache busting
setTimeout(() => {
// Add multiple cache-busting parameters
const timestamp = Date.now();
const random = Math.random().toString(36).substring(7);
window.location.href = window.location.origin + window.location.pathname + '?t=' + timestamp + '&r=' + random + '&force=1';
}, 1000);
}
showDiscordIntegration() {
let content = '<div class="discord-integration">';
content += '<h3>Discord Integration</h3>';
content += '<p>Connect your game to Discord for real-time updates and notifications!</p>';
content += '<div class="discord-features">';
content += '<h4>Features:</h4>';
content += '<ul>';
content += '<li>Real-time achievement notifications</li>';
content += '<li>Dungeon completion alerts</li>';
content += '<li>Level up notifications</li>';
content += '<li>Daily quest reminders</li>';
content += '<li>Statistics tracking</li>';
content += '</ul>';
content += '</div>';
content += '<div class="discord-setup">';
content += '<h4>Setup Instructions:</h4>';
content += '<ol>';
content += '<li>Create a Discord server or use an existing one</li>';
content += '<li>Create a webhook URL in server settings</li>';
content += '<li>Enter the webhook URL below</li>';
content += '<li>Test the connection</li>';
content += '</ol>';
content += '</div>';
content += '<div class="discord-config">';
content += '<input type="url" placeholder="Enter Discord webhook URL..." id="discordWebhookUrl">';
content += '<button class="btn btn-primary" onclick="if(window.game && window.game.systems) window.game.systems.ui.testDiscordWebhook()">Test Connection</button>';
content += '<button class="btn btn-success" onclick="if(window.game && window.game.systems) window.game.systems.ui.saveDiscordWebhook()">Save Integration</button>';
content += '</div>';
content += '</div>';
this.showModal('Discord Integration', content);
}
testDiscordWebhook() {
const urlInput = document.getElementById('discordWebhookUrl');
const webhookUrl = urlInput?.value;
if (!webhookUrl) {
this.game.showNotification('Please enter a webhook URL', 'error', 3000);
return;
}
// Test webhook (would need actual webhook implementation)
this.game.showNotification('Testing Discord webhook...', 'info', 2000);
// Simulate webhook test
setTimeout(() => {
this.game.showNotification('Discord webhook test successful!', 'success', 3000);
}, 2000);
}
saveDiscordWebhook() {
const urlInput = document.getElementById('discordWebhookUrl');
const webhookUrl = urlInput?.value;
if (!webhookUrl) {
this.game.showNotification('Please enter a webhook URL', 'error', 3000);
return;
}
// Save webhook URL
localStorage.setItem('discordWebhookUrl', webhookUrl);
this.game.systems.player.settings.discordIntegration = true;
this.game.showNotification('Discord integration saved!', 'success', 3000);
this.closeModal();
}
// Notification display
showNotification(message, type = 'info', duration = 3000) {
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.textContent = message;
// Position notification
notification.style.top = `${20 + (this.notifications.length * 70)}px`;
document.body.appendChild(notification);
this.notifications.push(notification);
// Auto remove
setTimeout(() => {
this.removeNotification(notification);
}, duration);
// Manual close on click
notification.addEventListener('click', () => {
this.removeNotification(notification);
});
}
removeNotification(notification) {
const index = this.notifications.indexOf(notification);
if (index > -1) {
this.notifications.splice(index, 1);
}
notification.style.opacity = '0';
notification.style.transform = 'translateX(100%)';
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
// Reposition remaining notifications
this.repositionNotifications();
}, 300);
}
repositionNotifications() {
this.notifications.forEach((notification, index) => {
notification.style.top = `${20 + (index * 70)}px`;
});
}
// Loading states
showLoading(elementId, message = 'Loading...') {
const element = document.getElementById(elementId);
if (element) {
element.innerHTML = `
<div class="loading-state">
<div class="loading-spinner"></div>
<p>${message}</p>
</div>
`;
}
}
hideLoading(elementId, content) {
const element = document.getElementById(elementId);
if (element) {
element.innerHTML = content || '';
}
}
checkAndStartTutorial() {
const debugLogger = window.debugLogger;
if (debugLogger) debugLogger.startStep('UIManager.checkAndStartTutorial');
try {
// Check if First Steps quest is active and tutorial dungeon hasn't been completed
const questSystem = this.game.systems.questSystem;
const player = this.game.systems.player;
if (!questSystem || !player) {
if (debugLogger) debugLogger.logStep('Quest system or player not available');
return;
}
const firstStepsQuest = questSystem.findQuest('tutorial_complete');
const tutorialCompleted = player.stats.tutorialDungeonCompleted;
if (debugLogger) debugLogger.logStep('Checking tutorial conditions', {
firstStepsQuestFound: !!firstStepsQuest,
questStatus: firstStepsQuest ? firstStepsQuest.status : 'not_found',
tutorialCompleted: tutorialCompleted
});
// Start tutorial dungeon if:
// 1. First Steps quest exists and is active
// 2. Tutorial dungeon hasn't been completed
if (firstStepsQuest &&
firstStepsQuest.status === 'active' &&
!tutorialCompleted) {
if (debugLogger) debugLogger.logStep('Auto-starting tutorial dungeon');
// Switch to dungeons tab
this.switchTab('dungeons');
// Start tutorial dungeon after a short delay to let UI update
setTimeout(() => {
if (this.game.systems.dungeonSystem) {
this.game.systems.dungeonSystem.startTutorialDungeon();
}
}, 1000);
}
if (debugLogger) debugLogger.endStep('UIManager.checkAndStartTutorial');
} catch (error) {
if (debugLogger) debugLogger.errorEvent(error, 'UIManager.checkAndStartTutorial');
}
}
// Notification system
showNotification(message, type = 'info', duration = 3000) {
const notification = document.createElement('div');
notification.className = `notification notification-${type}`;
notification.textContent = message;
document.body.appendChild(notification);
this.notifications.push(notification);
// Auto-remove notification after duration
setTimeout(() => {
this.removeNotification(notification);
}, duration);
}
removeNotification(notification) {
const index = this.notifications.indexOf(notification);
if (index > -1) {
this.notifications.splice(index, 1);
}
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}
// Utility methods
formatNumber(num) {
return this.game.formatNumber(num);
}
formatTime(milliseconds) {
return this.game.formatTime(milliseconds);
}
createProgressBar(current, max, showText = true) {
const percent = Math.min(100, (current / max) * 100);
const text = showText ? `<span>${current}/${max}</span>` : '';
return `
<div class="progress-bar">
<div class="progress-fill" style="width: ${percent}%"></div>
${text}
</div>
`;
}
createButton(text, className = 'btn-primary', onClick = '', disabled = false) {
return `
<button class="btn ${className} ${disabled ? 'disabled' : ''}"
onclick="${onClick}"
${disabled ? 'disabled' : ''}>
${text}
</button>
`;
}
// Update dungeon UI when player enters/exits dungeons
updateDungeonUI(dungeonData) {
console.log('[UI MANAGER] Updating dungeon UI:', dungeonData);
console.log('[UI MANAGER] Dungeon state check:', {
isExploring: dungeonData.isExploring,
hasCurrentDungeon: !!dungeonData.currentDungeon,
hasCurrentRoom: !!dungeonData.currentRoom,
progress: dungeonData.progress,
dungeonViewElement: !!document.getElementById('dungeonView')
});
// Check if dungeon is completed (progress = -1)
if (dungeonData.progress === -1 || !dungeonData.isExploring) {
// Player is not in dungeon or dungeon is completed - show normal dungeon list
console.log('[UI MANAGER] Player not exploring or dungeon completed, showing dungeon list');
this.switchTab('dungeons');
return;
}
// Player is in dungeon - show exploration interface
const dungeonView = document.getElementById('dungeonView');
if (dungeonView) {
console.log('[UI MANAGER] Found dungeonView element, creating exploration UI');
const dungeon = dungeonData.currentDungeon;
const progress = dungeonData.progress || 0;
const currentRoom = dungeonData.currentRoom;
let content = `
<div class="dungeon-exploration">
<div class="dungeon-header">
<h3>${dungeon.dungeonId} Dungeon</h3>
<div class="dungeon-progress">
<span>Progress: Room ${progress + 1}</span>
</div>
<button class="btn btn-danger" onclick="if(window.game && window.game.systems && window.game.systems.dungeonSystem) window.game.systems.dungeonSystem.exitDungeon()">
Exit Dungeon
</button>
</div>
`;
if (currentRoom) {
content += `
<div class="current-room">
<h4>${currentRoom.name}</h4>
<p>${currentRoom.description}</p>
<div class="room-enemies">
<h5>Enemies:</h5>
${currentRoom.enemies && currentRoom.enemies.length > 0 ?
currentRoom.enemies.map((enemy, index) => {
console.log(`[UI MANAGER] Rendering enemy ${index}:`, enemy);
return `
<div class="enemy-info">
<strong>${enemy.name || 'Unknown Enemy'}</strong>
<div>HP: ${enemy.health || '??'} | ATK: ${enemy.attack || '??'} | DEF: ${enemy.defense || '??'}</div>
</div>
`}).join('') :
'<p>No enemies in this room</p>'
}
</div>
${currentRoom.enemies && currentRoom.enemies.length > 0 ?
`<button class="btn btn-primary" onclick="if(window.game && window.game.systems && window.game.systems.dungeonSystem) window.game.systems.dungeonSystem.processEncounter()">
Start Combat
</button>` :
`<button class="btn btn-success" onclick="if(window.game && window.game.systems && window.game.systems.dungeonSystem) window.game.systems.dungeonSystem.moveToNextRoom()">
Continue to Next Room
</button>`
}
</div>
`;
} else {
content += `
<div class="room-loading">
<p>Loading next room...</p>
<button class="btn btn-primary" onclick="if(window.game && window.game.systems && window.game.systems.dungeonSystem) window.game.systems.dungeonSystem.processEncounter()">
Start First Room
</button>
</div>
`;
}
content += '</div>';
dungeonView.innerHTML = content;
console.log('[UI MANAGER] Dungeon exploration UI applied successfully');
} else {
console.error('[UI MANAGER] dungeonView element not found!');
}
}
}
// Export UIManager to global scope
if (typeof window !== 'undefined') {
window.UIManager = UIManager;
}