/** * 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 = '
'; content += '
'; content += `

Status: ${serverInfo.status}

`; if (serverInfo.isRunning) { content += `

Port: ${serverInfo.port}

`; content += `

URL: ${serverInfo.url}

`; content += `

Connected Clients: ${serverInfo.connectedClients}

`; content += `

Uptime: ${Math.floor(serverInfo.uptime)}s

`; } content += '
'; // Control buttons content += '
'; if (serverInfo.isRunning) { content += ''; content += ''; } else { content += ''; } content += '
'; // Information section content += '
'; content += '

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.

'; content += '
'; content += '
'; 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 = '
'; content += '

Are you sure you want to return to the main menu?

'; content += '

Warning: Any unsaved progress will be lost.

'; content += '
'; // Check if using fallback GameEngine if (window.game && window.game.isFallback) { content += ''; content += ''; } else { content += ''; content += ''; } content += '
'; content += '
'; 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 = '
'; content += '

Game Settings

'; content += '
'; content += '

Auto-Save Settings

'; content += '

Configure how often your game automatically saves:

'; content += '
'; content += ''; content += ''; content += '
'; content += '
'; content += ''; content += '
'; content += '
'; content += '
'; content += '

Reset Options

'; content += '
'; content += '

Choose your reset option:

'; content += '
'; content += '
'; content += `

Reset Character

Reset your character to level 1 and clear progress, but keep your inventory items.

  • Character level and stats reset
  • Skills and abilities reset
  • Quest progress cleared
  • Economy reset to starting values
  • Inventory items preserved

Hard Reset

Completely clear ALL saved data including inventory, settings, and local storage.

  • Character progress and stats
  • Inventory and items
  • Quest progress
  • Settings and preferences
  • All local application data
`; content += '
'; content += '
'; content += ''; content += '
'; content += '
'; // Force modal update with timestamp to prevent caching const timestamp = Date.now(); this.showModal('Settings', content + ``); } 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 = '
'; content += '

Reset Character

'; content += '

Warning: This will reset your character to level 1 and clear all progress, but will keep your settings and preferences.

'; content += ''; content += '
'; content += ''; content += ''; content += '
'; content += '
'; this.showModal('Confirm Reset', content); } confirmHardReset() { let content = '
'; content += '

Hard Reset - Clear All Data

'; content += '

Warning: This will completely clear ALL saved data including:

'; content += ''; content += '

This will completely reset the application to its initial state. This action cannot be undone!

'; content += '
'; content += ''; content += ''; content += '
'; content += '
'; 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 = '
'; content += '

Discord Integration

'; content += '

Connect your game to Discord for real-time updates and notifications!

'; content += '
'; content += '

Features:

'; content += '
    '; content += '
  • Real-time achievement notifications
  • '; content += '
  • Dungeon completion alerts
  • '; content += '
  • Level up notifications
  • '; content += '
  • Daily quest reminders
  • '; content += '
  • Statistics tracking
  • '; content += '
'; content += '
'; content += '
'; content += '

Setup Instructions:

'; content += '
    '; content += '
  1. Create a Discord server or use an existing one
  2. '; content += '
  3. Create a webhook URL in server settings
  4. '; content += '
  5. Enter the webhook URL below
  6. '; content += '
  7. Test the connection
  8. '; content += '
'; content += '
'; content += '
'; content += ''; content += ''; content += ''; content += '
'; content += '
'; 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 = `

${message}

`; } } 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 ? `${current}/${max}` : ''; return `
${text}
`; } createButton(text, className = 'btn-primary', onClick = '', disabled = false) { return ` `; } // 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 = `

${dungeon.dungeonId} Dungeon

Progress: Room ${progress + 1}
`; if (currentRoom) { content += `

${currentRoom.name}

${currentRoom.description}

Enemies:
${currentRoom.enemies && currentRoom.enemies.length > 0 ? currentRoom.enemies.map((enemy, index) => { console.log(`[UI MANAGER] Rendering enemy ${index}:`, enemy); return `
${enemy.name || 'Unknown Enemy'}
HP: ${enemy.health || '??'} | ATK: ${enemy.attack || '??'} | DEF: ${enemy.defense || '??'}
`}).join('') : '

No enemies in this room

' }
${currentRoom.enemies && currentRoom.enemies.length > 0 ? `` : `` }
`; } else { content += `

Loading next room...

`; } content += '
'; 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; }