Game-Server/Client/js/ui/MainMenu.js
2026-01-24 16:47:19 -04:00

1252 lines
52 KiB
JavaScript

/**
* 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 = `<pre style="margin: 0; font-family: 'Space Mono', monospace; color: white; font-size: 0.9em; line-height: 1.6;">${saveInfoText}</pre>`;
// 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 = '<i class="fas fa-spinner fa-spin"></i> Connecting...';
// Simulate SSO login (will be implemented later)
setTimeout(() => {
this.isLoggedIn = true;
this.ssoLoginBtn.innerHTML = '<i class="fas fa-check"></i> 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 = '<i class="fas fa-gamepad"></i> Continue';
this.newGameBtn.style.display = 'none';
this.deleteSaveBtn.style.display = 'block';
this.deleteSaveBtn.disabled = false;
this.deleteSaveBtn.innerHTML = '<i class="fas fa-trash"></i> 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 = '<i class="fas fa-play"></i> 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 = `<pre style="margin: 0; font-family: 'Space Mono', monospace; color: white; font-size: 0.9em; line-height: 1.6;">${saveInfoText}</pre>`;
} else {
saveInfoDetails.innerHTML = `
<div class="save-info-content">
<p><strong>Slot:</strong> ${this.selectedSlot || 'None'}</p>
<p><strong>Status:</strong> Empty</p>
<p>Ready for new adventure</p>
</div>
`;
}
}
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 = `
<div class="alert-message">${message}</div>
<div class="modal-footer">
<button class="btn-alert" onclick="window.mainMenu.closeAlert()">OK</button>
</div>
`;
// 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 = `
<div class="confirm-message">${message}</div>
<div class="modal-footer">
<button class="btn-cancel" onclick="window.mainMenu.closeConfirm()">Cancel</button>
<button class="btn-confirm" onclick="window.mainMenu.executeConfirm()">Confirm</button>
</div>
`;
// 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();
}