/** * Main Menu System * Handles the main menu UI, save management, and game initialization */ console.log('[MAIN MENU] MainMenu.js script loaded'); class MainMenu { constructor() { console.log('[MAIN MENU] Constructor called'); this.mainMenu = document.getElementById('mainMenu'); this.selectedSlot = null; this.saveSlotsCount = 3; // Number of save slots console.log('[MAIN MENU] Initializing elements'); this.initializeElements(); console.log('[MAIN MENU] Initializing event listeners'); this.bindEvents(); // Delay save folder verification until electronAPI is available this.initializeAfterElectronAPI(); } initializeAfterElectronAPI() { // Check if electronAPI is available, if not wait for it if (window.electronAPI) { console.log('[MAIN MENU] electronAPI available, proceeding with initialization'); this.completeInitialization(); } else { console.log('[MAIN MENU] electronAPI not available, waiting...'); // Wait for electronAPI to be available setTimeout(() => { this.initializeAfterElectronAPI(); }, 100); } } async completeInitialization() { console.log('[MAIN MENU] Starting save folder verification'); this.verifySaveFolders(); console.log('[MAIN MENU] Loading save data'); await this.loadSaveData(); console.log('[MAIN MENU] Updating save slots'); this.updateSaveSlots(); console.log('[MAIN MENU] Constructor completed'); } verifySaveFolders() { console.log('[MAIN MENU] Verifying save folders...'); console.log('[MAIN MENU] window.electronAPI available:', !!window.electronAPI); try { // Use Electron's IPC instead of direct Node.js access if (window.electronAPI) { console.log('[MAIN MENU] Using Electron API for folder operations'); console.log('[MAIN MENU] createSaveFolders method available:', typeof window.electronAPI.createSaveFolders); // Request folder creation through main process window.electronAPI.createSaveFolders(this.saveSlotsCount).then((result) => { console.log('[MAIN MENU] createSaveFolders response:', result); if (result.success) { console.log('[MAIN MENU] Save folders created successfully:', result.paths); this.savePaths = result.paths; } else { console.error('[MAIN MENU] Failed to create save folders:', result.error); console.log('[MAIN MENU] Falling back to localStorage save system'); this.useLocalStorageFallback(); } }).catch((error) => { console.error('[MAIN MENU] IPC error:', error); console.log('[MAIN MENU] Falling back to localStorage save system'); this.useLocalStorageFallback(); }); } else { console.log('[MAIN MENU] Electron API not available, using localStorage fallback'); this.useLocalStorageFallback(); } } catch (error) { console.error('[MAIN MENU] Error verifying save folders:', error); console.log('[MAIN MENU] Falling back to localStorage save system'); this.useLocalStorageFallback(); } } hideLoadingScreenAndShowGame() { console.log('[MAIN MENU] Hiding loading screen and showing game interface'); // Hide loading screen const loadingScreen = document.getElementById('loadingScreen'); if (loadingScreen) { loadingScreen.classList.add('hidden'); console.log('[MAIN MENU] Loading screen hidden'); } // Show game interface const gameInterface = document.getElementById('gameInterface'); if (gameInterface) { gameInterface.classList.remove('hidden'); console.log('[MAIN MENU] Game interface shown'); } // Show game container const gameContainer = document.getElementById('gameContainer'); if (gameContainer) { gameContainer.classList.remove('hidden'); console.log('[MAIN MENU] Game container shown'); } } useLocalStorageFallback() { // Fallback for browser testing or when Electron API fails for (let i = 1; i <= this.saveSlotsCount; i++) { const saveKey = `gso_save_slot_${i}`; if (!localStorage.getItem(saveKey)) { const saveInfo = { slot: i, created: new Date().toISOString(), version: '1.0.0', exists: false }; localStorage.setItem(saveKey, JSON.stringify(saveInfo)); console.log(`[MAIN MENU] Created localStorage save info for slot ${i}`); } } // Mark that we're using localStorage this.savePaths = null; console.log('[MAIN MENU] Using localStorage save system'); } initializeElements() { // Menu sections this.mainMenu = document.getElementById('mainMenu'); this.loginSection = document.getElementById('loginSection'); this.saveSection = document.getElementById('saveSection'); this.optionsSection = document.getElementById('optionsSection'); // Login buttons this.ssoLoginBtn = document.getElementById('ssoLoginBtn'); this.offlineBtn = document.getElementById('offlineBtn'); // Save selection this.saveSlots = document.querySelectorAll('.save-slot'); this.backToLoginBtn = document.getElementById('backToLoginBtn'); // Save confirmation section this.confirmSection = document.getElementById('confirmSection'); this.confirmSlotTitle = document.getElementById('confirmSlotTitle'); this.confirmSaveDetails = document.getElementById('confirmSaveDetails'); this.confirmLevel = document.getElementById('confirmLevel'); this.confirmShip = document.getElementById('confirmShip'); this.confirmPlayTime = document.getElementById('confirmPlayTime'); this.confirmLastPlayed = document.getElementById('confirmLastPlayed'); this.confirmNewGameBtn = document.getElementById('confirmNewGameBtn'); this.confirmContinueBtn = document.getElementById('confirmContinueBtn'); this.confirmDeleteBtn = document.getElementById('confirmDeleteBtn'); console.log('[MAIN MENU] confirmDeleteBtn found:', !!this.confirmDeleteBtn); this.confirmSettingsBtn = document.getElementById('confirmSettingsBtn'); this.backToSavesFromConfirmBtn = document.getElementById('backToSavesFromConfirmBtn'); // Game options this.newGameBtn = document.getElementById('newGameBtn'); this.continueBtn = document.getElementById('continueBtn'); this.deleteSaveBtn = document.getElementById('deleteSaveBtn'); console.log('[MAIN MENU] deleteSaveBtn found:', !!this.deleteSaveBtn); this.settingsBtn = document.getElementById('settingsBtn'); this.quitBtn = document.getElementById('quitBtn'); this.backToSavesBtn = document.getElementById('backToSavesBtn'); // Footer links this.aboutBtn = document.getElementById('aboutBtn'); this.helpBtn = document.getElementById('helpBtn'); } bindEvents() { // Login events this.ssoLoginBtn?.addEventListener('click', () => this.handleSSOLogin()); this.offlineBtn?.addEventListener('click', () => this.handleOfflineLogin()); // Save selection events this.saveSlots.forEach(slot => { slot.addEventListener('click', () => this.selectSaveSlot(slot)); }); this.backToLoginBtn?.addEventListener('click', () => this.showLoginSection()); // Save confirmation events this.confirmNewGameBtn?.addEventListener('click', () => this.startNewGame()); this.confirmContinueBtn?.addEventListener('click', () => this.continueGame()); this.confirmDeleteBtn?.addEventListener('click', () => { console.log('[MAIN MENU] Delete button clicked'); this.deleteSave(); }); this.confirmSettingsBtn?.addEventListener('click', () => this.showSettings()); this.backToSavesFromConfirmBtn?.addEventListener('click', () => this.showSaveSection()); // Game options events this.newGameBtn?.addEventListener('click', () => this.startNewGame()); this.continueBtn?.addEventListener('click', () => this.continueGame()); this.deleteSaveBtn?.addEventListener('click', () => { console.log('[MAIN MENU] Delete save button clicked'); this.deleteSave(); }); this.settingsBtn?.addEventListener('click', () => this.showSettings()); this.quitBtn?.addEventListener('click', () => this.quitGame()); this.backToSavesBtn?.addEventListener('click', async () => this.showSaveSection()); // Footer events this.aboutBtn?.addEventListener('click', () => this.showAbout()); this.helpBtn?.addEventListener('click', () => this.showHelp()); } showSection(sectionName) { // Hide all sections this.loginSection?.classList.add('hidden'); this.saveSection?.classList.add('hidden'); this.confirmSection?.classList.add('hidden'); this.optionsSection?.classList.add('hidden'); // Show selected section const section = document.getElementById(`${sectionName}Section`); if (section) { section.classList.remove('hidden'); this.currentSection = sectionName; } } showLoginSection() { this.showSection('login'); } async showSaveSection() { this.showSection('save'); await this.loadSaveData(); this.updateSaveSlots(); } showConfirmSection() { this.showSection('confirm'); // Clear any previous data to prevent contamination this.clearConfirmSectionData(); this.updateConfirmSection(); } updateConfirmSection() { const hasSave = this.selectedSlot && this.saveData[this.selectedSlot]; // Update slot title this.confirmSlotTitle.textContent = `Slot ${this.selectedSlot}`; if (hasSave) { const saveData = this.saveData[this.selectedSlot]; const player = saveData.player || {}; // Update save details const playerLevel = player.stats?.level || player.level || 1; // Get ship name from multiple possible fields const shipName = player.shipName || player.ship?.name || player.currentShip || player.selectedShip || saveData.shipName || saveData.currentShip || 'Unknown Ship'; // Calculate play time - check multiple possible fields and format properly const playTime = player.playTime || player.stats?.playTime || saveData.playTime || saveData.totalPlayTime || 0; // Handle different time formats (seconds, milliseconds, or already formatted) let totalSeconds = 0; if (typeof playTime === 'number') { // If it's a large number, it might be milliseconds if (playTime > 3600) { totalSeconds = Math.floor(playTime / 1000); } else { totalSeconds = Math.floor(playTime); } } else if (typeof playTime === 'string') { // If it's already formatted, use it as-is const playTimeText = playTime; } else { totalSeconds = 0; } // Format time as dd.hh.nn.ss const days = Math.floor(totalSeconds / 86400); const hours = Math.floor((totalSeconds % 86400) / 3600); const minutes = Math.floor((totalSeconds % 3600) / 60); const seconds = totalSeconds % 60; let playTimeText = ''; if (days > 0) { playTimeText = `${days}d ${hours}h ${minutes}m ${seconds}s`; } else if (hours > 0) { playTimeText = `${hours}h ${minutes}m ${seconds}s`; } else if (minutes > 0) { playTimeText = `${minutes}m ${seconds}s`; } else { playTimeText = `${seconds}s`; } // Last played - check multiple possible date fields const lastPlayed = saveData.lastPlayed || saveData.lastSave || saveData.timestamp || saveData.date || new Date(); const formattedDate = lastPlayed instanceof Date ? lastPlayed.toLocaleDateString() : (typeof lastPlayed === 'string' ? lastPlayed : new Date(lastPlayed).toLocaleDateString()); const lastPlayedText = formattedDate === new Date().toLocaleDateString() ? 'Today' : formattedDate; // Restore textContent properties this.confirmLevel.textContent = playerLevel; this.confirmShip.textContent = shipName; this.confirmPlayTime.textContent = playTimeText; this.confirmLastPlayed.textContent = lastPlayedText; // Format save info with consistent alignment and labels const saveInfoText = `Level: ${playerLevel}\n\nShip: ${shipName}\n\nPlay Time: ${playTimeText}\n\nLast Played: ${lastPlayedText}`; // Update save details with pre-formatted text for alignment this.confirmSaveDetails.innerHTML = `
${saveInfoText}
`; // Has save data - always show Continue, hide New Game this.confirmContinueBtn.style.display = 'block'; this.confirmContinueBtn.disabled = false; this.confirmDeleteBtn.style.display = 'block'; this.confirmDeleteBtn.disabled = false; this.confirmSettingsBtn.style.display = 'block'; this.confirmSettingsBtn.disabled = false; this.confirmNewGameBtn.style.display = 'none'; this.confirmNewGameBtn.disabled = true; } else { // No save data - show new game option this.confirmLevel.textContent = 'New'; this.confirmShip.textContent = 'New Adventure'; this.confirmPlayTime.textContent = '0h 0m'; this.confirmLastPlayed.textContent = 'Never'; // Show new game and settings buttons, hide continue and delete this.confirmNewGameBtn.style.display = 'block'; this.confirmNewGameBtn.disabled = false; this.confirmSettingsBtn.style.display = 'block'; this.confirmSettingsBtn.disabled = false; this.confirmContinueBtn.style.display = 'none'; this.confirmDeleteBtn.style.display = 'none'; } } showOptionsSection() { this.showSection('options'); // Clear any previous data to prevent contamination this.clearOptionsSectionData(); this.updateGameOptions(); } clearConfirmSectionData() { // Clear individual text content elements if (this.confirmLevel) this.confirmLevel.textContent = ''; if (this.confirmShip) this.confirmShip.textContent = ''; if (this.confirmPlayTime) this.confirmPlayTime.textContent = ''; if (this.confirmLastPlayed) this.confirmLastPlayed.textContent = ''; // Clear the save details display if (this.confirmSaveDetails) { this.confirmSaveDetails.innerHTML = ''; } } clearOptionsSectionData() { // Clear the save info details display const saveInfoDetails = document.getElementById('saveInfoDetails'); if (saveInfoDetails) { saveInfoDetails.innerHTML = ''; } } handleSSOLogin() { // Placeholder for SSO login console.log('[MAIN MENU] SSO login requested (placeholder)'); // Show loading state this.ssoLoginBtn.disabled = true; this.ssoLoginBtn.innerHTML = ' Connecting...'; // Simulate SSO login (will be implemented later) setTimeout(() => { this.isLoggedIn = true; this.ssoLoginBtn.innerHTML = ' Logged In'; this.ssoLoginBtn.disabled = false; // Show save selection this.showSaveSection(); }, 1500); } handleOfflineLogin() { console.log('[MAIN MENU] Login successful'); this.isLoggedIn = true; // Show save selection setTimeout(async () => { await this.showSaveSection(); }, 1500); } async loadSaveData() { // Initialize saveData object this.saveData = {}; console.log('[MAIN MENU] Loading save data from file system'); // Check if we have save paths and should use file system if (this.savePaths && window.electronAPI) { console.log('[MAIN MENU] Using file system for save data'); // Load save data from file system using the load-game API for (let i = 1; i <= 3; i++) { try { console.log(`[MAIN MENU] Loading save file for slot ${i}`); const result = await window.electronAPI.loadGame(i); if (result.success && result.data) { this.saveData[i] = result.data; console.log(`[MAIN MENU] Slot ${i} loaded successfully:`, this.saveData[i]); } else { console.log(`[MAIN MENU] Slot ${i} has no save file or failed to load:`, result.error); this.saveData[i] = null; } } catch (error) { console.error(`[MAIN MENU] Error reading save slot ${i}:`, error); this.saveData[i] = null; } } } else { console.log('[MAIN MENU] Using localStorage fallback for save data'); console.log('[MAIN MENU] Available localStorage keys:', Object.keys(localStorage)); // Fallback to localStorage for (let i = 1; i <= 3; i++) { const saveKey = `gso_save_slot_${i}`; const saveData = localStorage.getItem(saveKey); console.log(`[MAIN MENU] Slot ${i} key: ${saveKey}, data:`, saveData); if (saveData) { try { this.saveData[i] = JSON.parse(saveData); console.log(`[MAIN MENU] Slot ${i} parsed successfully:`, this.saveData[i]); } catch (error) { console.error(`[MAIN MENU] Error loading save slot ${i}:`, error); this.saveData[i] = null; } } else { console.log(`[MAIN MENU] Slot ${i} has no data`); this.saveData[i] = null; } } } console.log('[MAIN MENU] Final saveData object:', this.saveData); } updateSaveSlots() { console.log('[MAIN MENU] Updating save slots UI'); console.log('[MAIN MENU] this.saveSlots:', this.saveSlots); console.log('[MAIN MENU] this.saveData:', this.saveData); // Check if saveSlots elements exist if (!this.saveSlots || this.saveSlots.length === 0) { console.log('[MAIN MENU] Save slots not found, re-initializing elements'); this.saveSlots = document.querySelectorAll('.save-slot'); console.log('[MAIN MENU] Re-initialized saveSlots:', this.saveSlots); } if (!this.saveSlots || this.saveSlots.length === 0) { console.error('[MAIN MENU] Could not find save slot elements in DOM'); return; } this.saveSlots.forEach((slot, index) => { const slotNumber = index + 1; const saveInfo = this.saveData[slotNumber]; console.log(`[MAIN MENU] Processing slot ${slotNumber}, saveInfo:`, saveInfo); const slotStatus = slot.querySelector('.slot-status'); const slotName = slot.querySelector('.slot-name'); const slotDetails = slot.querySelector('.slot-details'); const slotBtn = slot.querySelector('.slot-btn'); console.log(`[MAIN MENU] Found elements for slot ${slotNumber}:`, { slotStatus: !!slotStatus, slotName: !!slotName, slotDetails: !!slotDetails, slotBtn: !!slotBtn }); // Check if this is a valid save game (has actual game data) console.log(`[MAIN MENU] Validating save data for slot ${slotNumber}:`, { saveInfo: saveInfo, hasSaveInfo: !!saveInfo, hasPlayer: !!(saveInfo && saveInfo.player), hasPlayerStats: !!(saveInfo && saveInfo.player && saveInfo.player.stats), playerLevel: saveInfo && saveInfo.player && saveInfo.player.stats ? saveInfo.player.stats.level : 'no player.stats', playerKeys: saveInfo && saveInfo.player ? Object.keys(saveInfo.player) : 'no player' }); const hasValidSave = saveInfo && saveInfo.player && saveInfo.player.stats && saveInfo.player.stats.level !== undefined; if (hasValidSave) { // Update with existing save data console.log(`[MAIN MENU] Slot ${slotNumber} has valid save data - showing Load`); if (slotStatus) { slotStatus.textContent = 'Level ' + (saveInfo.player.stats.level || 1); slotStatus.className = 'slot-status has-data'; } if (slotName) { slotName.textContent = saveInfo.player.info.name || 'Commander'; } // Format playtime and last played const playTime = saveInfo.player.stats.playTime || saveInfo.player.playTime || 0; // Handle different time formats (seconds, milliseconds, or already formatted) let totalSeconds = 0; if (typeof playTime === 'number') { // If it's a large number, it might be milliseconds if (playTime > 3600) { totalSeconds = Math.floor(playTime / 1000); } else { totalSeconds = Math.floor(playTime); } } else if (typeof playTime === 'string') { // If it's already formatted, use it as-is var playtime = playTime; } else { totalSeconds = 0; } // Only format if we haven't already set playtime from a string if (typeof playtime !== 'string') { // Format time as dd.hh.nn.ss const days = Math.floor(totalSeconds / 86400); const hours = Math.floor((totalSeconds % 86400) / 3600); const minutes = Math.floor((totalSeconds % 3600) / 60); const seconds = totalSeconds % 60; if (days > 0) { playtime = `${days}d ${hours}h ${minutes}m ${seconds}s`; } else if (hours > 0) { playtime = `${hours}h ${minutes}m ${seconds}s`; } else if (minutes > 0) { playtime = `${minutes}m ${seconds}s`; } else { playtime = `${seconds}s`; } } const lastSaved = saveInfo.lastSave ? new Date(saveInfo.lastSave).toLocaleDateString() : 'Unknown'; if (slotDetails) { slotDetails.textContent = `Playtime: ${playtime} | Saved: ${lastSaved}`; } if (slotBtn) { slotBtn.textContent = 'Select'; slotBtn.onclick = () => this.selectSaveSlot(slot); } } else { // Empty slot or invalid/placeholder save console.log(`[MAIN MENU] Slot ${slotNumber} is empty or invalid - showing New Game`); if (slotStatus) { slotStatus.textContent = 'Empty'; slotStatus.className = 'slot-status empty'; } if (slotName) { slotName.textContent = 'New Game'; } if (slotDetails) { slotDetails.textContent = 'Start a new adventure'; } if (slotBtn) { slotBtn.textContent = 'Select'; slotBtn.onclick = () => this.selectSaveSlot(slot); } } }); } selectSaveSlot(slot) { const slotNumber = parseInt(slot.dataset.slot); this.selectedSlot = slotNumber; console.log(`[MAIN MENU] Save slot ${slotNumber} selected`); // Show confirmation section this.showConfirmSection(); } updateGameOptions() { const hasSave = this.selectedSlot && this.saveData[this.selectedSlot]; // Update button states based on save data if (hasSave) { // Has save data - always show Continue, hide New Game this.continueBtn.style.display = 'block'; this.continueBtn.disabled = false; this.continueBtn.innerHTML = ' Continue'; this.newGameBtn.style.display = 'none'; this.deleteSaveBtn.style.display = 'block'; this.deleteSaveBtn.disabled = false; this.deleteSaveBtn.innerHTML = ' Delete Save'; // Update center information display this.updateSaveInfoDisplay(); } else { // Show new game button, hide continue button this.newGameBtn.style.display = 'block'; this.newGameBtn.disabled = false; this.newGameBtn.innerHTML = ' Start New Game'; this.continueBtn.style.display = 'none'; this.deleteSaveBtn.style.display = 'none'; // Update center information display for empty slot this.updateSaveInfoDisplay(); } } updateSaveInfoDisplay() { const saveInfoDetails = document.getElementById('saveInfoDetails'); if (!saveInfoDetails) return; const hasSave = this.selectedSlot && this.saveData[this.selectedSlot]; if (hasSave) { const saveData = this.saveData[this.selectedSlot]; const player = saveData.player || {}; // Format save information const level = player.stats?.level || player.level || 1; const name = player.info ? player.info.name : 'Commander'; const ship = player.shipName || 'Unknown Ship'; // Calculate play time - check multiple possible fields and format properly const playTime = player.playTime || player.stats?.playTime || saveData.playTime || saveData.totalPlayTime || 0; // Handle different time formats (seconds, milliseconds, or already formatted) let totalSeconds = 0; if (typeof playTime === 'number') { // If it's a large number, it might be milliseconds if (playTime > 3600) { totalSeconds = Math.floor(playTime / 1000); } else { totalSeconds = Math.floor(playTime); } } else if (typeof playTime === 'string') { // If it's already formatted, use it as-is var playtime = playTime; } else { totalSeconds = 0; } // Only format if we haven't already set playtime from a string if (typeof playtime !== 'string') { // Format time as dd.hh.nn.ss const days = Math.floor(totalSeconds / 86400); const hours = Math.floor((totalSeconds % 86400) / 3600); const minutes = Math.floor((totalSeconds % 3600) / 60); const seconds = totalSeconds % 60; if (days > 0) { playtime = `${days}d ${hours}h ${minutes}m ${seconds}s`; } else if (hours > 0) { playtime = `${hours}h ${minutes}m ${seconds}s`; } else if (minutes > 0) { playtime = `${minutes}m ${seconds}s`; } else { playtime = `${seconds}s`; } } // Check multiple possible date fields and use current date if none found const lastSaved = saveData.lastPlayed || saveData.lastSave || saveData.timestamp || new Date().toLocaleDateString(); const formattedDate = lastSaved === new Date().toLocaleDateString() ? 'Today' : (typeof lastSaved === 'string' ? lastSaved : new Date(lastSaved).toLocaleDateString()); // Format save info with consistent alignment and labels const saveInfoText = `Level: ${level}\n\nShip: ${ship}\n\nPlay Time: ${playtime}\n\nLast Played: ${formattedDate}`; // Update save details with pre-formatted text for alignment saveInfoDetails.innerHTML = `
${saveInfoText}
`; } else { saveInfoDetails.innerHTML = `

Slot: ${this.selectedSlot || 'None'}

Status: Empty

Ready for new adventure

`; } } startNewGame() { if (!this.selectedSlot) { console.error('[MAIN MENU] No save slot selected'); return; } console.log(`[MAIN MENU] Starting new game in slot ${this.selectedSlot}`); // Clear existing save data for this slot const saveKey = `gso_save_slot_${this.selectedSlot}`; localStorage.removeItem(saveKey); this.saveData[this.selectedSlot] = null; // Start the game this.launchGame('new'); } continueGame() { if (!this.selectedSlot || !this.saveData[this.selectedSlot]) { console.error('[MAIN MENU] No save data to continue'); return; } console.log(`[MAIN MENU] Continuing game from slot ${this.selectedSlot}`); this.launchGame('continue'); } async deleteSave() { console.log('[MAIN MENU] deleteSave called'); if (!this.selectedSlot || !this.saveData[this.selectedSlot]) { console.error('[MAIN MENU] No save data to delete', { selectedSlot: this.selectedSlot, hasSaveData: !!this.saveData[this.selectedSlot] }); return; } const saveData = this.saveData[this.selectedSlot]; const saveInfo = saveData.player ? `Level ${saveData.player.stats?.level || saveData.player.level || 1} ${saveData.player.shipName || saveData.player.ship?.name || 'Player'}` : 'Unknown save data'; const confirmMessage = `Are you sure you want to delete the save from slot ${this.selectedSlot}?\n\n${saveInfo}\n\nThis action cannot be undone!`; console.log('[MAIN MENU] Showing confirmation modal'); // Show custom confirmation modal this.showConfirm(confirmMessage, () => { console.log('[MAIN MENU] User confirmed deletion, executing delete'); this.executeDeleteSave(); }, () => { console.log('[MAIN MENU] User cancelled deletion'); }); } async executeDeleteSave() { console.log('[MAIN MENU] executeDeleteSave called', { selectedSlot: this.selectedSlot }); try { console.log(`[MAIN MENU] Deleting save data from slot ${this.selectedSlot}`); // Remove from localStorage const saveKey = `gso_save_slot_${this.selectedSlot}`; console.log(`[MAIN MENU] Removing localStorage key: ${saveKey}`); localStorage.removeItem(saveKey); // Verify removal const remainingData = localStorage.getItem(saveKey); console.log(`[MAIN MENU] Verification - save data still exists: ${!!remainingData}`); // Remove from file system if available if (window.electronAPI && window.electronAPI.deleteSaveFile) { console.log(`[MAIN MENU] Deleting save file via Electron API`); await window.electronAPI.deleteSaveFile(this.selectedSlot); console.log(`[MAIN MENU] Save file deleted from slot ${this.selectedSlot} via Electron API`); } else if (window.electronAPI && window.electronAPI.deleteSave) { console.log(`[MAIN MENU] Deleting save file via Electron API (deleteSave method)`); await window.electronAPI.deleteSave(this.selectedSlot); console.log(`[MAIN MENU] Save file deleted from slot ${this.selectedSlot} via Electron API`); } else { console.log(`[MAIN MENU] No Electron API available for file deletion, only localStorage deletion performed`); console.log(`[MAIN MENU] Available electronAPI methods:`, Object.keys(window.electronAPI || {})); } // Clear from memory console.log(`[MAIN MENU] Clearing save data from memory`); this.saveData[this.selectedSlot] = null; // Update UI console.log(`[MAIN MENU] Updating UI after deletion`); this.updateSaveSlots(); this.updateGameOptions(); console.log(`[MAIN MENU] Save data successfully deleted from slot ${this.selectedSlot}`); // Show success message this.showAlert(`Save data from slot ${this.selectedSlot} has been deleted successfully.`, 'success'); // Return to save selection screen console.log(`[MAIN MENU] Returning to save selection screen`); this.showSaveSection(); } catch (error) { console.error('[MAIN MENU] Failed to delete save data:', error); this.showAlert('Failed to delete save data. Please try again.', 'error'); } } loadGame(slotNumber) { this.selectedSlot = slotNumber; console.log(`[MAIN MENU] Loading game from slot ${slotNumber}`); this.launchGame('continue'); } async launchGame(mode) { const debugLogger = window.debugLogger; if (debugLogger) debugLogger.startStep('MainMenu.launchGame', { mode: mode, selectedSlot: this.selectedSlot, timestamp: new Date().toISOString() }); console.log('[MAIN MENU] Launching game in', mode, 'mode'); console.log('[MAIN MENU] Selected save slot:', this.selectedSlot); try { // Hide main menu this.hide(); // Show loading screen const loadingScreen = document.getElementById('loadingScreen'); if (loadingScreen) { loadingScreen.classList.remove('hidden'); console.log('[MAIN MENU] Loading screen shown'); if (debugLogger) debugLogger.logStep('Loading screen displayed'); } // Initialize game systems first console.log('[MAIN MENU] Starting game systems initialization...'); if (debugLogger) debugLogger.logStep('Starting game systems initialization'); try { await this.initializeGameSystems(); console.log('[MAIN MENU] Game systems initialized successfully'); if (debugLogger) debugLogger.logStep('Game systems initialized successfully'); // Set save slot in game engine if (window.game) { console.log('[MAIN MENU] Setting active save slot to:', this.selectedSlot); // Pass save path information to game engine if (this.savePaths) { window.game.saveSlotInfo = { slot: this.selectedSlot, path: this.savePaths.slots[this.selectedSlot - 1], useFileSystem: true }; console.log('[MAIN MENU] Using file system for saves'); if (debugLogger) debugLogger.logStep('Save slot info set with file system', { slot: this.selectedSlot, path: this.savePaths.slots[this.selectedSlot - 1] }); } else { window.game.saveSlotInfo = { slot: this.selectedSlot, useFileSystem: false }; console.log('[MAIN MENU] Using localStorage for saves'); if (debugLogger) debugLogger.logStep('Save slot info set with localStorage', { slot: this.selectedSlot }); } // Start the game if (mode === 'continue') { console.log('[MAIN MENU] Starting game in continue mode'); if (debugLogger) debugLogger.logStep('Starting game in continue mode'); await window.game.startGame(true); } else { console.log('[MAIN MENU] Starting new game'); if (debugLogger) debugLogger.logStep('Starting new game'); await window.game.startGame(false); } // Perform immediate UI updates after game starts console.log('[MAIN MENU] Performing immediate UI updates after game start'); // Update GUI immediately if (window.game && window.game.updateGUI) { console.log('[MAIN MENU] Updating GUI immediately'); window.game.updateGUI(); } // Update daily countdown immediately if (window.game && window.game.systems && window.game.systems.questSystem && window.game.systems.questSystem.updateDailyCountdown) { console.log('[MAIN MENU] Updating daily countdown immediately'); window.game.systems.questSystem.updateDailyCountdown(); } // Update weekly countdown immediately if (window.game && window.game.systems && window.game.systems.questSystem && window.game.systems.questSystem.updateWeeklyCountdown) { console.log('[MAIN MENU] Updating weekly countdown immediately'); window.game.systems.questSystem.updateWeeklyCountdown(); } // Hide loading screen and show game interface this.hideLoadingScreenAndShowGame(); if (debugLogger) debugLogger.endStep('MainMenu.launchGame', { success: true, mode: mode }); } else { throw new Error('Game engine not initialized'); } } catch (error) { console.error('[MAIN MENU] Game systems initialization failed:', error); if (debugLogger) { debugLogger.errorEvent('MainMenu.launchGame', error, { phase: 'initialization', mode: mode }); debugLogger.endStep('MainMenu.launchGame', { success: false, error: error.message }); } // Show error and return to menu this.showAlert('Failed to initialize game. Check console for details.\n\nError: ' + error.message, 'error'); this.show(); } } catch (error) { console.error('[MAIN MENU] Game systems initialization failed:', error); if (debugLogger) { debugLogger.errorEvent('MainMenu.launchGame', error, { phase: 'initialization', mode: mode }); debugLogger.endStep('MainMenu.launchGame', { success: false, error: error.message }); } // Show error and return to menu this.showAlert('Failed to initialize game. Check console for details.\n\nError: ' + error.message, 'error'); this.show(); } } async initializeGameSystems() { const debugLogger = window.debugLogger; if (debugLogger) debugLogger.startStep('MainMenu.initializeGameSystems', { timestamp: new Date().toISOString() }); console.log('[MAIN MENU] Initializing game systems'); try { // Initialize logger if available const logger = window.logger; if (logger) { await logger.timeAsync('Game Systems Initialization', async () => { await logger.info('Initializing game systems from main menu'); }); } if (debugLogger) debugLogger.logStep('Creating GameEngine instance'); // Prevent duplicate GameEngine creation if (window.game) { console.log('[MAIN MENU] GameEngine already exists, skipping creation'); if (debugLogger) debugLogger.logStep('GameEngine already exists, skipping creation'); return; } // Create game engine window.game = new GameEngine(); if (debugLogger) debugLogger.logStep('GameEngine created, calling init()'); // Initialize game systems await window.game.init(); if (debugLogger) debugLogger.logStep('GameEngine init() completed successfully'); console.log('[MAIN MENU] Game systems initialized successfully'); if (debugLogger) debugLogger.endStep('MainMenu.initializeGameSystems', { success: true }); } catch (error) { console.error('[MAIN MENU] Failed to initialize game systems:', error); if (debugLogger) { debugLogger.errorEvent('MainMenu.initializeGameSystems', error, { phase: 'initialization', timestamp: new Date().toISOString() }); debugLogger.endStep('MainMenu.initializeGameSystems', { success: false, error: error.message }); } // Show error and return to menu this.showAlert('Failed to initialize game. Please try again.\n\nError: ' + error.message, 'error'); this.show(); } } showSettings() { console.log('[MAIN MENU] Settings requested (placeholder)'); // Placeholder for settings menu this.showAlert('Settings menu coming soon!', 'info'); } showAbout() { console.log('[MAIN MENU] About requested'); this.showAlert('Galaxy Strike Online\nVersion 1.0.0\n\nA space-themed idle MMORPG built with Electron.\n\n© 2024 Korvarix Studios', 'info', 'About'); } showHelp() { console.log('[MAIN MENU] Help requested'); this.showAlert('Galaxy Strike Online - Help\n\n• Login with your account to play\n• Select a save slot to store your progress\n• Start a new game or continue existing save\n• Use Ctrl+Alt+Shift+C for developer console\n\nFor more help, visit our Discord or documentation.', 'info', 'Help'); } quitGame() { console.log('[MAIN MENU] Quit requested'); this.showConfirm('Are you sure you want to quit Galaxy Strike Online?', () => { // In Electron, this would close the app if (window.electronAPI && window.electronAPI.quit) { window.electronAPI.quit(); } else { // Fallback for browser window.close(); } }); } // Public methods for external access show() { this.mainMenu?.classList.remove('hidden'); this.showSaveSection(); } hide() { this.mainMenu?.classList.add('hidden'); } // Custom alert function to replace browser alerts showAlert(message, type = 'info', title = null) { const modalOverlay = document.getElementById('modalOverlay'); const modal = document.getElementById('modal'); const modalTitle = document.getElementById('modalTitle'); const modalBody = document.getElementById('modalBody'); console.log('[MAIN MENU] showAlert called', { modalOverlay: !!modalOverlay, modal: !!modal, modalTitle: !!modalTitle, modalBody: !!modalBody }); if (!modalOverlay || !modal || !modalTitle || !modalBody) { // Fallback to browser alert if modal elements aren't found alert(message); return; } // Set title based on type const titles = { 'info': 'Information', 'success': 'Success', 'error': 'Error', 'warning': 'Warning' }; const alertTitle = title || titles[type] || 'Information'; // Set modal content modalTitle.textContent = alertTitle; modalBody.innerHTML = `
${message}
`; // Set modal type class modal.className = `modal alert-modal ${type}`; // Show modal modalOverlay.classList.remove('hidden'); // Add click outside to close modalOverlay.onclick = (e) => { if (e.target === modalOverlay) { this.closeAlert(); } }; // Add escape key to close const handleEscape = (e) => { if (e.key === 'Escape') { this.closeAlert(); document.removeEventListener('keydown', handleEscape); } }; document.addEventListener('keydown', handleEscape); } closeAlert() { const modalOverlay = document.getElementById('modalOverlay'); if (modalOverlay) { modalOverlay.classList.add('hidden'); } } // Custom confirmation function to replace browser confirm() showConfirm(message, onConfirm, onCancel = null) { const modalOverlay = document.getElementById('modalOverlay'); const modal = document.getElementById('modal'); const modalTitle = document.getElementById('modalTitle'); const modalBody = document.getElementById('modalBody'); console.log('[MAIN MENU] showConfirm called', { modalOverlay: !!modalOverlay, modal: !!modal, modalTitle: !!modalTitle, modalBody: !!modalBody }); if (!modalOverlay || !modal || !modalTitle || !modalBody) { console.warn('[MAIN MENU] Modal elements not found, falling back to browser confirm'); // Fallback to browser confirm if modal elements aren't found const result = confirm(message); if (result && onConfirm) onConfirm(); if (!result && onCancel) onCancel(); return; } // Set modal content modalTitle.textContent = 'Confirm Action'; modalBody.innerHTML = `
${message}
`; // Set modal type class modal.className = 'modal confirmation-modal'; // Store callbacks this._confirmCallback = onConfirm; this._cancelCallback = onCancel; // Show modal modalOverlay.classList.remove('hidden'); // Add click outside to close modalOverlay.onclick = (e) => { if (e.target === modalOverlay) { this.closeConfirm(); } }; // Add escape key to close const handleEscape = (e) => { if (e.key === 'Escape') { this.closeConfirm(); document.removeEventListener('keydown', handleEscape); } }; document.addEventListener('keydown', handleEscape); } executeConfirm() { console.log('[MAIN MENU] executeConfirm called', { hasCallback: !!this._confirmCallback }); const modalOverlay = document.getElementById('modalOverlay'); if (modalOverlay) { modalOverlay.classList.add('hidden'); } if (this._confirmCallback) { console.log('[MAIN MENU] Executing confirmation callback'); this._confirmCallback(); } else { console.warn('[MAIN MENU] No confirmation callback found'); } // Clear callbacks this._confirmCallback = null; this._cancelCallback = null; } closeConfirm() { const modalOverlay = document.getElementById('modalOverlay'); if (modalOverlay) { modalOverlay.classList.add('hidden'); } if (this._cancelCallback) { this._cancelCallback(); } // Clear callbacks this._confirmCallback = null; this._cancelCallback = null; } } // Initialize main menu when DOM is ready document.addEventListener('DOMContentLoaded', () => { console.log('[MAIN MENU] DOMContentLoaded event fired'); window.mainMenu = new MainMenu(); }); // Also try immediate initialization if DOM is already loaded if (document.readyState === 'loading') { console.log('[MAIN MENU] DOM still loading, waiting for DOMContentLoaded'); } else { console.log('[MAIN MENU] DOM already loaded, initializing immediately'); window.mainMenu = new MainMenu(); }