@@ -672,12 +704,558 @@
+
+
+
+
+
+
+
diff --git a/Client/js/GameInitializer.js b/Client/js/GameInitializer.js
index 2e88c07..1dbf404 100644
--- a/Client/js/GameInitializer.js
+++ b/Client/js/GameInitializer.js
@@ -15,12 +15,16 @@ class GameInitializer {
this.socket = null;
this.apiBaseUrl = 'https://api.korvarix.com/api'; // API Server
this.gameServerUrl = 'https://dev.gameserver.galaxystrike.online'; // Game Server for Socket.IO (local dev server)
+
+ console.log('[GAME INITIALIZER] Constructor - gameServerUrl set to:', this.gameServerUrl);
}
updateServerUrls(apiUrl, gameUrl) {
- console.log('[GAME INITIALIZER] Updating server URLs:', { apiUrl, gameUrl });
+ console.log('[GAME INITIALIZER] Updating server URLs:', { apiUrl: apiUrl, gameUrl: gameUrl });
+ console.log('[GAME INITIALIZER] Previous gameServerUrl:', this.gameServerUrl);
this.apiBaseUrl = apiUrl;
this.gameServerUrl = gameUrl;
+ console.log('[GAME INITIALIZER] New gameServerUrl:', this.gameServerUrl);
}
initializeMultiplayer(server, serverData, authToken, currentUser) {
@@ -33,8 +37,10 @@ class GameInitializer {
// Initialize Socket.IO connection
this.initializeSocketConnection();
- // Initialize game systems with multiplayer support
- this.initializeGameSystems();
+ // Set SmartSaveManager to multiplayer mode
+ if (window.smartSaveManager) {
+ window.smartSaveManager.setMultiplayerMode(true, this);
+ }
// Update UI for multiplayer mode
this.updateUIForMultiplayerMode();
@@ -49,6 +55,7 @@ class GameInitializer {
}
console.log('[GAME INITIALIZER] Initializing Socket.IO connection');
+ console.log('[GAME INITIALIZER] Using gameServerUrl:', this.gameServerUrl);
// Check if we're in local mode and should use mock socket
if (this.gameServerUrl.includes('localhost') && window.localServerManager && window.localServerManager.localServer) {
@@ -63,14 +70,21 @@ class GameInitializer {
return;
}
- // Connect to the game server (different from API server)
- this.socket = io(this.gameServerUrl, {
+ // FORCE THE URL - Override any undefined issues
+ const FORCED_URL = 'https://dev.gameserver.galaxystrike.online';
+ console.log('[GAME INITIALIZER] FORCING URL to:', FORCED_URL);
+ console.log('[GAME INITIALIZER] Original this.gameServerUrl:', this.gameServerUrl);
+
+ // Connect to the game server with FORCED URL
+ this.socket = io(FORCED_URL, {
auth: {
token: this.authToken,
serverId: this.serverData.id
}
});
+ console.log('[GAME INITIALIZER] Socket.IO connection initiated to FORCED URL:', FORCED_URL);
+
// Socket event handlers
this.socket.on('connect', () => {
console.log('[GAME INITIALIZER] Connected to server');
@@ -111,9 +125,69 @@ class GameInitializer {
console.log('[GAME INITIALIZER] Chat message:', data);
this.onChatMessage(data);
});
+
+ // Server data events
+ this.socket.on('authenticated', (data) => {
+ console.log('[GAME INITIALIZER] Authentication response:', data);
+ this.onAuthenticated(data);
+ });
+
+ this.socket.on('gameDataLoaded', (data) => {
+ console.log('[GAME INITIALIZER] Game data loaded:', data);
+ this.onGameDataLoaded(data);
+ });
+
+ this.socket.on('updatePlayerStats', (data) => {
+ console.log('[GAME INITIALIZER] Player stats updated on server:', data);
+ this.onGameDataSaved(data);
+ });
+
+ this.socket.on('gameDataSaved', (data) => {
+ console.log('[GAME INITIALIZER] Game data saved:', data);
+ this.onGameDataSaved(data);
+ });
+
+ // Idle rewards events
+ this.socket.on('offlineRewardsClaimed', (data) => {
+ console.log('[GAME INITIALIZER] Offline rewards claimed:', data);
+ this.onOfflineRewardsClaimed(data);
+ });
+
+ this.socket.on('onlineIdleRewards', (data) => {
+ console.log('[GAME INITIALIZER] Online idle rewards received:', data);
+ this.onOnlineIdleRewards(data);
+ });
+
+ // PlayTime events
+ this.socket.on('playTimeUpdated', (data) => {
+ console.log('[GAME INITIALIZER] PlayTime updated from server:', data);
+ this.onPlayTimeUpdated(data);
+ });
+
+ // Shop purchase events
+ this.socket.on('purchaseCompleted', (data) => {
+ console.log('[GAME INITIALIZER] Purchase completed:', data);
+ this.onPurchaseCompleted(data);
+ });
+
+ // Item system events
+ this.socket.on('shopItemsReceived', (data) => {
+ console.log('[GAME INITIALIZER] Shop items received:', data);
+ this.onShopItemsReceived(data);
+ });
+
+ this.socket.on('itemDetailsReceived', (data) => {
+ console.log('[GAME INITIALIZER] Item details received:', data);
+ this.onItemDetailsReceived(data);
+ });
}
onSocketConnected() {
+ // Expose socket globally for systems that need it
+ if (window.game) {
+ window.game.socket = this.socket;
+ }
+
// Join the server room
this.socket.emit('joinServer', {
serverId: this.serverData.id,
@@ -121,13 +195,48 @@ class GameInitializer {
username: this.currentUser.username
});
+ // Authenticate with server to get player data
+ this.authenticateWithServer();
+
// Show connected status
this.showConnectionStatus('Connected', 'success');
}
onSocketDisconnected() {
+ console.log('[GAME INITIALIZER] Socket disconnected - switching to singleplayer mode');
+
// Show disconnected status
this.showConnectionStatus('Disconnected', 'error');
+
+ // Switch SmartSaveManager back to singleplayer mode
+ if (window.smartSaveManager) {
+ window.smartSaveManager.setMultiplayerMode(false);
+ console.log('[GAME INITIALIZER] SmartSaveManager set to singleplayer mode');
+ }
+
+ // Clear socket reference
+ this.socket = null;
+ console.log('[GAME INITIALIZER] Socket reference cleared');
+ }
+
+ // Force disconnect from multiplayer server
+ forceDisconnect() {
+ console.log('[GAME INITIALIZER] Force disconnect called');
+
+ if (this.socket) {
+ console.log('[GAME INITIALIZER] Disconnecting socket...');
+ this.socket.disconnect();
+ this.socket = null;
+ }
+
+ // Switch to singleplayer mode
+ if (window.smartSaveManager) {
+ window.smartSaveManager.setMultiplayerMode(false);
+ console.log('[GAME INITIALIZER] Force switched to singleplayer mode');
+ }
+
+ this.showConnectionStatus('Disconnected', 'error');
+ console.log('[GAME INITIALIZER] Force disconnect completed');
}
onPlayerJoined(data) {
@@ -156,6 +265,147 @@ class GameInitializer {
}
}
+ onAuthenticated(data) {
+ console.log('[GAME INITIALIZER] Authentication successful:', data);
+
+ if (data.success && data.playerData) {
+ // Store server player data from authentication (this is our primary source)
+ this.serverPlayerData = data.playerData;
+ this.currentUser = data.user;
+
+ // CRITICAL: Force multiplayer mode and prevent fallback
+ if (window.smartSaveManager) {
+ console.log('[GAME INITIALIZER] FORCING multiplayer mode after authentication');
+ window.smartSaveManager.setMultiplayerMode(true, this);
+ }
+
+ // ItemSystem is now initialized by GameEngine - no need to initialize here
+ console.log('[GAME INITIALIZER] ItemSystem initialization handled by GameEngine');
+
+ console.log('[GAME INITIALIZER] Using authentication data as primary source:', this.serverPlayerData);
+
+ // NOW create GameEngine AFTER authentication is successful
+ if (!window.game) {
+ console.log('[GAME INITIALIZER] Creating GameEngine after successful authentication');
+ this.createGameEngineForMultiplayer();
+ }
+
+ // Set SmartSaveManager to multiplayer mode
+ if (window.smartSaveManager) {
+ window.smartSaveManager.setMultiplayerMode(true, this);
+ console.log('[GAME INITIALIZER] SmartSaveManager set to multiplayer mode');
+
+ // Apply authentication data immediately (this will be stored for later)
+ window.smartSaveManager.applyServerDataToGame(data.playerData);
+ }
+
+ // NOTE: Don't apply to GameEngine here - it doesn't exist yet!
+ // The data will be applied in createGameEngineForMultiplayer() after the game is created.
+
+ this.showNotification(`Welcome back! Level ${data.playerData.stats?.level || 1}`, 'success');
+ } else {
+ this.showNotification(data.error || 'Authentication failed', 'error');
+ }
+ }
+
+ createGameEngineForMultiplayer() {
+ console.log('[GAME INITIALIZER] Creating GameEngine for multiplayer mode');
+ console.log('[GAME INITIALIZER] Server player data available:', !!this.serverPlayerData);
+
+ try {
+ // Create GameEngine instance
+ window.game = new GameEngine();
+
+ // CRITICAL: Set multiplayer mode BEFORE initializing systems
+ console.log('[GAME INITIALIZER] Setting multiplayer mode BEFORE initialization');
+ window.game.setMultiplayerMode(true, this.socket, this.serverData, this.currentUser);
+
+ // NOTE: Don't apply server data immediately - wait for full initialization
+ console.log('[GAME INITIALIZER] Server data ready, will apply after GameEngine initialization');
+ console.log('[GAME INITIALIZER] - this.serverPlayerData:', !!this.serverPlayerData);
+ console.log('[GAME INITIALIZER] - window.game:', !!window.game);
+ console.log('[GAME INITIALIZER] - window.game.loadServerPlayerData:', !!window.game?.loadServerPlayerData);
+
+ // Initialize the game engine
+ console.log('[GAME INITIALIZER] About to call window.game.init()');
+ const initPromise = window.game.init();
+ console.log('[GAME INITIALIZER] GameEngine.init() returned:', typeof initPromise, initPromise);
+
+ // Apply server data and refresh UI after initialization is complete
+ initPromise.then(() => {
+ console.log('[GAME INITIALIZER] GameEngine initialized successfully for multiplayer');
+
+ // Apply server data immediately after initialization
+ if (this.serverPlayerData && window.game.loadServerPlayerData) {
+ console.log('[GAME INITIALIZER] Applying server player data to GameEngine:', this.serverPlayerData);
+ window.game.loadServerPlayerData(this.serverPlayerData);
+ console.log('[GAME INITIALIZER] Server player data applied to GameEngine');
+
+ // Force UI refresh
+ if (window.game.systems && window.game.systems.ui && window.game.systems.ui.forceRefreshAllUI) {
+ console.log('[GAME INITIALIZER] Forcing UI refresh after data application');
+ window.game.systems.ui.forceRefreshAllUI();
+ } else {
+ console.warn('[GAME INITIALIZER] UI refresh not available - systems:', !!window.game.systems, 'ui:', !!window.game.systems?.ui, 'forceRefreshAllUI:', !!window.game.systems?.ui?.forceRefreshAllUI);
+ }
+ } else {
+ console.warn('[GAME INITIALIZER] No server player data or loadServerPlayerData method available');
+ console.log('[GAME INITIALIZER] - this.serverPlayerData:', !!this.serverPlayerData);
+ console.log('[GAME INITIALIZER] - window.game.loadServerPlayerData:', !!window.game?.loadServerPlayerData);
+ }
+
+ // Start the game
+ if (window.game.start) {
+ window.game.start();
+ }
+
+ }).catch((error) => {
+ console.error('[GAME INITIALIZER] GameEngine init failed:', error);
+ console.error('[GAME INITIALIZER] Error details:', error.stack);
+ this.showNotification('Failed to initialize game engine', 'error');
+ });
+
+ } catch (error) {
+ console.error('[GAME INITIALIZER] Error creating GameEngine:', error);
+ this.showNotification('Error creating game engine', 'error');
+ }
+ }
+
+ onGameDataLoaded(data) {
+ console.log('[GAME INITIALIZER] Server game data loaded:', data);
+ console.log('[GAME INITIALIZER] Data success:', data.success);
+ console.log('[GAME INITIALIZER] Data content:', data.data);
+ console.log('[GAME INITIALIZER] Data keys:', data.data ? Object.keys(data.data) : 'No data object');
+
+ // Only process if we don't already have good data from authentication
+ if (data.success && data.data && Object.keys(data.data).length > 0 && !this.serverPlayerData) {
+ console.log('[GAME INITIALIZER] Using gameDataLoaded as primary source (no auth data available)');
+ this.serverPlayerData = data.data;
+
+ // Apply server data to game if game is running
+ if (window.game && window.game.loadServerPlayerData) {
+ window.game.loadServerPlayerData(data.data);
+
+ // Force UI refresh when server data is applied
+ if (window.game.systems && window.game.systems.ui && window.game.systems.ui.forceRefreshAllUI) {
+ window.game.systems.ui.forceRefreshAllUI();
+ }
+ }
+ } else {
+ console.log('[GAME INITIALIZER] Ignoring gameDataLoaded - already have data from authentication or data is empty');
+ }
+ }
+
+ onGameDataSaved(data) {
+ console.log('[GAME INITIALIZER] Server game data saved:', data);
+
+ if (data.success) {
+ this.showNotification('Game saved to server!', 'success');
+ } else {
+ this.showNotification(data.error || 'Failed to save to server', 'error');
+ }
+ }
+
onForceDisconnect(data) {
// Handle forced disconnection from server
console.warn('[GAME INITIALIZER] Force disconnected by server:', data);
@@ -211,7 +461,56 @@ class GameInitializer {
// Configure game for multiplayer mode
console.log('[GAME INITIALIZER] Configuring for multiplayer mode');
- window.game.setMultiplayerMode(true, this.socket, this.serverData, this.currentUser);
+ // Note: setMultiplayerMode already called in createGameEngineForMultiplayer() before initialization
+ window.game.gameInitializer = this; // Store reference for server polling
+
+ // DISABLE game logic in multiplayer - server is authoritative
+ if (window.game) {
+ console.log('[GAME INITIALIZER] Disabling client game logic - server is authoritative');
+
+ // Override game logic methods to do nothing in multiplayer
+ const originalUpdateGameLogic = window.game.updateGameLogic;
+ window.game.updateGameLogic = function() {
+ // In multiplayer mode, client does NO game logic
+ // Server is authoritative for ALL game data including credits
+ // Client is display-only
+ };
+
+ const originalStart = window.game.start;
+ window.game.start = function() {
+ console.log('[GAME ENGINE] Multiplayer mode - client does not run game logic, server is authoritative');
+ console.log('[GAME ENGINE] GameInitializer reference:', !!this.gameInitializer);
+ console.log('[GAME ENGINE] Socket reference:', !!(this.gameInitializer && this.gameInitializer.socket));
+ console.log('[GAME ENGINE] Game mode:', this.gameInitializer ? this.gameInitializer.gameMode : 'no gameInitializer');
+
+ this.isRunning = true;
+
+ // NO game logic timer - client is display-only
+ // Server handles all game logic including credit generation
+
+ // Start server data polling for UI updates
+ if (this.gameInitializer && this.gameInitializer.socket) {
+ console.log('[GAME ENGINE] Starting server data polling for UI updates');
+ this.serverPollTimer = setInterval(() => {
+ console.log('[GAME ENGINE] Polling server for data...');
+ this.gameInitializer.loadGameDataFromServer();
+ }, 5000); // Request updates every 5 seconds
+ } else {
+ console.warn('[GAME ENGINE] Cannot start server polling - no gameInitializer or socket');
+ }
+
+ // Only start UI updates that use server data (every second)
+ if (this.systems.ui) {
+ console.log('[GAME ENGINE] Starting multiplayer UI updates');
+ this.uiUpdateTimer = setInterval(() => {
+ if (this.systems && this.systems.ui && this.systems.ui.updateUI) {
+ console.log('[GAME ENGINE] Updating multiplayer UI with server data');
+ this.systems.ui.updateUI();
+ }
+ }, 1000);
+ }
+ };
+ }
// Game is already set up with save data, just start the game loop
if (window.game.start) {
@@ -230,8 +529,24 @@ class GameInitializer {
updateUIForMultiplayerMode() {
// Update UI elements to show multiplayer mode
const playerName = document.getElementById('playerName');
- if (playerName && this.currentUser) {
- playerName.textContent = this.currentUser.username;
+ const playerTitle = document.getElementById('playerTitle');
+ const playerUsername = document.getElementById('playerUsername');
+
+ if (this.currentUser) {
+ // Set the player name (rank/title)
+ if (playerName) {
+ playerName.textContent = 'Commander';
+ }
+
+ // Set the player title
+ if (playerTitle) {
+ playerTitle.textContent = '- Rookie Pilot';
+ }
+
+ // Set the username next to the level
+ if (playerUsername) {
+ playerUsername.textContent = this.currentUser.username + ' ';
+ }
}
// Show multiplayer-specific UI elements
@@ -340,10 +655,211 @@ class GameInitializer {
}
}
+ // Method to save game data to server (SECURE: only send specific updates)
+ saveGameDataToServer(gameData) {
+ if (this.socket && this.gameMode === 'multiplayer') {
+ console.log('[GAME INITIALIZER] Sending secure game updates to server');
+
+ // Only send specific, validated updates - not entire game state
+ const secureUpdates = {
+ playerStats: {
+ credits: gameData.player?.credits || 0,
+ level: gameData.player?.level || 1,
+ experience: gameData.player?.experience || 0,
+ playTime: gameData.player?.playTime || 0
+ },
+ timestamp: Date.now()
+ };
+
+ // Send only the specific updates for server validation
+ this.socket.emit('updatePlayerStats', secureUpdates);
+ }
+ }
+
+ // Method to load game data from server
+ loadGameDataFromServer() {
+ if (this.socket && this.gameMode === 'multiplayer') {
+ console.log('[GAME INITIALIZER] Loading game data from server');
+ console.log('[GAME INITIALIZER] Socket available:', !!this.socket);
+ console.log('[GAME INITIALIZER] Game mode:', this.gameMode);
+ console.log('[GAME INITIALIZER] Current user:', this.currentUser);
+ console.log('[GAME INITIALIZER] Emitting loadGameData event with username');
+
+ // Get username from current user or fallback to stored user
+ let username = 'anonymous'; // default fallback
+ if (this.currentUser?.username) {
+ username = this.currentUser.username;
+ } else {
+ // Try to get from localStorage as fallback
+ const storedUser = localStorage.getItem('currentUser');
+ if (storedUser) {
+ try {
+ const user = JSON.parse(storedUser);
+ username = user.username || 'anonymous';
+ } catch (e) {
+ console.warn('[GAME INITIALIZER] Failed to parse stored user, using default');
+ }
+ }
+ }
+
+ console.log('[GAME INITIALIZER] Using username for data load:', username);
+
+ // Send the username to load the correct player data
+ this.socket.emit('loadGameData', {
+ username: username
+ });
+ } else {
+ console.log('[GAME INITIALIZER] Cannot load game data - socket or multiplayer mode not available');
+ console.log('[GAME INITIALIZER] Socket available:', !!this.socket);
+ console.log('[GAME INITIALIZER] Game mode:', this.gameMode);
+ }
+ }
+
+ // Method to authenticate with server
+ authenticateWithServer() {
+ console.log('[GAME INITIALIZER] authenticateWithServer called');
+ console.log('[GAME INITIALIZER] Socket available:', !!this.socket);
+ console.log('[GAME INITIALIZER] Game mode:', this.gameMode);
+ console.log('[GAME INITIALIZER] Current user:', this.currentUser);
+
+ if (this.socket && this.currentUser) {
+ console.log('[GAME INITIALIZER] Sending authentication to server');
+ this.socket.emit('authenticate', {
+ userId: this.currentUser.userId,
+ username: this.currentUser.username
+ });
+ } else {
+ console.warn('[GAME INITIALIZER] Cannot authenticate - missing socket or user data');
+ if (!this.socket) {
+ console.warn('[GAME INITIALIZER] Socket is null/undefined');
+ }
+ if (!this.currentUser) {
+ console.warn('[GAME INITIALIZER] Current user is null/undefined');
+ }
+ }
+ }
+
+ onOfflineRewardsClaimed(data) {
+ if (data.success) {
+ if (data.rewards.credits > 0 || data.rewards.experience > 0) {
+ // Apply rewards to player
+ if (window.game && window.game.systems) {
+ if (data.rewards.credits > 0) {
+ window.game.systems.economy.addCredits(data.rewards.credits, 'offline');
+ }
+ if (data.rewards.experience > 0) {
+ window.game.systems.player.addExperience(data.rewards.experience);
+ }
+
+ // Show success message
+ let message = 'Offline rewards claimed!\n';
+ if (data.rewards.credits > 0) message += `+${data.rewards.credits} credits\n`;
+ if (data.rewards.experience > 0) message += `+${data.rewards.experience} experience\n`;
+
+ window.game.showNotification(message, 'success', 5000);
+ }
+ } else {
+ window.game.showNotification('No offline rewards available', 'info', 3000);
+ }
+ } else {
+ window.game.showNotification(`Failed to claim offline rewards: ${data.error}`, 'error', 5000);
+ }
+ }
+
+ onOnlineIdleRewards(data) {
+ if (window.game && window.game.systems) {
+ // Update player balance with online idle rewards
+ if (data.credits > 0) {
+ // The server already updated the balance, just show notification
+ window.game.showNotification(`+${data.credits} credits (online idle)`, 'success', 2000);
+ }
+ }
+ }
+
+ onPlayTimeUpdated(data) {
+ console.log('[GAME INITIALIZER] PlayTime updated from server:', data);
+
+ if (window.game && window.game.systems && window.game.systems.player) {
+ const player = window.game.systems.player;
+
+ // Update playTime from server
+ player.stats.playTime = data.playTime;
+
+ console.log('[GAME INITIALIZER] Updated local playTime to:', data.playTime, 'ms');
+ console.log('[GAME INITIALIZER] PlayTime in hours:', data.playTime / (1000 * 60 * 60), 'hours');
+
+ // Update UI
+ player.updateUI();
+ }
+ }
+
+ onPurchaseCompleted(data) {
+ if (data.success) {
+ // Update local player data with server response
+ if (window.game && window.game.systems && window.game.systems.economy) {
+ const economy = window.game.systems.economy;
+
+ // Update currency balance
+ if (data.currency === 'credits') {
+ economy.credits = data.newBalance;
+ } else if (data.currency === 'gems') {
+ economy.gems = data.newBalance;
+ }
+
+ // Request fresh economy data from server to ensure sync
+ if (economy.requestEconomyData) {
+ setTimeout(() => {
+ economy.requestEconomyData();
+ }, 500);
+ }
+
+ // Update UI
+ economy.updateUI();
+
+ // Show success message
+ window.game.showNotification(`Purchased ${data.item.name}!`, 'success', 3000);
+ }
+ } else {
+ // Show error message
+ window.game.showNotification(`Purchase failed: ${data.error}`, 'error', 5000);
+ }
+ }
+
+ onShopItemsReceived(data) {
+ if (data.success && window.game && window.game.systems && window.game.systems.itemSystem) {
+ // Update ItemSystem with server data
+ window.game.systems.itemSystem.processServerItems(data.items);
+ console.log('[GAME INITIALIZER] ItemSystem updated with server shop items');
+
+ // Update Economy shop UI
+ if (window.game.systems.economy) {
+ window.game.systems.economy.updateShopUI();
+ console.log('[GAME INITIALIZER] Economy shop UI updated');
+ }
+ } else {
+ console.warn('[GAME INITIALIZER] Failed to receive shop items:', data);
+ }
+ }
+
+ onItemDetailsReceived(data) {
+ // This is handled by the ItemSystem directly
+ // Just log for debugging
+ if (data.success) {
+ console.log('[GAME INITIALIZER] Item details received for:', data.item.name);
+ } else {
+ console.warn('[GAME INITIALIZER] Failed to receive item details:', data);
+ }
+ }
+
// Cleanup method
cleanup() {
console.log('[GAME INITIALIZER] Cleaning up');
+ // Reset SmartSaveManager to singleplayer mode
+ if (window.smartSaveManager) {
+ window.smartSaveManager.setMultiplayerMode(false);
+ }
+
if (this.socket) {
this.socket.disconnect();
this.socket = null;
@@ -353,12 +869,26 @@ class GameInitializer {
this.serverData = null;
this.authToken = null;
this.currentUser = null;
+ this.serverPlayerData = null;
}
}
// Create global instance
window.gameInitializer = new GameInitializer();
+// Make force disconnect available globally for testing
+window.forceDisconnectMultiplayer = function() {
+ if (window.gameInitializer && window.gameInitializer.forceDisconnect) {
+ window.gameInitializer.forceDisconnect();
+ } else {
+ console.log('[GAME INITIALIZER] GameInitializer not available for force disconnect');
+ }
+};
+
+// Debug: Log the global instance immediately
+console.log('[GAME INITIALIZER] Global instance created:', window.gameInitializer);
+console.log('[GAME INITIALIZER] Global instance gameServerUrl:', window.gameInitializer.gameServerUrl);
+
// Export for use in other scripts
if (typeof module !== 'undefined' && module.exports) {
module.exports = GameInitializer;
diff --git a/Client/js/SaveSystemIntegration.js b/Client/js/SaveSystemIntegration.js
new file mode 100644
index 0000000..85f5e2e
--- /dev/null
+++ b/Client/js/SaveSystemIntegration.js
@@ -0,0 +1,325 @@
+/**
+ * Save System Integration
+ * Integrates SmartSaveManager with existing game systems
+ */
+
+console.log('[SAVE INTEGRATION] Save system integration loading');
+
+// Override the game's save method to use SmartSaveManager
+function integrateWithGameEngine() {
+ if (window.game && window.game.save) {
+ // Store original save method
+ const originalSave = window.game.save;
+
+ // Override game save method
+ window.game.save = async function() {
+ // console.log('[SAVE INTEGRATION] Game save called');
+
+ if (window.smartSaveManager) {
+ await window.smartSaveManager.save();
+ } else {
+ // Fallback to original save if SmartSaveManager not available
+ return await originalSave.call(this);
+ }
+ };
+
+ console.log('[SAVE INTEGRATION] Game save method overridden');
+ }
+}
+
+// Override the game's load method to use SmartSaveManager
+function integrateLoadSystem() {
+ if (window.game && window.game.load) {
+ // Store original load method
+ const originalLoad = window.game.load;
+
+ // Override load method
+ window.game.load = async function(saveData = null) {
+ console.log('[SAVE INTEGRATION] Game load called, using SmartSaveManager');
+
+ try {
+ let dataToLoad = saveData;
+
+ // If no data provided, use SmartSaveManager
+ if (!dataToLoad && window.smartSaveManager) {
+ dataToLoad = await window.smartSaveManager.loadPlayerData();
+ }
+
+ // Load the data
+ if (dataToLoad) {
+ if (this.loadPlayerData) {
+ this.loadPlayerData(dataToLoad);
+ } else {
+ // Fallback to original load
+ return await originalLoad.call(this, dataToLoad);
+ }
+
+ console.log('[SAVE INTEGRATION] Game data loaded successfully');
+ return true;
+ } else {
+ console.log('[SAVE INTEGRATION] No save data found, starting fresh');
+ return false;
+ }
+ } catch (error) {
+ console.error('[SAVE INTEGRATION] Load error:', error);
+ return false;
+ }
+ };
+
+ console.log('[SAVE INTEGRATION] Game load method overridden');
+ }
+}
+
+// Add server data loading method to game
+function addServerDataSupport() {
+ if (window.game) {
+ // Store pending server data for later application
+ window.game.pendingServerData = null;
+
+ window.game.loadServerPlayerData = function(serverData) {
+ console.log('[SAVE INTEGRATION] Loading server player data into game');
+ console.log('[SAVE INTEGRATION] Server data received:', serverData);
+ console.log('[SAVE INTEGRATION] Server data type:', typeof serverData);
+ console.log('[SAVE INTEGRATION] Server data keys:', serverData ? Object.keys(serverData) : 'No data');
+ console.log('[SAVE INTEGRATION] Game systems available:', this.systems ? Object.keys(this.systems) : 'No systems');
+
+ // Store server data for later if systems aren't ready
+ if (!this.systems || Object.keys(this.systems).length === 0) {
+ console.log('[SAVE INTEGRATION] Game systems not ready, storing data for later');
+ this.pendingServerData = serverData;
+ return;
+ }
+
+ console.log('[SAVE INTEGRATION] Game systems ready, applying server data now');
+
+ try {
+ // Apply player stats
+ if (serverData.stats && this.systems && this.systems.player) {
+ console.log('[SAVE INTEGRATION] Applying player stats:', serverData.stats);
+ console.log('[SAVE INTEGRATION] Player system methods:', Object.getOwnPropertyNames(Object.getPrototypeOf(this.systems.player)));
+
+ // Check if load method exists
+ if (typeof this.systems.player.load === 'function') {
+ this.systems.player.load(serverData.stats);
+ console.log('[SAVE INTEGRATION] Player stats applied successfully');
+ console.log('[SAVE INTEGRATION] Updated player stats:', this.systems.player.stats);
+ } else {
+ console.warn('[SAVE INTEGRATION] Player system has no load method, trying direct assignment');
+ // Direct assignment as fallback
+ if (this.systems.player.stats) {
+ Object.assign(this.systems.player.stats, serverData.stats);
+ console.log('[SAVE INTEGRATION] Player stats assigned directly:', this.systems.player.stats);
+ }
+ }
+ } else {
+ console.warn('[SAVE INTEGRATION] No player system or stats in server data');
+ console.log('[SAVE INTEGRATION] Has serverData.stats:', !!serverData?.stats);
+ console.log('[SAVE INTEGRATION] Has systems.player:', !!(this.systems?.player));
+ }
+
+ // Apply inventory
+ if (serverData.inventory && this.systems && this.systems.inventory) {
+ console.log('[SAVE INTEGRATION] Applying player inventory:', serverData.inventory);
+ if (typeof this.systems.inventory.load === 'function') {
+ this.systems.inventory.load(serverData.inventory);
+ } else {
+ console.warn('[SAVE INTEGRATION] Inventory system has no load method');
+ }
+ }
+
+ // Apply ship data
+ if (serverData.ship && this.systems && this.systems.ship) {
+ console.log('[SAVE INTEGRATION] Applying player ship:', serverData.ship);
+ if (typeof this.systems.ship.load === 'function') {
+ this.systems.ship.load(serverData.ship);
+ } else {
+ console.warn('[SAVE INTEGRATION] Ship system has no load method');
+ }
+ }
+
+ // Apply base data
+ if (serverData.base && this.systems && this.systems.base) {
+ console.log('[SAVE INTEGRATION] Applying player base:', serverData.base);
+ if (typeof this.systems.base.load === 'function') {
+ this.systems.base.load(serverData.base);
+ } else {
+ console.warn('[SAVE INTEGRATION] Base system has no load method');
+ }
+ }
+
+ // Show notification
+ if (this.showNotification) {
+ this.showNotification(`Welcome back! Level ${serverData.stats?.level || 1}`, 'success', 3000);
+ }
+
+ console.log('[SAVE INTEGRATION] Server player data application completed');
+
+ // Force UI update
+ if (this.systems && this.systems.ui && this.systems.ui.updateUI) {
+ this.systems.ui.updateUI();
+ console.log('[SAVE INTEGRATION] Server player data application completed');
+ }
+
+ // Apply pending server data if any exists
+ if (this.pendingServerData) {
+ console.log('[SAVE INTEGRATION] Applying pending server data');
+ this.loadServerPlayerData(this.pendingServerData);
+ this.pendingServerData = null;
+ }
+ } catch (error) {
+ console.error('[SAVE INTEGRATION] Error applying server player data:', error);
+ if (this.showNotification) {
+ this.showNotification('Failed to load server data!', 'error', 3000);
+ }
+ }
+ };
+
+ // Method to check and apply pending server data
+ window.game.checkAndApplyPendingServerData = function() {
+ if (this.pendingServerData && this.systems && Object.keys(this.systems).length > 0) {
+ console.log('[SAVE INTEGRATION] Systems ready, applying pending server data');
+ this.loadServerPlayerData(this.pendingServerData);
+ this.pendingServerData = null;
+ }
+ };
+
+ // Fallback loadPlayerData method if GameEngine doesn't have it
+ if (!window.game.loadPlayerData) {
+ window.game.loadPlayerData = window.game.loadServerPlayerData;
+ }
+
+ console.log('[SAVE INTEGRATION] Server data support added to game');
+ }
+}
+
+// Add save mode switching to UI
+function addSaveModeUI() {
+ // Add save mode indicator to UI
+ const createSaveModeIndicator = () => {
+ const indicator = document.createElement('div');
+ indicator.id = 'save-mode-indicator';
+ indicator.style.cssText = `
+ position: fixed;
+ bottom: 10px;
+ right: 10px;
+ background: rgba(0, 0, 0, 0.8);
+ color: white;
+ padding: 8px 12px;
+ border-radius: 4px;
+ font-size: 12px;
+ z-index: 10000;
+ display: none;
+ `;
+ document.body.appendChild(indicator);
+ return indicator;
+ };
+
+ const updateSaveModeIndicator = () => {
+ const indicator = document.getElementById('save-mode-indicator') || createSaveModeIndicator();
+
+ if (window.smartSaveManager) {
+ const info = window.smartSaveManager.getSaveInfo();
+ indicator.textContent = `Save: ${info.saveLocation}`;
+ indicator.style.display = 'block';
+
+ // Color code based on mode
+ indicator.style.background = info.isMultiplayer ? 'rgba(0, 100, 200, 0.8)' : 'rgba(0, 150, 0, 0.8)';
+ }
+ };
+
+ // Update indicator when mode changes
+ if (window.smartSaveManager) {
+ const originalSetMultiplayerMode = window.smartSaveManager.setMultiplayerMode;
+ window.smartSaveManager.setMultiplayerMode = function(...args) {
+ originalSetMultiplayerMode.apply(this, args);
+ updateSaveModeIndicator();
+ };
+ }
+
+ // Initial update
+ setTimeout(updateSaveModeIndicator, 1000);
+}
+
+// Debug function to check data flow
+function debugDataFlow() {
+ console.log('[DEBUG] === DATA FLOW DEBUG ===');
+
+ // Check GameInitializer
+ if (window.gameInitializer) {
+ console.log('[DEBUG] GameInitializer exists:', !!window.gameInitializer);
+ console.log('[DEBUG] GameInitializer serverPlayerData:', window.gameInitializer.serverPlayerData);
+ console.log('[DEBUG] GameInitializer gameMode:', window.gameInitializer.gameMode);
+ } else {
+ console.log('[DEBUG] GameInitializer NOT found');
+ }
+
+ // Check game systems
+ if (window.game) {
+ console.log('[DEBUG] Game exists:', !!window.game);
+ console.log('[DEBUG] Game systems:', window.game.systems ? Object.keys(window.game.systems) : 'No systems');
+
+ if (window.game.systems && window.game.systems.player) {
+ console.log('[DEBUG] Player system exists:', !!window.game.systems.player);
+ console.log('[DEBUG] Player stats:', window.game.systems.player.stats);
+ console.log('[DEBUG] Player credits:', window.game.systems.player.stats?.credits);
+ }
+ } else {
+ console.log('[DEBUG] Game NOT found');
+ }
+
+ // Check SmartSaveManager
+ if (window.smartSaveManager) {
+ console.log('[DEBUG] SmartSaveManager exists:', !!window.smartSaveManager);
+ console.log('[DEBUG] SmartSaveManager mode:', window.smartSaveManager.isMultiplayer ? 'multiplayer' : 'singleplayer');
+ } else {
+ console.log('[DEBUG] SmartSaveManager NOT found');
+ }
+
+ console.log('[DEBUG] === END DEBUG ===');
+}
+
+// Debug function available for manual testing
+window.debugDataFlow = debugDataFlow;
+
+// Enhanced debug function for connection testing
+window.debugConnectionState = function() {
+ console.log('=== CONNECTION STATE DEBUG ===');
+ console.log('GameInitializer exists:', !!window.gameInitializer);
+ console.log('GameInitializer socket connected:', !!window.gameInitializer?.socket?.connected);
+ console.log('GameInitializer gameMode:', window.gameInitializer?.gameMode);
+ console.log('GameInitializer serverPlayerData:', !!window.gameInitializer?.serverPlayerData);
+ console.log('SmartSaveManager exists:', !!window.smartSaveManager);
+ console.log('SmartSaveManager mode:', window.smartSaveManager?.isMultiplayer ? 'multiplayer' : 'singleplayer');
+ console.log('Game exists:', !!window.game);
+ console.log('Game isRunning:', window.game?.isRunning);
+ console.log('=== END CONNECTION DEBUG ===');
+};
+
+// Initialize integration when DOM is ready
+function initializeIntegration() {
+ console.log('[SAVE INTEGRATION] Initializing save system integration');
+
+ // Wait for game to be ready
+ const checkGameReady = () => {
+ if (window.game) {
+ integrateWithGameEngine();
+ integrateLoadSystem();
+ addServerDataSupport();
+ addSaveModeUI();
+ console.log('[SAVE INTEGRATION] Integration complete');
+ } else {
+ setTimeout(checkGameReady, 500);
+ }
+ };
+
+ checkGameReady();
+}
+
+// Auto-initialize
+if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', initializeIntegration);
+} else {
+ initializeIntegration();
+}
+
+console.log('[SAVE INTEGRATION] Save system integration loaded');
diff --git a/Client/js/SmartSaveManager.js b/Client/js/SmartSaveManager.js
new file mode 100644
index 0000000..91234e1
--- /dev/null
+++ b/Client/js/SmartSaveManager.js
@@ -0,0 +1,228 @@
+/**
+ * Smart Save Manager
+ * Intelligently handles save data for both singleplayer and multiplayer modes
+ */
+
+class SmartSaveManager {
+ constructor() {
+ this.isMultiplayer = false;
+ this.serverPlayerData = null;
+ this.localSaveData = null;
+ this.gameInitializer = null;
+
+ console.log('[SMART SAVE] SmartSaveManager initialized');
+ }
+
+ setMultiplayerMode(isMultiplayer, gameInitializer = null) {
+ const oldMode = this.isMultiplayer;
+ this.isMultiplayer = isMultiplayer;
+ this.gameInitializer = gameInitializer;
+
+ console.log(`[SMART SAVE] Mode change: ${oldMode ? 'multiplayer' : 'singleplayer'} -> ${isMultiplayer ? 'multiplayer' : 'singleplayer'}`);
+ console.log(`[SMART SAVE] Set to ${isMultiplayer ? 'multiplayer' : 'singleplayer'} mode`);
+
+ if (isMultiplayer && gameInitializer) {
+ // Load server data when switching to multiplayer
+ this.loadServerData();
+ }
+ }
+
+ // Load player data (intelligently chooses source)
+ async loadPlayerData() {
+ if (this.isMultiplayer) {
+ return await this.loadServerData();
+ } else {
+ return await this.loadLocalData();
+ }
+ }
+
+ // Save player data (intelligently chooses destination)
+ async savePlayerData(gameData) {
+ if (this.isMultiplayer) {
+ return await this.saveServerData(gameData);
+ } else {
+ return await this.saveLocalData(gameData);
+ }
+ }
+
+ // Load server data
+ async loadServerData() {
+ try {
+ if (!this.gameInitializer || !this.gameInitializer.socket) {
+ // Don't warn during initialization - this is expected before socket is ready
+ // console.warn('[SMART SAVE] No multiplayer connection available');
+ return null;
+ }
+
+ console.log('[SMART SAVE] Loading server player data');
+
+ // Request data from server
+ this.gameInitializer.loadGameDataFromServer();
+
+ // Return cached server data if available
+ return this.serverPlayerData;
+ } catch (error) {
+ console.error('[SMART SAVE] Error loading server data:', error);
+ return null;
+ }
+ }
+
+ // Save server data (DISABLED - client should not send data to server)
+ async saveServerData(gameData) {
+ console.warn('[SMART SAVE] Client save disabled - server is authoritative');
+ return true; // Pretend it worked to avoid client errors
+ }
+
+ // Load local data
+ async loadLocalData() {
+ try {
+ console.log('[SMART SAVE] Loading local save data');
+
+ // Use existing local save system
+ if (window.mainMenu && window.mainMenu.loadGame) {
+ const saveData = await window.mainMenu.loadGame(1); // Load slot 1
+ this.localSaveData = saveData;
+ return saveData;
+ }
+
+ // Fallback to localStorage
+ const saveKey = 'gso_save_slot_1';
+ const saveData = localStorage.getItem(saveKey);
+
+ if (saveData) {
+ const parsed = JSON.parse(saveData);
+ this.localSaveData = parsed;
+ return parsed;
+ }
+
+ return null;
+ } catch (error) {
+ console.error('[SMART SAVE] Error loading local data:', error);
+ return null;
+ }
+ }
+
+ // Save local data
+ async saveLocalData(gameData) {
+ try {
+ // Don't save locally when in multiplayer mode
+ if (this.isMultiplayer) {
+ console.log('[SMART SAVE] Skipping local save - in multiplayer mode');
+ return true;
+ }
+
+ console.log('[SMART SAVE] Saving locally');
+
+ // Use existing local save system
+ if (window.mainMenu && window.mainMenu.saveGame) {
+ await window.mainMenu.saveGame(1, gameData); // Save to slot 1
+ this.localSaveData = gameData;
+ return true;
+ }
+
+ // Fallback to localStorage
+ const saveKey = 'gso_save_slot_1';
+ localStorage.setItem(saveKey, JSON.stringify(gameData));
+ this.localSaveData = gameData;
+
+ return true;
+ } catch (error) {
+ console.error('[SMART SAVE] Error saving local data:', error);
+ return false;
+ }
+ }
+
+ // Apply server data to game
+ applyServerDataToGame(serverData) {
+ console.log('[SMART SAVE] Applying server data to game');
+
+ this.serverPlayerData = serverData;
+
+ // Apply to game if game is running (try both methods)
+ if (window.game) {
+ console.log('[SMART SAVE] Game is available, checking for data loading methods');
+ console.log('[SMART SAVE] - loadPlayerData:', !!window.game.loadPlayerData);
+ console.log('[SMART SAVE] - loadServerPlayerData:', !!window.game.loadServerPlayerData);
+
+ if (window.game.loadServerPlayerData) {
+ console.log('[SMART SAVE] Using loadServerPlayerData method');
+ window.game.loadServerPlayerData(serverData);
+ console.log('[SMART SAVE] Server data applied to game, forcing UI refresh');
+
+ // Force UI refresh after applying server data
+ if (window.game.systems && window.game.systems.ui && window.game.systems.ui.forceRefreshAllUI) {
+ console.log('[SMART SAVE] Forcing UI refresh after server data application');
+ window.game.systems.ui.forceRefreshAllUI();
+ } else {
+ console.warn('[SMART SAVE] UI refresh not available after server data application - systems:', !!window.game.systems, 'ui:', !!window.game.systems?.ui, 'forceRefreshAllUI:', !!window.game.systems?.ui?.forceRefreshAllUI);
+ }
+ } else if (window.game.loadPlayerData) {
+ console.log('[SMART SAVE] Using loadPlayerData method');
+ window.game.loadPlayerData(serverData);
+ console.log('[SMART SAVE] Server data applied to game, forcing UI refresh');
+
+ // Force UI refresh after applying server data
+ if (window.game.systems && window.game.systems.ui && window.game.systems.ui.forceRefreshAllUI) {
+ console.log('[SMART SAVE] Forcing UI refresh after server data application');
+ window.game.systems.ui.forceRefreshAllUI();
+ } else {
+ console.warn('[SMART SAVE] UI refresh not available after server data application - systems:', !!window.game.systems, 'ui:', !!window.game.systems?.ui, 'forceRefreshAllUI:', !!window.game.systems?.ui?.forceRefreshAllUI);
+
+ // Try delayed UI refresh since UIManager might not be ready yet
+ console.log('[SMART SAVE] Attempting delayed UI refresh...');
+ setTimeout(() => {
+ if (window.game.systems && window.game.systems.ui && window.game.systems.ui.forceRefreshAllUI) {
+ console.log('[SMART SAVE] Delayed UI refresh successful');
+ window.game.systems.ui.forceRefreshAllUI();
+ } else {
+ console.warn('[SMART SAVE] Delayed UI refresh also failed - systems:', !!window.game.systems, 'ui:', !!window.game.systems?.ui, 'forceRefreshAllUI:', !!window.game.systems?.ui?.forceRefreshAllUI);
+ }
+ }, 2000); // Wait 2 seconds for systems to initialize
+ }
+ } else {
+ console.warn('[SMART SAVE] No data loading method available on game object');
+ }
+ } else {
+ console.warn('[SMART SAVE] Game not available for data application');
+ }
+
+ // Store for game engine
+ if (window.gameInitializer) {
+ window.gameInitializer.serverPlayerData = serverData;
+ }
+ }
+
+ // Get current save source info
+ getSaveInfo() {
+ return {
+ isMultiplayer: this.isMultiplayer,
+ hasServerData: !!this.serverPlayerData,
+ hasLocalData: !!this.localSaveData,
+ saveLocation: this.isMultiplayer ? 'Server Database' : 'Local Storage'
+ };
+ }
+
+ // Sync data between local and server (for migration)
+ async syncData(direction = 'toServer') {
+ if (direction === 'toServer') {
+ // Upload local data to server
+ const localData = await this.loadLocalData();
+ if (localData) {
+ await this.saveServerData(localData);
+ console.log('[SMART SAVE] Synced local data to server');
+ }
+ } else {
+ // Download server data to local
+ const serverData = await this.loadServerData();
+ if (serverData) {
+ await this.saveLocalData(serverData);
+ console.log('[SMART SAVE] Synced server data to local');
+ }
+ }
+ }
+}
+
+// Create global instance
+window.smartSaveManager = new SmartSaveManager();
+
+console.log('[SMART SAVE] SmartSaveManager loaded and available globally');
diff --git a/Client/js/core/DebugLogger.js b/Client/js/core/DebugLogger.js
index ad59a1f..947f156 100644
--- a/Client/js/core/DebugLogger.js
+++ b/Client/js/core/DebugLogger.js
@@ -5,7 +5,9 @@
class DebugLogger {
constructor() {
- this.debugEnabled = true; // Always enabled
+ // Completely disable debug logging to prevent console flooding
+ this.debugEnabled = false;
+
this.startTime = performance.now();
this.stepTimers = new Map();
this.debugLogs = []; // Store logs in memory
@@ -15,10 +17,15 @@ class DebugLogger {
this.logger = window.logger || null;
// Log initialization
- this.log('=== DEBUG SESSION STARTED ===');
+ if (this.debugEnabled) {
+ this.log('=== DEBUG SESSION STARTED ===');
+ }
}
async log(message, data = null) {
+ // Skip logging if debug is disabled
+ if (!this.debugEnabled) return;
+
const timestamp = new Date().toISOString();
const stackTrace = new Error().stack;
@@ -55,13 +62,13 @@ class DebugLogger {
this.debugLogs.shift();
}
- // Always log to console
- console.log(`[DEBUG] ${message}`, data || '');
+ // Skip console logging to prevent flooding
+ // console.log(`[DEBUG] ${message}`, data || '');
- // Log performance data to console
- if (performanceData.memory) {
- console.log(`[PERF] ${performanceData.elapsed} | Memory: ${performanceData.memory.used}/${performanceData.memory.total}`);
- }
+ // Skip performance logging to prevent flooding
+ // if (performanceData.memory) {
+ // console.log(`[PERF] ${performanceData.elapsed} | Memory: ${performanceData.memory.used}/${performanceData.memory.total}`);
+ // }
// Use existing logger if available
if (this.logger) {
@@ -79,6 +86,9 @@ class DebugLogger {
}
async startStep(stepName) {
+ // Skip logging if debug is disabled
+ if (!this.debugEnabled) return;
+
this.stepTimers.set(stepName, performance.now());
await this.log(`STEP START: ${stepName}`, {
type: 'step_start',
@@ -88,6 +98,9 @@ class DebugLogger {
}
async endStep(stepName, data = null) {
+ // Skip logging if debug is disabled
+ if (!this.debugEnabled) return;
+
const startTime = this.stepTimers.get(stepName);
const duration = startTime ? (performance.now() - startTime).toFixed(2) : 'N/A';
@@ -101,6 +114,9 @@ class DebugLogger {
}
async logStep(stepName, data = null) {
+ // Skip logging if debug is disabled
+ if (!this.debugEnabled) return;
+
await this.log(`STEP: ${stepName}`, {
type: 'step',
step: stepName,
diff --git a/Client/js/core/Economy.js b/Client/js/core/Economy.js
index ecdea9c..d9a3f52 100644
--- a/Client/js/core/Economy.js
+++ b/Client/js/core/Economy.js
@@ -1,21 +1,24 @@
/**
* Galaxy Strike Online - Economy System
- * Manages credits, gems, transactions, and shop
+ * Manages player currency, transactions, and shop functionality
+ * Now uses server-side ItemSystem for all item data
*/
class Economy {
constructor(gameEngine) {
- const debugLogger = window.debugLogger;
- if (debugLogger) debugLogger.log('Economy constructor called');
-
this.game = gameEngine;
- // Currency
- this.credits = 1000;
- this.gems = 10;
- this.premiumCurrency = 0; // For future premium features
+ // Preserve existing economy data if available (prevents wipe during re-initialization)
+ const existingEconomy = window.game?.systems?.economy;
+ const preservedCredits = existingEconomy?.credits || 0;
+ const preservedGems = existingEconomy?.gems || 0;
- // Transaction history
+ // Player resources
+ this.credits = preservedCredits;
+ this.gems = preservedGems;
+ this.premiumCurrency = 0;
+
+ // Transaction tracking
this.transactionHistory = [];
this.transactions = []; // Add missing transactions array
@@ -25,1324 +28,125 @@ class Economy {
// Shop categories
this.shopCategories = {
ships: 'Ships',
- weapons: 'Weapons',
- armors: 'Armors',
- materials: 'Materials',
+ weapons: 'Weapons',
+ modules: 'Modules',
cosmetics: 'Cosmetics',
- // upgrades: 'Upgrades', // Temporarily disabled
consumables: 'Consumables',
- buildings: 'Buildings'
+ materials: 'Materials'
};
- // Random shop system
+ // Random shop system - now uses server ItemSystem
this.randomShopItems = {}; // Current random items per category
this.shopRefreshInterval = null; // Timer for 2-hour refresh
this.shopHeartbeatInterval = null; // Timer for live countdown updates
this.lastShopRefresh = null; // Timestamp of last refresh
this.SHOP_REFRESH_INTERVAL = 2 * 60 * 60 * 1000; // 2 hours in milliseconds
- this.MAX_ITEMS_PER_CATEGORY = 6;
+ this.MAX_ITEMS_PER_CATEGORY = 8;
this.categoryPurchaseLimits = {}; // Track purchases per category per refresh
- // Shop items
- this.shopItems = {
- ships: [
- // Starter Cruiser Variants
- {
- id: 'starter_cruiser_common',
- name: 'Starter Cruiser',
- type: 'ship',
- rarity: 'common',
- price: 5000,
- currency: 'credits',
- description: 'Reliable starter cruiser for new pilots',
- texture: 'assets/textures/ships/starter_cruiser.png',
- stats: { attack: 15, speed: 10, defense: 12, hull: 100 }
- },
- {
- id: 'starter_cruiser_uncommon',
- name: 'Starter Cruiser II',
- type: 'ship',
- rarity: 'uncommon',
- price: 12000,
- currency: 'credits',
- description: 'Upgraded starter cruiser with enhanced systems',
- texture: 'assets/textures/ships/starter_cruiser.png',
- stats: { attack: 18, speed: 12, defense: 15, hull: 120 }
- },
- {
- id: 'starter_cruiser_rare',
- name: 'Starter Cruiser III',
- type: 'ship',
- rarity: 'rare',
- price: 25000,
- currency: 'credits',
- description: 'Elite starter cruiser with advanced weaponry',
- texture: 'assets/textures/ships/starter_cruiser.png',
- stats: { attack: 22, speed: 14, defense: 18, hull: 145 }
- },
- {
- id: 'starter_cruiser_epic',
- name: 'Starter Cruiser IV',
- type: 'ship',
- rarity: 'epic',
- price: 45000,
- currency: 'credits',
- description: 'Master starter cruiser with elite modifications',
- texture: 'assets/textures/ships/starter_cruiser.png',
- stats: { attack: 26, speed: 16, defense: 22, hull: 175 }
- },
- {
- id: 'starter_cruiser_legendary',
- name: 'Starter Cruiser V',
- type: 'ship',
- rarity: 'legendary',
- price: 75000,
- currency: 'credits',
- description: 'Legendary starter cruiser with unparalleled performance',
- texture: 'assets/textures/ships/starter_cruiser.png',
- stats: { attack: 32, speed: 18, defense: 28, hull: 210 }
- },
-
- // Light Destroyer Variants
- {
- id: 'light_destroyer_common',
- name: 'Light Destroyer',
- type: 'ship',
- rarity: 'common',
- price: 12000,
- currency: 'credits',
- description: 'Fast and maneuverable light destroyer',
- texture: 'assets/textures/ships/light_destroyer.png',
- stats: { attack: 18, speed: 18, defense: 10, hull: 80 }
- },
- {
- id: 'light_destroyer_uncommon',
- name: 'Light Destroyer II',
- type: 'ship',
- rarity: 'uncommon',
- price: 28000,
- currency: 'credits',
- description: 'Enhanced light destroyer with improved speed',
- texture: 'assets/textures/ships/light_destroyer.png',
- stats: { attack: 22, speed: 22, defense: 12, hull: 95 }
- },
- {
- id: 'light_destroyer_rare',
- name: 'Light Destroyer III',
- type: 'ship',
- rarity: 'rare',
- price: 55000,
- currency: 'credits',
- description: 'Elite light destroyer with superior agility',
- texture: 'assets/textures/ships/light_destroyer.png',
- stats: { attack: 26, speed: 26, defense: 15, hull: 115 }
- },
- {
- id: 'light_destroyer_epic',
- name: 'Light Destroyer IV',
- type: 'ship',
- rarity: 'epic',
- price: 95000,
- currency: 'credits',
- description: 'Master light destroyer with exceptional speed',
- texture: 'assets/textures/ships/light_destroyer.png',
- stats: { attack: 30, speed: 30, defense: 18, hull: 140 }
- },
- {
- id: 'light_destroyer_legendary',
- name: 'Light Destroyer V',
- type: 'ship',
- rarity: 'legendary',
- price: 150000,
- currency: 'credits',
- description: 'Legendary light destroyer with unmatched velocity',
- texture: 'assets/textures/ships/light_destroyer.png',
- stats: { attack: 36, speed: 36, defense: 22, hull: 170 }
- },
-
- // Heavy Destroyer Variants
- {
- id: 'heavy_destroyer_common',
- name: 'Heavy Destroyer',
- type: 'ship',
- rarity: 'common',
- price: 25000,
- currency: 'credits',
- description: 'Powerful heavy destroyer with strong weapons',
- texture: 'assets/textures/ships/heavy_destroyer.png',
- stats: { attack: 25, speed: 12, defense: 18, hull: 120 }
- },
- {
- id: 'heavy_destroyer_uncommon',
- name: 'Heavy Destroyer II',
- type: 'ship',
- rarity: 'uncommon',
- price: 55000,
- currency: 'credits',
- description: 'Upgraded heavy destroyer with enhanced firepower',
- texture: 'assets/textures/ships/heavy_destroyer.png',
- stats: { attack: 30, speed: 14, defense: 22, hull: 145 }
- },
- {
- id: 'heavy_destroyer_rare',
- name: 'Heavy Destroyer III',
- type: 'ship',
- rarity: 'rare',
- price: 110000,
- currency: 'credits',
- description: 'Elite heavy destroyer with devastating weapons',
- texture: 'assets/textures/ships/heavy_destroyer.png',
- stats: { attack: 35, speed: 16, defense: 26, hull: 175 }
- },
- {
- id: 'heavy_destroyer_epic',
- name: 'Heavy Destroyer IV',
- type: 'ship',
- rarity: 'epic',
- price: 190000,
- currency: 'credits',
- description: 'Master heavy destroyer with overwhelming power',
- texture: 'assets/textures/ships/heavy_destroyer.png',
- stats: { attack: 40, speed: 18, defense: 30, hull: 210 }
- },
- {
- id: 'heavy_destroyer_legendary',
- name: 'Heavy Destroyer V',
- type: 'ship',
- rarity: 'legendary',
- price: 300000,
- currency: 'credits',
- description: 'Legendary heavy destroyer with ultimate destruction',
- texture: 'assets/textures/ships/heavy_destroyer.png',
- stats: { attack: 48, speed: 20, defense: 36, hull: 255 }
- },
-
- // Heavy Cruiser Variants
- {
- id: 'heavy_cruiser_common',
- name: 'Heavy Cruiser',
- type: 'ship',
- rarity: 'common',
- price: 45000,
- currency: 'credits',
- description: 'Massive heavy cruiser with excellent defense',
- texture: 'assets/textures/ships/heavy_cruiser.png',
- stats: { attack: 22, speed: 8, defense: 25, hull: 150 }
- },
- {
- id: 'heavy_cruiser_uncommon',
- name: 'Heavy Cruiser II',
- type: 'ship',
- rarity: 'uncommon',
- price: 95000,
- currency: 'credits',
- description: 'Enhanced heavy cruiser with superior armor',
- texture: 'assets/textures/ships/heavy_cruiser.png',
- stats: { attack: 26, speed: 9, defense: 30, hull: 180 }
- },
- {
- id: 'heavy_cruiser_rare',
- name: 'Heavy Cruiser III',
- type: 'ship',
- rarity: 'rare',
- price: 190000,
- currency: 'credits',
- description: 'Elite heavy cruiser with fortress-like defense',
- texture: 'assets/textures/ships/heavy_cruiser.png',
- stats: { attack: 30, speed: 10, defense: 36, hull: 220 }
- },
- {
- id: 'heavy_cruiser_epic',
- name: 'Heavy Cruiser IV',
- type: 'ship',
- rarity: 'epic',
- price: 320000,
- currency: 'credits',
- description: 'Master heavy cruiser with impenetrable armor',
- texture: 'assets/textures/ships/heavy_cruiser.png',
- stats: { attack: 34, speed: 11, defense: 42, hull: 265 }
- },
- {
- id: 'heavy_cruiser_legendary',
- name: 'Heavy Cruiser V',
- type: 'ship',
- rarity: 'legendary',
- price: 500000,
- currency: 'credits',
- description: 'Legendary heavy cruiser with ultimate defense',
- texture: 'assets/textures/ships/heavy_cruiser.png',
- stats: { attack: 40, speed: 12, defense: 50, hull: 320 }
- }
- ],
- weapons: [
- // Starter Blaster Variants
- {
- id: 'starter_blaster_common',
- name: 'Starter Blaster',
- type: 'weapon',
- rarity: 'common',
- price: 2000,
- currency: 'credits',
- description: 'Basic blaster for new pilots',
- texture: 'assets/textures/weapons/starter_blaster.png',
- stats: { damage: 10, fireRate: 2, range: 5, energy: 5 }
- },
- {
- id: 'starter_blaster_uncommon',
- name: 'Starter Blaster II',
- type: 'weapon',
- rarity: 'uncommon',
- price: 5000,
- currency: 'credits',
- description: 'Enhanced starter blaster with better fire rate',
- texture: 'assets/textures/weapons/starter_blaster.png',
- stats: { damage: 12, fireRate: 2.5, range: 5.5, energy: 6 }
- },
- {
- id: 'starter_blaster_rare',
- name: 'Starter Blaster III',
- type: 'weapon',
- rarity: 'rare',
- price: 12000,
- currency: 'credits',
- description: 'Elite starter blaster with improved damage',
- texture: 'assets/textures/weapons/starter_blaster.png',
- stats: { damage: 15, fireRate: 3, range: 6, energy: 7 }
- },
- {
- id: 'starter_blaster_epic',
- name: 'Starter Blaster IV',
- type: 'weapon',
- rarity: 'epic',
- price: 25000,
- currency: 'credits',
- description: 'Master starter blaster with superior performance',
- texture: 'assets/textures/weapons/starter_blaster.png',
- stats: { damage: 18, fireRate: 3.5, range: 6.5, energy: 8 }
- },
- {
- id: 'starter_blaster_legendary',
- name: 'Starter Blaster V',
- type: 'weapon',
- rarity: 'legendary',
- price: 50000,
- currency: 'credits',
- description: 'Legendary starter blaster with ultimate power',
- texture: 'assets/textures/weapons/starter_blaster.png',
- stats: { damage: 22, fireRate: 4, range: 7, energy: 10 }
- },
-
- // Laser Pistol Variants
- {
- id: 'laser_pistol_common',
- name: 'Laser Pistol',
- type: 'weapon',
- rarity: 'common',
- price: 3000,
- currency: 'credits',
- description: 'Compact laser pistol for close combat',
- texture: 'assets/textures/weapons/laser_pistol.png',
- stats: { damage: 8, fireRate: 3, range: 4, energy: 3 }
- },
- {
- id: 'laser_pistol_uncommon',
- name: 'Laser Pistol II',
- type: 'weapon',
- rarity: 'uncommon',
- price: 7500,
- currency: 'credits',
- description: 'Enhanced laser pistol with better accuracy',
- texture: 'assets/textures/weapons/laser_pistol.png',
- stats: { damage: 10, fireRate: 3.5, range: 4.5, energy: 4 }
- },
- {
- id: 'laser_pistol_rare',
- name: 'Laser Pistol III',
- type: 'weapon',
- rarity: 'rare',
- price: 18000,
- currency: 'credits',
- description: 'Elite laser pistol with piercing shots',
- texture: 'assets/textures/weapons/laser_pistol.png',
- stats: { damage: 13, fireRate: 4, range: 5, energy: 5 }
- },
- {
- id: 'laser_pistol_epic',
- name: 'Laser Pistol IV',
- type: 'weapon',
- rarity: 'epic',
- price: 35000,
- currency: 'credits',
- description: 'Master laser pistol with rapid fire capability',
- texture: 'assets/textures/weapons/laser_pistol.png',
- stats: { damage: 16, fireRate: 4.5, range: 5.5, energy: 6 }
- },
- {
- id: 'laser_pistol_legendary',
- name: 'Laser Pistol V',
- type: 'weapon',
- rarity: 'legendary',
- price: 70000,
- currency: 'credits',
- description: 'Legendary laser pistol with devastating power',
- texture: 'assets/textures/weapons/laser_pistol.png',
- stats: { damage: 20, fireRate: 5, range: 6, energy: 8 }
- },
-
- // Laser Sniper Rifle Variants
- {
- id: 'laser_sniper_rifle_common',
- name: 'Laser Sniper Rifle',
- type: 'weapon',
- rarity: 'common',
- price: 8000,
- currency: 'credits',
- description: 'Long-range laser sniper rifle for precision attacks',
- texture: 'assets/textures/weapons/laser_sniper_rifle.png',
- stats: { damage: 25, fireRate: 1, range: 10, energy: 8 }
- },
- {
- id: 'laser_sniper_rifle_uncommon',
- name: 'Laser Sniper Rifle II',
- type: 'weapon',
- rarity: 'uncommon',
- price: 20000,
- currency: 'credits',
- description: 'Enhanced laser sniper rifle with better penetration',
- texture: 'assets/textures/weapons/laser_sniper_rifle.png',
- stats: { damage: 30, fireRate: 1.2, range: 11, energy: 9 }
- },
- {
- id: 'laser_sniper_rifle_rare',
- name: 'Laser Sniper Rifle III',
- type: 'weapon',
- rarity: 'rare',
- price: 50000,
- currency: 'credits',
- description: 'Elite laser sniper rifle with deadly accuracy',
- texture: 'assets/textures/weapons/laser_sniper_rifle.png',
- stats: { damage: 36, fireRate: 1.4, range: 12, energy: 10 }
- },
- {
- id: 'laser_sniper_rifle_epic',
- name: 'Laser Sniper Rifle IV',
- type: 'weapon',
- rarity: 'epic',
- price: 100000,
- currency: 'credits',
- description: 'Master laser sniper rifle with extreme range',
- texture: 'assets/textures/weapons/laser_sniper_rifle.png',
- stats: { damage: 42, fireRate: 1.6, range: 13, energy: 12 }
- },
- {
- id: 'laser_sniper_rifle_legendary',
- name: 'Laser Sniper Rifle V',
- type: 'weapon',
- rarity: 'legendary',
- price: 200000,
- currency: 'credits',
- description: 'Legendary laser sniper rifle with unparalleled precision',
- texture: 'assets/textures/weapons/laser_sniper_rifle.png',
- stats: { damage: 50, fireRate: 2, range: 15, energy: 15 }
- }
- ],
- armors: [
- // Basic Armor Variants
- {
- id: 'basic_armor_common',
- name: 'Basic Armor',
- type: 'armor',
- rarity: 'common',
- price: 1500,
- currency: 'credits',
- description: 'Light protection for beginners',
- texture: 'assets/textures/armors/basic_armor.png',
- stats: { defense: 5, durability: 20, weight: 2, energyShield: 0 }
- },
- {
- id: 'basic_armor_uncommon',
- name: 'Basic Armor II',
- type: 'armor',
- rarity: 'uncommon',
- price: 4000,
- currency: 'credits',
- description: 'Improved basic armor with better durability',
- texture: 'assets/textures/armors/basic_armor.png',
- stats: { defense: 7, durability: 25, weight: 2.2, energyShield: 2 }
- },
- {
- id: 'basic_armor_rare',
- name: 'Basic Armor III',
- type: 'armor',
- rarity: 'rare',
- price: 10000,
- currency: 'credits',
- description: 'Elite basic armor with energy shielding',
- texture: 'assets/textures/armors/basic_armor.png',
- stats: { defense: 10, durability: 30, weight: 2.5, energyShield: 5 }
- },
- {
- id: 'basic_armor_epic',
- name: 'Basic Armor IV',
- type: 'armor',
- rarity: 'epic',
- price: 20000,
- currency: 'credits',
- description: 'Master basic armor with advanced protection',
- texture: 'assets/textures/armors/basic_armor.png',
- stats: { defense: 13, durability: 35, weight: 2.8, energyShield: 8 }
- },
- {
- id: 'basic_armor_legendary',
- name: 'Basic Armor V',
- type: 'armor',
- rarity: 'legendary',
- price: 40000,
- currency: 'credits',
- description: 'Legendary basic armor with ultimate defense',
- texture: 'assets/textures/armors/basic_armor.png',
- stats: { defense: 17, durability: 40, weight: 3, energyShield: 12 }
- },
-
- // Medium Armor Variants
- {
- id: 'medium_armor_common',
- name: 'Medium Armor',
- type: 'armor',
- rarity: 'common',
- price: 5000,
- currency: 'credits',
- description: 'Balanced armor for general combat',
- texture: 'assets/textures/armors/medium_armor.png',
- stats: { defense: 12, durability: 40, weight: 5, energyShield: 3 }
- },
- {
- id: 'medium_armor_uncommon',
- name: 'Medium Armor II',
- type: 'armor',
- rarity: 'uncommon',
- price: 12000,
- currency: 'credits',
- description: 'Enhanced medium armor with better shielding',
- texture: 'assets/textures/armors/medium_armor.png',
- stats: { defense: 15, durability: 50, weight: 5.5, energyShield: 6 }
- },
- {
- id: 'medium_armor_rare',
- name: 'Medium Armor III',
- type: 'armor',
- rarity: 'rare',
- price: 30000,
- currency: 'credits',
- description: 'Elite medium armor with advanced systems',
- texture: 'assets/textures/armors/medium_armor.png',
- stats: { defense: 19, durability: 60, weight: 6, energyShield: 10 }
- },
- {
- id: 'medium_armor_epic',
- name: 'Medium Armor IV',
- type: 'armor',
- rarity: 'epic',
- price: 60000,
- currency: 'credits',
- description: 'Master medium armor with superior protection',
- texture: 'assets/textures/armors/medium_armor.png',
- stats: { defense: 23, durability: 70, weight: 6.5, energyShield: 15 }
- },
- {
- id: 'medium_armor_legendary',
- name: 'Medium Armor V',
- type: 'armor',
- rarity: 'legendary',
- price: 100000,
- currency: 'credits',
- description: 'Legendary medium armor with ultimate defense',
- texture: 'assets/textures/armors/medium_armor.png',
- stats: { defense: 28, durability: 80, weight: 7, energyShield: 20 }
- },
-
- // Heavy Armor Variants
- {
- id: 'heavy_armor_common',
- name: 'Heavy Armor',
- type: 'armor',
- rarity: 'common',
- price: 10000,
- currency: 'credits',
- description: 'Maximum protection with increased weight',
- texture: 'assets/textures/armors/heavy_armor.png',
- stats: { defense: 20, durability: 60, weight: 8, energyShield: 5 }
- },
- {
- id: 'heavy_armor_uncommon',
- name: 'Heavy Armor II',
- type: 'armor',
- rarity: 'uncommon',
- price: 25000,
- currency: 'credits',
- description: 'Upgraded heavy armor with better energy shielding',
- texture: 'assets/textures/armors/heavy_armor.png',
- stats: { defense: 25, durability: 75, weight: 9, energyShield: 10 }
- },
- {
- id: 'heavy_armor_rare',
- name: 'Heavy Armor III',
- type: 'armor',
- rarity: 'rare',
- price: 60000,
- currency: 'credits',
- description: 'Elite heavy armor with advanced protection systems',
- texture: 'assets/textures/armors/heavy_armor.png',
- stats: { defense: 31, durability: 90, weight: 10, energyShield: 16 }
- },
- {
- id: 'heavy_armor_epic',
- name: 'Heavy Armor IV',
- type: 'armor',
- rarity: 'epic',
- price: 120000,
- currency: 'credits',
- description: 'Master heavy armor with superior defense capabilities',
- texture: 'assets/textures/armors/heavy_armor.png',
- stats: { defense: 37, durability: 105, weight: 11, energyShield: 23 }
- },
- {
- id: 'heavy_armor_legendary',
- name: 'Heavy Armor V',
- type: 'armor',
- rarity: 'legendary',
- price: 200000,
- currency: 'credits',
- description: 'Legendary heavy armor with ultimate protection',
- texture: 'assets/textures/armors/heavy_armor.png',
- stats: { defense: 45, durability: 120, weight: 12, energyShield: 30 }
- }
- ],
- cosmetics: [
- {
- id: 'blue_paint',
- name: 'Blue Paint Job',
- type: 'cosmetic',
- rarity: 'common',
- price: 100,
- currency: 'gems',
- description: 'Custom blue paint for your ship'
- },
- {
- id: 'golden_trim',
- name: 'Golden Trim',
- type: 'cosmetic',
- rarity: 'rare',
- price: 500,
- currency: 'gems',
- description: 'Luxurious golden trim for your ship'
- }
- ],
- consumables: [
- {
- id: 'health_kit',
- name: 'Health Kit',
- type: 'consumable',
- rarity: 'common',
- price: 15,
- currency: 'credits',
- description: 'Restores 50 health points',
- texture: 'assets/textures/items/health_pack.png',
- effect: { heal: 50 }
- },
- {
- id: 'mega_health_kit',
- name: 'Mega Health Kit',
- type: 'consumable',
- rarity: 'uncommon',
- price: 50,
- currency: 'credits',
- description: 'Restores full health',
- texture: 'assets/textures/items/mega_health_pack.png',
- effect: { heal: 999 }
- }
- ],
- buildings: [
- {
- id: 'command_center',
- name: 'Command Center',
- type: 'building',
- rarity: 'uncommon',
- price: 50000,
- currency: 'credits',
- description: 'Central command facility for base operations and coordination',
- texture: 'assets/textures/base/command_center.png',
- stats: { command: 10, efficiency: 15, capacity: 20 }
- },
- {
- id: 'mining_facility',
- name: 'Mining Facility',
- type: 'building',
- rarity: 'common',
- price: 25000,
- currency: 'credits',
- description: 'Automated mining facility for resource extraction and processing',
- texture: 'assets/textures/base/mining_facility.png',
- stats: { production: 12, efficiency: 8, storage: 15 }
- }
- ],
- materials: [
- {
- id: 'iron_ore',
- name: 'Iron Ore',
- type: 'material',
- rarity: 'common',
- price: 10,
- currency: 'credits',
- description: 'Basic metal ore used for crafting weapons and armor',
- texture: 'assets/textures/items/iron_ore.png',
- stackable: true
- },
- {
- id: 'copper_ore',
- name: 'Copper Ore',
- type: 'material',
- rarity: 'common',
- price: 8,
- currency: 'credits',
- description: 'Conductive metal ore used for wiring and electronics',
- texture: 'assets/textures/items/copper_ore.png',
- stackable: true
- },
- {
- id: 'tin_bar',
- name: 'Tin Bar',
- type: 'material',
- rarity: 'common',
- price: 12,
- currency: 'credits',
- description: 'Refined tin bar used for alloys and soldering',
- texture: 'assets/textures/items/tin_bar.png',
- stackable: true
- },
- {
- id: 'copper_wire',
- name: 'Copper Wire',
- type: 'material',
- rarity: 'common',
- price: 8,
- currency: 'credits',
- description: 'Conductive wiring for electronic components',
- texture: 'assets/textures/items/copper_wire.png',
- stackable: true
- },
- {
- id: 'energy_crystal',
- name: 'Energy Crystal',
- type: 'material',
- rarity: 'uncommon',
- price: 25,
- currency: 'credits',
- description: 'Crystallized energy source for power systems',
- texture: 'assets/textures/items/energy_crystal.png',
- stackable: true
- },
- {
- id: 'leather',
- name: 'Leather',
- type: 'material',
- rarity: 'common',
- price: 12,
- currency: 'credits',
- description: 'Durable material for crafting armor and accessories',
- texture: 'assets/textures/items/leather.png',
- stackable: true
- },
- {
- id: 'herbs',
- name: 'Herbs',
- type: 'material',
- rarity: 'common',
- price: 5,
- currency: 'credits',
- description: 'Medicinal herbs used for healing items',
- texture: 'assets/textures/items/herbs.png',
- stackable: true
- },
- {
- id: 'bandages',
- name: 'Bandages',
- type: 'material',
- rarity: 'common',
- price: 3,
- currency: 'credits',
- description: 'Medical bandages used for crafting healing items',
- texture: 'assets/textures/items/bandages.png',
- stackable: true
- },
- {
- id: 'steel_plate',
- name: 'Steel Plate',
- type: 'material',
- rarity: 'uncommon',
- price: 30,
- currency: 'credits',
- description: 'Reinforced steel plates used for advanced armor and ship components',
- texture: 'assets/textures/items/stell_plate.png',
- stackable: true
- },
- {
- id: 'advanced_component',
- name: 'Advanced Component',
- type: 'material',
- rarity: 'rare',
- price: 75,
- currency: 'credits',
- description: 'High-tech component used for advanced equipment and upgrades',
- texture: 'assets/textures/items/advanced_component.png',
- stackable: true
- },
- {
- id: 'advanced_components',
- name: 'Advanced Components Set',
- type: 'material',
- rarity: 'epic',
- price: 150,
- currency: 'credits',
- description: 'Complete set of advanced components for high-end manufacturing',
- texture: 'assets/textures/items/advanced_components.png',
- stackable: true
- },
- {
- id: 'basic_circuitboard',
- name: 'Basic Circuit Board',
- type: 'material',
- rarity: 'common',
- price: 20,
- currency: 'credits',
- description: 'Basic electronic circuit board for simple devices',
- texture: 'assets/textures/items/basic_circuitboard.png',
- stackable: true
- },
- {
- id: 'common_circuitboard',
- name: 'Common Circuit Board',
- type: 'material',
- rarity: 'common',
- price: 35,
- currency: 'credits',
- description: 'Standard circuit board for most electronic equipment',
- texture: 'assets/textures/items/common_circuitboard.png',
- stackable: true
- },
- {
- id: 'advanced_circuitboard',
- name: 'Advanced Circuit Board',
- type: 'material',
- rarity: 'rare',
- price: 100,
- currency: 'credits',
- description: 'High-performance circuit board for advanced systems',
- texture: 'assets/textures/items/advanced_circuitboard.png',
- stackable: true
- },
- {
- id: 'battery',
- name: 'Battery',
- type: 'material',
- rarity: 'uncommon',
- price: 35,
- currency: 'credits',
- description: 'Power batteries used for energy-based equipment',
- texture: 'assets/textures/items/battery.png',
- stackable: true
- },
- {
- id: 'advanced_components',
- name: 'Advanced Components',
- type: 'material',
- rarity: 'rare',
- price: 150,
- currency: 'credits',
- description: 'Sophisticated electronic components for advanced ship systems',
- stackable: true
- }
- ]
- };
+ // Shop items - now loaded from server ItemSystem
+ this.shopItems = null; // Will be populated by ItemSystem in multiplayer
// Owned cosmetics
this.ownedCosmetics = [];
- if (debugLogger) debugLogger.log('Economy constructor completed', {
- initialCredits: this.credits,
- initialGems: this.gems,
- shopCategoriesCount: Object.keys(this.shopCategories).length,
- totalShopItems: Object.values(this.shopItems).reduce((total, category) => total + category.length, 0)
+ console.log('[ECONOMY] Economy system initialized with server-side ItemSystem');
+ console.log('[ECONOMY] Preserved values - Credits:', this.credits, 'Gems:', this.gems);
+
+ // Set up socket listeners for economy sync
+ this.setupSocketListeners();
+
+ // Request fresh economy data after a short delay to ensure sync
+ if (window.smartSaveManager?.isMultiplayer) {
+ setTimeout(() => {
+ this.requestEconomyData();
+ }, 1000);
+ }
+ }
+
+ /**
+ * Set up socket listeners for economy data synchronization
+ */
+ setupSocketListeners() {
+ if (!this.game.socket) {
+ console.warn('[ECONOMY] No socket available for economy sync');
+ return;
+ }
+
+ // Listen for economy data updates from server
+ this.game.socket.on('economy_data', (data) => {
+ console.log('[ECONOMY] Received economy data from server:', data);
+ this.credits = data.credits || 0;
+ this.gems = data.gems || 0;
+
+ // Update UI immediately
+ if (this.game.ui) {
+ this.game.ui.updatePlayerStats();
+ }
+
+ console.log('[ECONOMY] Economy synced - Credits:', this.credits, 'Gems:', this.gems);
});
}
+ /**
+ * Request economy data from server
+ */
+ requestEconomyData() {
+ if (this.game.socket) {
+ console.log('[ECONOMY] Requesting economy data from server');
+ this.game.socket.emit('get_economy_data');
+ } else {
+ console.warn('[ECONOMY] Cannot request economy data - no socket available');
+ }
+ }
+
async initialize() {
- const debugLogger = window.debugLogger;
- console.log('[ECONOMY] Economy system initializing');
- if (debugLogger) await debugLogger.startStep('economyInitialize');
+ console.log('[ECONOMY] Initializing economy system');
- try {
- if (debugLogger) await debugLogger.logStep('Economy initialization started', {
- currentCredits: this.credits,
- currentGems: this.gems,
- transactionHistoryLength: this.transactionHistory.length
- });
-
- // Setup shop purchase event listeners
- this.setupShopEventListeners();
-
- // Initialize random shop system
+ // In multiplayer mode, wait for ItemSystem to be ready (handled by event listener)
+ this.game.on('itemSystemReady', () => {
+ console.log('[ECONOMY] ItemSystem is ready, updating shop UI');
+ this.updateShopUI();
+ });
+ if (window.smartSaveManager?.isMultiplayer) {
+ console.log('[ECONOMY] Multiplayer mode - waiting for ItemSystem to be ready');
+ // ItemSystem initialization removed - wait for event instead
+ } else {
+ console.log('[ECONOMY] Singleplayer mode - using local shop data');
+ // Initialize random shop for singleplayer
this.initializeRandomShop();
-
- console.log('[ECONOMY] Economy system initialization completed');
- if (debugLogger) await debugLogger.endStep('economyInitialize');
- } catch (error) {
- console.error('[ECONOMY] Error during initialization:', error);
- if (debugLogger) await debugLogger.errorEvent(error, 'Economy Initialize');
}
+
+ console.log('[ECONOMY] Economy system initialized');
}
- initializeRandomShop() {
- const debugLogger = window.debugLogger;
- console.log('[ECONOMY] Initializing random shop system');
-
- // Shop data is now loaded through the main save/load system in load()
- // Only initialize if no shop data exists (new game)
- if (!this.lastShopRefresh || Object.keys(this.randomShopItems).length === 0) {
- console.log('[ECONOMY] No shop data found, generating new shop');
- this.refreshRandomShop();
- } else {
- console.log('[ECONOMY] Shop data already loaded from save');
- // Start the refresh timer for ongoing updates
- this.startShopRefreshTimer();
- }
-
- if (debugLogger) debugLogger.logStep('Random shop system initialized', {
- hasShopData: Object.keys(this.randomShopItems).length > 0,
- lastRefresh: this.lastShopRefresh
- });
- }
-
- refreshRandomShop() {
- const debugLogger = window.debugLogger;
- console.log('[ECONOMY] Refreshing random shop items');
-
- const categories = Object.keys(this.shopCategories);
- this.randomShopItems = {};
-
- categories.forEach(category => {
- const categoryItems = this.shopItems[category] || [];
- if (categoryItems.length === 0) return;
-
- // Group items by their base ID (remove tier suffixes)
- const baseItemGroups = {};
- categoryItems.forEach(item => {
- // Extract base ID by removing tier suffixes (common, uncommon, rare, epic, legendary)
- const baseId = item.id.replace(/_(common|uncommon|rare|epic|legendary)$/, '');
- if (!baseItemGroups[baseId]) {
- baseItemGroups[baseId] = [];
- }
- baseItemGroups[baseId].push(item);
- });
-
- // Get all unique base items
- const uniqueBaseItems = Object.keys(baseItemGroups);
-
- // Randomly select up to MAX_ITEMS_PER_CATEGORY base items
- const shuffledBaseItems = [...uniqueBaseItems].sort(() => Math.random() - 0.5);
- const selectedBaseItems = shuffledBaseItems.slice(0, Math.min(this.MAX_ITEMS_PER_CATEGORY, shuffledBaseItems.length));
-
- // For each selected base item, randomly pick one tier variant
- this.randomShopItems[category] = selectedBaseItems.map(baseId => {
- const variants = baseItemGroups[baseId];
- const selectedVariant = variants[Math.floor(Math.random() * variants.length)];
-
- return {
- ...selectedVariant,
- id: `${selectedVariant.id}_random_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
- originalId: selectedVariant.id,
- baseId: baseId,
- price: Math.round(selectedVariant.price * (0.8 + Math.random() * 0.4)), // ±20% price variation
- isRandomItem: true,
- refreshTimestamp: Date.now()
- };
- });
-
- // Reset purchase limits for this category
- this.categoryPurchaseLimits[category] = 0;
-
- if (debugLogger) debugLogger.logStep('Random shop category refreshed', {
- category: category,
- totalBaseItems: uniqueBaseItems.length,
- baseItemsSelected: selectedBaseItems.length,
- itemsGenerated: this.randomShopItems[category].length,
- selectedVariants: this.randomShopItems[category].map(item => ({
- baseId: item.baseId,
- variantId: item.originalId,
- rarity: item.rarity,
- name: item.name
- })),
- purchaseLimitReset: true
- });
- });
-
- this.lastShopRefresh = Date.now();
- this.saveRandomShopData();
- this.startShopRefreshTimer();
-
- // Update UI if shop tab is active
- this.updateShopUI();
-
- this.game.showNotification('Shop inventory refreshed!', 'info', 3000);
-
- if (debugLogger) debugLogger.logStep('Random shop refresh completed', {
- categoriesRefreshed: categories.length,
- totalItemsGenerated: Object.values(this.randomShopItems).flat().length,
- nextRefresh: new Date(this.lastShopRefresh + this.SHOP_REFRESH_INTERVAL)
- });
- }
-
- startShopRefreshTimer() {
- // Clear existing timers
- if (this.shopRefreshInterval) {
- clearInterval(this.shopRefreshInterval);
- }
- if (this.shopHeartbeatInterval) {
- clearInterval(this.shopHeartbeatInterval);
- }
-
- // Set up heartbeat timer for live countdown updates (every second)
- this.shopHeartbeatInterval = setInterval(() => {
- this.updateShopCountdown();
- }, 1000);
-
- // Set up refresh timer (every 2 hours)
- this.shopRefreshInterval = setInterval(() => {
- this.refreshRandomShop();
- }, this.SHOP_REFRESH_INTERVAL);
-
- console.log('[ECONOMY] Shop refresh timers started');
- }
-
- updateShopCountdown() {
- // Only update if shop tab is active and using random shop
- const shopTab = document.getElementById('shop-tab');
- if (!shopTab || shopTab.style.display === 'none') {
- return;
- }
-
- const activeCategory = document.querySelector('.shop-cat-btn.active')?.dataset.category || 'ships';
- if (!this.randomShopItems[activeCategory]) {
- return;
- }
-
- // Find and update the countdown element
- const countdownElement = document.querySelector('.refresh-countdown');
- if (countdownElement) {
- countdownElement.innerHTML = `
-
- Next refresh in: ${this.getShopRefreshCountdown()}
- `;
- }
- }
-
- stopShopRefreshTimer() {
- if (this.shopRefreshInterval) {
- clearInterval(this.shopRefreshInterval);
- this.shopRefreshInterval = null;
- }
- if (this.shopHeartbeatInterval) {
- clearInterval(this.shopHeartbeatInterval);
- this.shopHeartbeatInterval = null;
- }
- console.log('[ECONOMY] Shop refresh timers stopped');
- }
-
- loadRandomShopData() {
- try {
- const savedData = localStorage.getItem('gso_random_shop');
- if (savedData) {
- const data = JSON.parse(savedData);
- this.randomShopItems = data.randomShopItems || {};
- this.categoryPurchaseLimits = data.categoryPurchaseLimits || {};
- this.lastShopRefresh = data.lastShopRefresh || null;
- console.log('[ECONOMY] Random shop data loaded from localStorage');
- }
- } catch (error) {
- console.error('[ECONOMY] Error loading random shop data:', error);
- this.randomShopItems = {};
- this.categoryPurchaseLimits = {};
- this.lastShopRefresh = null;
- }
- }
- saveRandomShopData() {
- try {
- const data = {
- randomShopItems: this.randomShopItems,
- categoryPurchaseLimits: this.categoryPurchaseLimits,
- lastShopRefresh: this.lastShopRefresh
- };
- localStorage.setItem('gso_random_shop', JSON.stringify(data));
- console.log('[ECONOMY] Random shop data saved to localStorage');
- } catch (error) {
- console.error('[ECONOMY] Error saving random shop data:', error);
- }
- }
-
- shouldRefreshShop() {
- // Check if enough time has passed since the last actual refresh
- if (!this.lastShopRefresh) {
- return true; // No previous refresh, need to refresh
- }
-
- const now = Date.now();
- const timeSinceRefresh = now - this.lastShopRefresh;
- return timeSinceRefresh >= this.SHOP_REFRESH_INTERVAL;
- }
-
- getShopRefreshCountdown() {
- const schedule = this.getRefreshSchedule();
- const now = Date.now();
-
- // Calculate time until next server refresh
- const timeUntilRefresh = schedule.nextRefresh.getTime() - now;
-
- if (timeUntilRefresh <= 0) {
- return 'Refreshing...';
- }
-
- return this.formatTimeRemaining(timeUntilRefresh);
- }
-
- getNextRefreshTime() {
- // Calculate the next refresh time based on server time
- const now = Date.now();
-
- if (!this.lastShopRefresh) {
- // If no last refresh, next refresh is now + interval
- return new Date(now + this.SHOP_REFRESH_INTERVAL);
- }
-
- // Calculate next refresh time
- const timeSinceRefresh = now - this.lastShopRefresh;
- const intervalsPassed = Math.floor(timeSinceRefresh / this.SHOP_REFRESH_INTERVAL);
- const nextRefreshTime = this.lastShopRefresh + ((intervalsPassed + 1) * this.SHOP_REFRESH_INTERVAL);
-
- return new Date(nextRefreshTime);
- }
-
- getRefreshSchedule() {
- // Generate a consistent refresh schedule based on server time
- const now = new Date();
- const currentHour = now.getHours();
-
- // Refresh at even hours: 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22
- let nextRefreshHour;
-
- if (currentHour % 2 === 0 && now.getMinutes() === 0 && now.getSeconds() === 0) {
- // Exactly on an even hour at :00:00, next refresh is in 2 hours
- nextRefreshHour = currentHour + 2;
- } else {
- // Find the next even hour
- nextRefreshHour = currentHour + (2 - (currentHour % 2));
- }
-
- // Handle midnight wrap-around
- if (nextRefreshHour >= 24) {
- nextRefreshHour = nextRefreshHour % 24;
- }
-
- const nextRefresh = new Date(now);
- nextRefresh.setHours(nextRefreshHour, 0, 0, 0);
-
- // If the calculated time is in the past (shouldn't happen but just in case), add 2 hours
- if (nextRefresh <= now) {
- nextRefresh.setHours(nextRefresh.getHours() + 2);
- }
-
- console.log('[ECONOMY] Current time:', now.toLocaleTimeString());
- console.log('[ECONOMY] Next refresh at:', nextRefresh.toLocaleTimeString());
-
- return {
- nextRefresh: nextRefresh,
- interval: 2 * 60 * 60 * 1000, // 2 hours
- schedule: 'Every 2 hours at even hours (2,4,6,8,10,12,14,16,18,20,22,00)'
- };
- }
-
- formatTimeRemaining(timeUntilRefresh) {
- const hours = Math.floor(timeUntilRefresh / (1000 * 60 * 60));
- const minutes = Math.floor((timeUntilRefresh % (1000 * 60 * 60)) / (1000 * 60));
- const seconds = Math.floor((timeUntilRefresh % (1000 * 60)) / 1000);
-
- if (hours > 0) {
- return `${hours}h ${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
- } else {
- return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
- }
- }
-
- getItemCategory(item) {
- // Determine category based on item type
- switch (item.type) {
- case 'ship': return 'ships';
- case 'weapon': return 'weapons';
- case 'armor': return 'armors';
- case 'material': return 'materials';
- case 'cosmetic': return 'cosmetics';
- case 'upgrade': return 'upgrades';
- case 'consumable': return 'consumables';
- case 'building': return 'buildings';
- default:
- // For random items, check if we can find them in shop categories
- for (const [category, items] of Object.entries(this.shopItems)) {
- if (items.some(shopItem => shopItem.id === item.originalId || shopItem.id === item.id)) {
- return category;
- }
- }
- return 'unknown';
- }
- }
-
- setupShopEventListeners() {
- const debugLogger = window.debugLogger;
- console.log('[ECONOMY] Setting up shop event listeners');
-
- // Remove existing listeners to prevent duplicates
- const shopItemsElement = document.getElementById('shopItems');
- if (shopItemsElement) {
- // Clone the element to remove all event listeners
- const newElement = shopItemsElement.cloneNode(true);
- shopItemsElement.parentNode.replaceChild(newElement, shopItemsElement);
-
- // Setup shop purchase button event delegation on the new element
- newElement.addEventListener('click', (event) => {
- const purchaseBtn = event.target.closest('.shop-item-purchase-btn');
- if (purchaseBtn && !purchaseBtn.disabled) {
- const itemId = purchaseBtn.getAttribute('data-item-id');
- console.log(`[ECONOMY] Shop purchase button clicked for item: ${itemId}`);
-
- if (itemId) {
- this.purchaseItem(itemId, 1);
- } else {
- console.error('[ECONOMY] No item ID found on purchase button');
- }
- }
- });
-
- console.log('[ECONOMY] Shop event listeners setup completed');
- } else {
- console.error('[ECONOMY] Shop items element not found');
- }
- }
-
- // Currency management
- addCredits(amount, source = 'unknown') {
- const oldCredits = this.credits;
- this.credits += amount;
- this.addTransaction('credits', amount, source);
-
- console.log(`[ECONOMY] Added ${amount} credits from ${source}. New total: ${this.credits}`);
-
- // Update UI to show new credit amount
- if (this.game.systems.ui && this.game.shouldUpdateGUI()) {
- this.game.systems.ui.updateUI();
- }
-
- this.game.showNotification(`+${this.game.formatNumber(amount)} credits`, 'success', 2000);
- }
-
- removeCredits(amount) {
- const oldCredits = this.credits;
-
- if (this.credits < amount) {
- this.game.showNotification('Not enough credits!', 'error', 3000);
- return false;
- }
-
- this.credits -= amount;
- this.addTransaction('credits', -amount, 'purchase');
-
- console.log(`[ECONOMY] Removed ${amount} credits. New total: ${this.credits}`);
-
- // Update UI to show new credit amount
- if (this.game.systems.ui && this.game.shouldUpdateGUI()) {
- this.game.systems.ui.updateUI();
- }
-
- return true;
- }
-
- addGems(amount, source = 'unknown') {
- const oldGems = this.gems;
- this.gems += amount;
- this.addTransaction('gems', amount, source);
-
- console.log(`[ECONOMY] Added ${amount} gems from ${source}. New total: ${this.gems}`);
-
- // Update UI to show new gem amount
- if (this.game.systems.ui && this.game.shouldUpdateGUI()) {
- this.game.systems.ui.updateUI();
- }
-
- this.game.showNotification(`+${this.game.formatNumber(amount)} gems`, 'success', 2000);
- }
-
- removeGems(amount) {
- const oldGems = this.gems;
-
- if (this.gems < amount) {
- this.game.showNotification('Not enough gems!', 'error', 3000);
- return false;
- }
-
- this.gems -= amount;
- this.addTransaction('gems', -amount, 'purchase');
-
- console.log(`[ECONOMY] Removed ${amount} gems. New total: ${this.gems}`);
-
- // Update UI to show new gem amount
- if (this.game.systems.ui && this.game.shouldUpdateGUI()) {
- this.game.systems.ui.updateUI();
- }
-
- return true;
- }
-
- // Transaction tracking
- addTransaction(currency, amount, source) {
- const debugLogger = window.debugLogger;
- const transaction = {
- id: Date.now() + Math.random().toString(36).substr(2, 9),
- currency: currency,
- amount: amount,
- source: source,
- timestamp: Date.now()
- };
-
- this.transactionHistory.unshift(transaction);
-
- // Keep only last 100 transactions
- const oldLength = this.transactionHistory.length;
- if (this.transactionHistory.length > 100) {
- this.transactionHistory = this.transactionHistory.slice(0, 100);
- }
-
- if (debugLogger) debugLogger.logStep('Transaction recorded', {
- transactionId: transaction.id,
- currency: currency,
- amount: amount,
- source: source,
- timestamp: transaction.timestamp,
- historyLength: this.transactionHistory.length,
- wasTrimmed: oldLength > 100,
- removedCount: oldLength > 100 ? oldLength - 100 : 0
- });
- }
-
- // Shop functionality
+ // Shop functionality - now uses ItemSystem in multiplayer
purchaseItem(itemId, quantity = 1) {
const debugLogger = window.debugLogger;
+
+ // In multiplayer mode, send request to server
+ if (window.smartSaveManager?.isMultiplayer) {
+ if (debugLogger) debugLogger.logStep('Sending purchase request to server', {
+ itemId: itemId,
+ quantity: quantity
+ });
+
+ // Send purchase request to server
+ if (window.game && window.game.socket) {
+ window.game.socket.emit('purchaseItem', {
+ itemId: itemId,
+ quantity: quantity
+ });
+
+ // Show loading message
+ this.game.showNotification('Processing purchase...', 'info', 2000);
+ } else {
+ this.game.showNotification('Not connected to server', 'error', 3000);
+ }
+ return;
+ }
+
+ // Singleplayer mode - use local logic
const item = this.findShopItem(itemId);
if (!item) {
@@ -1423,25 +227,8 @@ class Economy {
return false;
}
- // Random shop items don't have purchase limits - unlimited purchases allowed
- if (item.isRandomItem) {
- const category = this.getItemCategory(item);
-
- if (debugLogger) debugLogger.logStep('Random shop purchase completed', {
- itemId: itemId,
- itemName: item.name,
- category: category
- });
- }
-
- // Update inventory UI to show the new item
- if (this.game.systems.ui && this.game.systems.ui.updateInventory) {
- this.game.systems.ui.updateInventory();
- } else if (this.game.systems.ui && this.game.systems.ui.updateUI) {
- this.game.systems.ui.updateUI();
- } else {
- console.warn('No UI update method available after purchase');
- }
+ // Show success message
+ this.game.showNotification(`Purchased ${item.name}!`, 'success', 3000);
if (debugLogger) debugLogger.logStep('Item purchase completed successfully', {
itemId: itemId,
@@ -1463,27 +250,42 @@ class Economy {
findShopItem(itemId) {
const debugLogger = window.debugLogger;
- for (const category of Object.values(this.shopItems)) {
- const item = category.find(i => i.id === itemId);
- if (item) {
- if (debugLogger) debugLogger.logStep('Shop item found', {
- itemId: itemId,
- itemName: item.name,
- itemType: item.type,
- itemPrice: item.price,
- itemCurrency: item.currency
- });
- return item;
- }
- }
+ console.log('[ECONOMY] Looking for shop item:', itemId);
+ console.log('[ECONOMY] Multiplayer mode:', window.smartSaveManager?.isMultiplayer);
+ console.log('[ECONOMY] ItemSystem available:', !!(this.game.systems.itemSystem));
- if (debugLogger) debugLogger.logStep('Shop item not found', {
- itemId: itemId,
- availableCategories: Object.keys(this.shopItems)
- });
- return null;
+ // In multiplayer mode, use ItemSystem (required)
+ if (window.smartSaveManager?.isMultiplayer) {
+ // Check if ItemSystem is ready before using it
+ if (!this.game.systems.itemSystem || !this.game.systems.itemSystem.itemCatalog) {
+ console.log('[ECONOMY] ItemSystem not ready yet, cannot find shop item');
+ if (debugLogger) debugLogger.logStep('Shop item lookup failed - ItemSystem not ready', {
+ itemId: itemId,
+ multiplayer: true
+ });
+ return null;
+ }
+
+ // Search in ItemSystem catalog
+ const item = this.game.systems.itemSystem.itemCatalog.get(itemId);
+ if (item) {
+ console.log('[ECONOMY] Found item in ItemSystem:', item.name);
+ return item;
+ } else {
+ console.log('[ECONOMY] Item not found in ItemSystem:', itemId);
+ return null;
+ }
+ } else {
+ // Singleplayer mode - search in local random shop
+ for (const categoryItems of Object.values(this.randomShopItems)) {
+ const item = categoryItems.find(item => item.id === itemId);
+ if (item) return item;
+ }
+ return null;
+ }
}
+ // Purchase methods
purchaseShip(ship) {
const debugLogger = window.debugLogger;
const player = this.game.systems.player;
@@ -1491,75 +293,36 @@ class Economy {
const oldShipClass = player.ship.class;
const oldAttributes = { ...player.attributes };
- if (debugLogger) debugLogger.logStep('Ship purchase processing', {
- shipId: ship.id,
- shipName: ship.name,
- shipType: ship.type,
- shipStats: ship.stats,
- oldShipName: oldShipName,
- oldShipClass: oldShipClass
- });
+ // Update player ship
+ player.ship = {
+ name: ship.name,
+ class: ship.id,
+ texture: ship.texture,
+ stats: ship.stats || {}
+ };
- // Add ship to player's ship collection
- // Add ship to base gallery
- if (this.game.systems.baseSystem) {
- this.game.systems.baseSystem.addShipToGallery(ship);
- if (debugLogger) debugLogger.logStep('Ship added to base gallery');
+ // Update player attributes
+ if (ship.stats) {
+ player.attributes = { ...player.attributes, ...ship.stats };
}
- // Replace current ship (no upgrade functionality)
- player.ship.name = ship.name;
- player.ship.class = ship.type;
- player.ship.level = 1; // Reset level for new ship
- player.ship.texture = ship.texture; // Copy texture for image display
-
- // Set ship-specific stats (not just attributes)
- if (ship.stats.health) {
- player.ship.maxHealth = ship.stats.health;
- player.ship.health = ship.stats.health;
+ // Add to owned ships
+ if (!player.ownedShips) {
+ player.ownedShips = [];
}
- if (ship.stats.attack) {
- player.ship.attack = ship.stats.attack;
- }
- if (ship.stats.defense) {
- player.ship.defense = ship.stats.defense;
- }
- if (ship.stats.speed) {
- player.ship.speed = ship.stats.speed;
- }
- if (ship.stats.criticalChance) {
- player.ship.criticalChance = ship.stats.criticalChance;
- }
- if (ship.stats.criticalDamage) {
- player.ship.criticalDamage = ship.stats.criticalDamage;
- }
-
- // Also update player attributes for compatibility
- for (const [stat, value] of Object.entries(ship.stats)) {
- if (player.attributes[stat] !== undefined) {
- player.attributes[stat] = value; // Replace stats instead of adding
- }
- }
-
- player.updateUI();
-
- // Also update ShipSystem display
- if (this.game.systems.ship && this.game.systems.ship.updateCurrentShipDisplay) {
- this.game.systems.ship.updateCurrentShipDisplay();
+ if (!player.ownedShips.includes(ship.id)) {
+ player.ownedShips.push(ship.id);
}
if (debugLogger) debugLogger.logStep('Ship purchase completed', {
shipId: ship.id,
+ shipName: ship.name,
oldShipName: oldShipName,
- newShipName: player.ship.name,
oldShipClass: oldShipClass,
- newShipClass: player.ship.class,
- attributeChanges: Object.entries(ship.stats).map(([stat, value]) => ({
- stat: stat,
- oldValue: oldAttributes[stat],
- newValue: player.attributes[stat],
- change: value
- }))
+ newShipName: ship.name,
+ newShipClass: ship.id,
+ oldAttributes: oldAttributes,
+ newAttributes: player.attributes
});
}
@@ -1583,380 +346,277 @@ class Economy {
purchaseConsumable(consumable, quantity) {
const debugLogger = window.debugLogger;
const inventory = this.game.systems.inventory;
- const oldInventorySize = inventory ? inventory.items.length : 0;
- if (debugLogger) debugLogger.logStep('Consumable purchase processing', {
- consumableId: consumable.id,
- consumableName: consumable.name,
+ // Create item object for inventory
+ const item = {
+ id: consumable.id,
+ name: consumable.name,
+ type: consumable.type,
+ rarity: consumable.rarity,
quantity: quantity,
- effect: consumable.effect,
- oldInventorySize: oldInventorySize
- });
-
- // Add to inventory
- for (let i = 0; i < quantity; i++) {
- const item = {
- id: `${consumable.id}_${Date.now()}_${i}`,
- name: consumable.name,
- type: 'consumable',
- rarity: consumable.rarity,
- quantity: 1,
- description: consumable.description,
- consumable: true,
- effect: consumable.effect
- };
-
- inventory.addItem(item);
- }
-
- if (debugLogger) debugLogger.logStep('Consumable purchase completed', {
- consumableId: consumable.id,
- quantity: quantity,
- oldInventorySize: oldInventorySize,
- newInventorySize: inventory ? inventory.items.length : 0,
- itemsAdded: quantity
- });
- }
-
- purchaseEquipment(equipment, quantity = 1) {
- const inventory = this.game.systems.inventory;
- const oldInventorySize = inventory ? inventory.items.length : 0;
-
- // Add to inventory
- for (let i = 0; i < quantity; i++) {
- const item = {
- id: `${equipment.id}_${Date.now()}_${i}`,
- name: equipment.name,
- type: equipment.type,
- rarity: equipment.rarity,
- quantity: 1,
- description: equipment.description,
- texture: equipment.texture,
- stats: equipment.stats || {},
- equipable: true
- };
-
- inventory.addItem(item);
- }
- }
-
- purchaseMaterial(material, quantity = 1) {
- const inventory = this.game.systems.inventory;
- const debugLogger = window.debugLogger;
-
- if (!inventory) {
- console.error('[ECONOMY] Inventory system not available for material purchase');
- return false;
- }
+ description: consumable.description,
+ texture: consumable.texture,
+ stats: consumable.stats || {},
+ acquired: new Date().toISOString()
+ };
try {
const oldInventorySize = inventory.items.length;
- console.log(`[DEBUG] Inventory state before purchase: ${oldInventorySize} items`);
+ inventory.addItem(item);
- // Create item object for inventory
- const item = {
- id: material.id,
- name: material.name,
- type: 'material',
- rarity: material.rarity || 'common',
- quantity: quantity,
- description: material.description || `A ${material.name} material`,
- stackable: true,
- stats: {},
- equipable: false,
- slot: null
- };
-
- console.log(`[DEBUG] Adding item to inventory: ${item.name}`);
- const added = inventory.addItem(item);
-
- console.log(`[DEBUG] addItem() returned: ${added}, new inventory size: ${inventory.items.length}`);
-
- if (debugLogger) debugLogger.logStep('Material purchase completed', {
- materialId: material.id,
- materialName: material.name,
+ if (debugLogger) debugLogger.logStep('Consumable purchase completed', {
+ itemId: consumable.id,
+ itemName: consumable.name,
quantity: quantity,
oldInventorySize: oldInventorySize,
- newInventorySize: inventory.items.length,
- addedSuccessfully: added
+ newInventorySize: inventory.items.length
});
-
- if (!added) {
- console.error('Failed to add material to inventory');
- return false;
- } else {
- // Update inventory UI to show the new material
- if (this.game.systems.ui && this.game.systems.ui.updateInventory) {
- this.game.systems.ui.updateInventory();
- } else if (this.game.systems.ui && this.game.systems.ui.updateUI) {
- this.game.systems.ui.updateUI();
- } else {
- console.warn('No UI update method available after material purchase');
- }
- console.log('[DEBUG] Material purchase completed and UI update called');
- return true;
- }
} catch (error) {
- console.error('Exception in purchaseMaterial:', error);
- if (debugLogger) debugLogger.errorEvent('Purchase Material Exception', error, {
- materialId: material.id,
- materialName: material.name
- });
- return false;
+ console.error('[ECONOMY] Error adding consumable to inventory:', error);
+ this.game.showNotification('Failed to add item to inventory', 'error', 3000);
}
}
- // Reward generation
- generateRewards(difficulty = 'normal', source = 'dungeon') {
+ purchaseMaterial(material, quantity) {
const debugLogger = window.debugLogger;
+ const inventory = this.game.systems.inventory;
- if (debugLogger) debugLogger.startStep('generateRewards', {
- difficulty: difficulty,
- source: source
- });
-
- const baseRewards = {
- easy: { credits: [50, 150], gems: [0, 1], experience: [20, 50] },
- normal: { credits: [100, 300], gems: [1, 3], experience: [50, 100] },
- hard: { credits: [200, 500], gems: [2, 5], experience: [100, 200] },
- extreme: { credits: [500, 1000], gems: [5, 10], experience: [200, 400] }
+ // Create item object for inventory
+ const item = {
+ id: material.id,
+ name: material.name,
+ type: material.type,
+ rarity: material.rarity,
+ quantity: quantity,
+ description: material.description,
+ texture: material.texture,
+ stackable: material.stackable || true,
+ acquired: new Date().toISOString()
};
- const rewards = baseRewards[difficulty] || baseRewards.normal;
- const generatedRewards = {};
-
- // Generate credits
- generatedRewards.credits = Math.floor(
- Math.random() * (rewards.credits[1] - rewards.credits[0] + 1) + rewards.credits[0]
- );
-
- // Generate gems
- generatedRewards.gems = Math.floor(
- Math.random() * (rewards.gems[1] - rewards.gems[0] + 1) + rewards.gems[0]
- );
-
- // Generate experience
- generatedRewards.experience = Math.floor(
- Math.random() * (rewards.experience[1] - rewards.experience[0] + 1) + rewards.experience[0]
- );
-
- // Bonus for premium currency (rare)
- if (Math.random() < 0.05) { // 5% chance
- generatedRewards.premiumCurrency = 1;
- }
-
- if (debugLogger) debugLogger.endStep('generateRewards', {
- difficulty: difficulty,
- source: source,
- generatedRewards: generatedRewards,
- rewardRanges: rewards
- });
-
- return generatedRewards;
- }
-
- giveRewards(rewards, source = 'unknown') {
- const debugLogger = window.debugLogger;
- const player = this.game.systems.player;
- const oldCredits = this.credits;
- const oldGems = this.gems;
- const oldExperience = player.stats.experience;
- const oldLevel = player.stats.level;
-
- if (debugLogger) debugLogger.startStep('giveRewards', {
- source: source,
- rewards: rewards,
- oldPlayerState: {
- credits: oldCredits,
- gems: oldGems,
- experience: oldExperience,
- level: oldLevel
- }
- });
-
- let totalRewards = [];
-
- // Add credits
- if (rewards.credits > 0) {
- this.addCredits(rewards.credits, source);
- totalRewards.push(`${rewards.credits} credits`);
- }
-
- // Add gems
- if (rewards.gems > 0) {
- this.addGems(rewards.gems, source);
- totalRewards.push(`${rewards.gems} gems`);
- }
-
- // Add premium currency
- if (rewards.premiumCurrency > 0) {
- this.addPremiumCurrency(rewards.premiumCurrency, source);
- totalRewards.push(`${rewards.premiumCurrency} premium currency`);
- }
-
- // Add experience
- if (rewards.experience > 0) {
- player.addExperience(rewards.experience);
- totalRewards.push(`${rewards.experience} experience`);
- }
-
- // Add materials
- if (rewards.materials && rewards.materials.length > 0) {
- const inventory = this.game.systems.inventory;
- alert(`[DUNGEON DEBUG] Adding ${rewards.materials.length} materials to inventory\nInventory available: ${!!inventory}\nMaterials: ${JSON.stringify(rewards.materials, null, 2)}`);
+ try {
+ const oldInventorySize = inventory.items.length;
+ inventory.addItem(item);
- for (const material of rewards.materials) {
- // Create proper item object like in purchaseMaterial
- const itemObject = {
- id: material.id,
- name: material.id.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase()), // Convert ID to readable name
- type: 'material',
- rarity: 'common',
- quantity: material.quantity,
- description: `A ${material.id.replace('_', ' ')} material`,
- stackable: true,
- stats: {},
- equipable: false,
- slot: null
- };
-
- alert(`[DUNGEON DEBUG] Fixed! Adding material as full item object:\n${JSON.stringify(itemObject, null, 2)}`);
- inventory.addItem(itemObject);
- totalRewards.push(`${material.quantity}x ${material.id}`);
- }
+ if (debugLogger) debugLogger.logStep('Material purchase completed', {
+ itemId: material.id,
+ itemName: material.name,
+ quantity: quantity,
+ oldInventorySize: oldInventorySize,
+ newInventorySize: inventory.items.length
+ });
+ } catch (error) {
+ console.error('[ECONOMY] Error adding material to inventory:', error);
+ this.game.showNotification('Failed to add item to inventory', 'error', 3000);
}
-
- // Add items
- if (rewards.items && rewards.items.length > 0) {
- const inventory = this.game.systems.inventory;
- for (const item of rewards.items) {
- inventory.addItem(item.id, item.quantity || 1);
- totalRewards.push(`${item.quantity || 1}x ${item.id}`);
- }
- }
-
- // Show reward notification
- if (totalRewards.length > 0) {
- const rewardText = totalRewards.join(', ');
- this.game.showNotification(`Rewards: ${rewardText}`, 'success', 3000);
- }
-
- if (debugLogger) debugLogger.endStep('giveRewards', {
- source: source,
- rewardsGiven: rewards,
- totalRewardsText: totalRewards.join(', '),
- currencyChanges: {
- credits: { old: oldCredits, new: this.credits, change: this.credits - oldCredits },
- gems: { old: oldGems, new: this.gems, change: this.gems - oldGems }
- },
- playerChanges: {
- experience: { old: oldExperience, new: player.stats.experience, change: player.stats.experience - oldExperience },
- level: { old: oldLevel, new: player.stats.level, leveledUp: player.stats.level > oldLevel }
- }
- });
}
- // Daily rewards and bonuses
- claimDailyReward() {
- const debugLogger = window.debugLogger;
- const lastClaim = localStorage.getItem('lastDailyReward');
- const today = new Date().toDateString();
+ // Currency management
+ addCredits(amount, source = 'unknown') {
+ const oldCredits = this.credits;
+ this.credits += amount;
- if (debugLogger) debugLogger.startStep('claimDailyReward', {
- lastClaim: lastClaim,
- today: today,
- alreadyClaimed: lastClaim === today
+ // Add transaction
+ this.addTransaction({
+ type: 'credit',
+ amount: amount,
+ source: source,
+ balance: this.credits,
+ timestamp: new Date().toISOString()
});
- if (lastClaim === today) {
- this.game.showNotification('Daily reward already claimed!', 'warning', 3000);
- if (debugLogger) debugLogger.endStep('claimDailyReward', {
- success: false,
- reason: 'Already claimed today'
- });
- return false;
+ console.log(`[ECONOMY] Added ${amount} credits from ${source}. New balance: ${this.credits}`);
+ this.updateUI();
+
+ return this.credits - oldCredits;
+ }
+
+ addGems(amount, source = 'unknown') {
+ const oldGems = this.gems;
+ this.gems += amount;
+
+ // Add transaction
+ this.addTransaction({
+ type: 'gem',
+ amount: amount,
+ source: source,
+ balance: this.gems,
+ timestamp: new Date().toISOString()
+ });
+
+ console.log(`[ECONOMY] Added ${amount} gems from ${source}. New balance: ${this.gems}`);
+ this.updateUI();
+
+ return this.gems - oldGems;
+ }
+
+ canAfford(cost, currency = 'credits') {
+ if (currency === 'credits') {
+ return this.credits >= cost;
+ } else if (currency === 'gems') {
+ return this.gems >= cost;
+ } else if (currency === 'premium') {
+ return this.premiumCurrency >= cost;
}
+ return false;
+ }
+
+ // Transaction management
+ addTransaction(transaction) {
+ this.transactions.push(transaction);
+ this.transactionHistory.push(transaction);
- // Calculate reward based on consecutive days
- const consecutiveDays = parseInt(localStorage.getItem('consecutiveDailyRewards') || '0') + 1;
- const baseReward = 100;
- const bonusMultiplier = Math.min(consecutiveDays * 0.1, 2); // Max 2x bonus
- const credits = Math.floor(baseReward * (1 + bonusMultiplier));
- const gems = Math.min(Math.floor(consecutiveDays / 7), 5); // 1 gem per week, max 5
-
- if (debugLogger) debugLogger.logStep('Daily reward calculation', {
- consecutiveDays: consecutiveDays,
- baseReward: baseReward,
- bonusMultiplier: bonusMultiplier,
- creditsReward: credits,
- gemsReward: gems
- });
-
- // Give rewards
- this.addCredits(credits, 'daily_reward');
- this.addGems(gems, 'daily_reward');
-
- // Update daily reward tracking
- localStorage.setItem('lastDailyReward', today);
- localStorage.setItem('consecutiveDailyRewards', consecutiveDays.toString());
-
- // Show notification
- const rewardText = `${credits} credits${gems > 0 ? ` and ${gems} gems` : ''}`;
- this.game.showNotification(`Daily reward claimed: ${rewardText}! (${consecutiveDays} day streak)`, 'success', 4000);
-
- if (debugLogger) debugLogger.endStep('claimDailyReward', {
- success: true,
- consecutiveDays: consecutiveDays,
- creditsReward: credits,
- gemsReward: gems,
- rewardText: rewardText,
- newLastClaim: today,
- newConsecutiveDays: consecutiveDays
- });
-
- return true;
+ // Keep only last 100 transactions in memory
+ if (this.transactions.length > 100) {
+ this.transactions = this.transactions.slice(-100);
+ }
}
// UI updates
updateUI() {
- const debugLogger = window.debugLogger;
-
- if (debugLogger) debugLogger.startStep('updateUI', {
- currentCredits: this.credits,
- currentGems: this.gems,
- currentPremiumCurrency: this.premiumCurrency
- });
-
- // Update resource displays
- const creditsElement = document.getElementById('credits');
- if (creditsElement) {
- creditsElement.textContent = this.game.formatNumber(this.credits);
+ // Update resource display
+ if (this.game.systems.ui) {
+ this.game.systems.ui.updateResourceDisplay();
}
- const gemsElement = document.getElementById('gems');
- if (gemsElement) {
- gemsElement.textContent = this.game.formatNumber(this.gems);
- }
-
- const premiumElement = document.getElementById('premiumCurrency');
- if (premiumElement) {
- premiumElement.textContent = this.game.formatNumber(this.premiumCurrency);
- }
-
- // Update shop UI when shop tab is active
+ // Update shop UI if open
this.updateShopUI();
-
- if (debugLogger) debugLogger.endStep('updateUI', {
- creditsUpdated: !!creditsElement,
- gemsUpdated: !!gemsElement,
- premiumUpdated: !!premiumElement,
- shopUIUpdated: true
- });
}
- // Reset economy (for new game)
- reset() {
+ updateShopUI() {
const debugLogger = window.debugLogger;
+
+ console.log('[ECONOMY] updateShopUI called');
+ console.log('[ECONOMY] Multiplayer mode:', window.smartSaveManager?.isMultiplayer);
+ console.log('[ECONOMY] ItemSystem available:', !!(this.game.systems.itemSystem));
+ console.log('[ECONOMY] ItemSystem catalog:', !!(this.game.systems.itemSystem?.itemCatalog));
+
+ if (window.smartSaveManager?.isMultiplayer) {
+ // Check if ItemSystem is ready before using it
+ if (!this.game.systems.itemSystem || !this.game.systems.itemSystem.itemCatalog) {
+ console.log('[ECONOMY] ItemSystem not ready yet, skipping shop update');
+ return;
+ }
+
+ // Safe to use ItemSystem now
+ const items = Array.from(this.game.systems.itemSystem.shopItems || []);
+ console.log('[ECONOMY] Rendering shop with', items.length, 'items from ItemSystem');
+ console.log('[ECONOMY] First few items:', items.slice(0, 3));
+ this.renderShopItems(items);
+ } else {
+ // Singleplayer mode - use local shop data
+ console.log('[ECONOMY] Singleplayer mode - using local shop data');
+ const items = Object.values(this.randomShopItems).flat();
+ console.log('[ECONOMY] Rendering shop with', items.length, 'local items');
+ this.renderShopItems(items);
+ }
+ }
+
+ renderShopItems(items) {
+ const shopItemsElement = document.getElementById('shopItems');
+ if (!shopItemsElement) return;
+
+ const activeCategory = document.querySelector('.shop-cat-btn.active')?.dataset.category || 'ships';
+ console.log('[ECONOMY] Active shop category:', activeCategory);
+ console.log('[ECONOMY] All items types:', items.map(item => ({id: item.id, type: item.type, name: item.name})));
+ console.log('[ECONOMY] Unique item types:', [...new Set(items.map(item => item.type))]);
+
+ // Map category names to item types (handle plural/singular mismatches)
+ const categoryTypeMap = {
+ 'ships': 'ship',
+ 'weapons': 'weapon',
+ 'armors': 'armor',
+ 'cosmetics': 'cosmetic',
+ 'consumables': 'consumable',
+ 'materials': 'material',
+ 'keys': 'key'
+ };
+
+ const targetItemType = categoryTypeMap[activeCategory] || activeCategory;
+ console.log('[ECONOMY] Mapped category', activeCategory, 'to item type', targetItemType);
+
+ const categoryItems = items.filter(item => item.type === targetItemType);
+ console.log('[ECONOMY] Filtered items for category', activeCategory, '(type:', targetItemType, ') :', categoryItems.length, 'items');
+
+ if (categoryItems.length === 0) {
+ shopItemsElement.innerHTML = '
No items available in this category
';
+ return;
+ }
+
+ shopItemsElement.innerHTML = categoryItems.map(item => {
+ const canAfford = this.canAfford(item.price, item.currency);
+ const isOwned = item.type === 'cosmetic' && this.ownedCosmetics.includes(item.id);
+
+ return `
+
+
+
+
+
${item.description}
+
+ ${this.formatPrice(item)}
+
+
+
+
+
+ `;
+ }).join('');
+ }
+
+ formatPrice(item) {
+ if (!item.price) return 'Free';
+
+ const currency = item.currency || 'credits';
+ const price = this.game.formatNumber(item.price);
+
+ return `${price} ${currency}`;
+ }
+
+ // Save/Load functionality
+ save() {
+ return {
+ credits: this.credits,
+ gems: this.gems,
+ premiumCurrency: this.premiumCurrency,
+ transactions: this.transactions,
+ ownedCosmetics: this.ownedCosmetics,
+ shopData: {
+ randomShopItems: this.randomShopItems,
+ categoryPurchaseLimits: this.categoryPurchaseLimits,
+ lastShopRefresh: this.lastShopRefresh
+ }
+ };
+ }
+
+ load(data) {
+ if (data.credits !== undefined) this.credits = data.credits;
+ if (data.gems !== undefined) this.gems = data.gems;
+ if (data.premiumCurrency !== undefined) this.premiumCurrency = data.premiumCurrency;
+ if (data.transactions) this.transactions = data.transactions;
+ if (data.ownedCosmetics) this.ownedCosmetics = data.ownedCosmetics;
+
+ // Load shop data
+ if (data.shopData) {
+ this.randomShopItems = data.shopData.randomShopItems || {};
+ this.categoryPurchaseLimits = data.shopData.categoryPurchaseLimits || {};
+ this.lastShopRefresh = data.shopData.lastShopRefresh || null;
+ }
+
+ this.updateUI();
+ }
+
+ // Reset functionality
+ reset() {
const oldState = {
credits: this.credits,
gems: this.gems,
@@ -1965,10 +625,6 @@ class Economy {
ownedCosmeticsCount: this.ownedCosmetics.length
};
- if (debugLogger) debugLogger.startStep('reset', {
- oldState: oldState
- });
-
this.credits = 1000;
this.gems = 10;
this.premiumCurrency = 0;
@@ -1977,23 +633,13 @@ class Economy {
// Reset daily rewards
localStorage.removeItem('lastDailyReward');
- localStorage.removeItem('consecutiveDailyRewards');
- if (debugLogger) debugLogger.endStep('reset', {
- oldState: oldState,
- newState: {
- credits: this.credits,
- gems: this.gems,
- premiumCurrency: this.premiumCurrency,
- transactionCount: this.transactions.length,
- ownedCosmeticsCount: this.ownedCosmetics.length
- }
- });
+ this.updateUI();
+
+ return oldState;
}
- // Clear all data
clear() {
- const debugLogger = window.debugLogger;
const oldState = {
credits: this.credits,
gems: this.gems,
@@ -2002,334 +648,40 @@ class Economy {
ownedCosmeticsCount: this.ownedCosmetics.length
};
- if (debugLogger) debugLogger.startStep('clear', {
- oldState: oldState
- });
-
this.credits = 0;
this.gems = 0;
this.premiumCurrency = 0;
this.transactions = [];
this.ownedCosmetics = [];
- if (debugLogger) debugLogger.endStep('clear', {
- oldState: oldState,
- newState: {
- credits: this.credits,
- gems: this.gems,
- premiumCurrency: this.premiumCurrency,
- transactionCount: this.transactions.length,
- ownedCosmeticsCount: this.ownedCosmetics.length
- }
- });
+ this.updateUI();
+
+ return oldState;
}
- updateShopUI() {
- const debugLogger = window.debugLogger;
- const shopItemsElement = document.getElementById('shopItems');
-
- if (!shopItemsElement) {
- if (debugLogger) debugLogger.log('updateShopUI: Shop items element not found');
- return;
- }
-
- const activeCategory = document.querySelector('.shop-cat-btn.active')?.dataset.category || 'ships';
-
- // Always use random shop items when the system is available
- // If no random items exist for this category, trigger a refresh
- if (!this.randomShopItems[activeCategory] || this.randomShopItems[activeCategory].length === 0) {
- console.log(`[ECONOMY] No random items for ${activeCategory}, triggering refresh`);
- this.refreshRandomShop();
- }
-
- const items = this.randomShopItems[activeCategory] || [];
- const isRandomShop = true; // Always random shop when system is available
-
- if (debugLogger) debugLogger.startStep('updateShopUI', {
- activeCategory: activeCategory,
- itemCount: items.length,
- isRandomShop: isRandomShop,
- currentCredits: this.credits,
- currentGems: this.gems,
- currentPremiumCurrency: this.premiumCurrency
- });
-
- shopItemsElement.innerHTML = '';
-
- // Add shop refresh info if using random shop
- if (isRandomShop) {
- const refreshInfo = document.createElement('div');
- refreshInfo.className = 'shop-refresh-info';
- refreshInfo.innerHTML = `
-
-
-
- Next refresh in: ${this.getShopRefreshCountdown()}
-
-
- `;
- shopItemsElement.appendChild(refreshInfo);
- }
-
- items.forEach(item => {
- const itemElement = document.createElement('div');
- itemElement.className = 'shop-item';
-
- const canAfford = this.canAfford(item);
- const isOwned = item.type === 'cosmetic' && this.ownedCosmetics.includes(item.id);
-
- itemElement.innerHTML = `
-
- ${item.texture ?
- `
-
-
` : ''}
-
-
${item.name}
-
${item.description}
- ${item.stats ? `
-
- ${Object.entries(item.stats).map(([stat, value]) =>
- `
${stat}: ${value}
`
- ).join('')}
-
- ` : ''}
-
${this.formatPrice(item)}
-
${item.rarity}
-
-
-
- ${isOwned ? 'Owned' : canAfford ? 'Purchase' : 'Cannot Afford'}
-
- `;
-
- shopItemsElement.appendChild(itemElement);
-
- if (debugLogger) debugLogger.logStep('Shop item rendered', {
- itemId: item.id,
- itemName: item.name,
- itemCategory: activeCategory,
- itemPrice: item.price,
- itemCurrency: item.currency,
- canAfford: canAfford,
- isOwned: isOwned
- });
- });
-
- // Re-setup event listeners since we just recreated all the buttons
- this.setupShopEventListeners();
-
- if (debugLogger) debugLogger.endStep('updateShopUI', {
- activeCategory: activeCategory,
- itemsRendered: items.length,
- shopUIUpdated: true
- });
+ // Initialize random shop for singleplayer (minimal implementation)
+ initializeRandomShop() {
+ console.log('[ECONOMY] Random shop not available in singleplayer mode');
+ this.randomShopItems = {};
}
- // Testing and utility methods
- testPurchase(itemId) {
- const debugLogger = window.debugLogger;
-
- if (debugLogger) debugLogger.startStep('testPurchase', {
- itemId: itemId
- });
-
- const item = this.findShopItem(itemId);
- if (!item) {
- if (debugLogger) debugLogger.endStep('testPurchase', {
- success: false,
- reason: 'Item not found',
- itemId: itemId
- });
- return false;
- }
-
- const result = this.purchaseItem(itemId);
-
- if (debugLogger) debugLogger.endStep('testPurchase', {
- success: result,
- itemId: itemId,
- itemName: item.name,
- itemCategory: item.type
- });
-
- return result;
- }
-
- formatItemStats(item) {
- const debugLogger = window.debugLogger;
-
- if (debugLogger) debugLogger.startStep('formatItemStats', {
- itemId: item.id,
- itemType: item.type
- });
-
- let stats = [];
-
- if (item.stats) {
- for (const [stat, value] of Object.entries(item.stats)) {
- stats.push(`${stat}: +${value}`);
- }
- }
-
- if (item.effect) {
- if (item.effect.attackMultiplier) {
- stats.push(`Attack: x${item.effect.attackMultiplier}`);
- }
- if (item.effect.defense) {
- stats.push(`Defense: +${item.effect.defense}`);
- }
- if (item.effect.healing) {
- stats.push(`Healing: +${item.effect.healing}`);
- }
- if (item.effect.energyRestore) {
- stats.push(`Energy: +${item.effect.energyRestore}`);
- }
- }
-
- const formattedStats = stats.length > 0 ? stats.join(', ') : 'No special stats';
-
- if (debugLogger) debugLogger.endStep('formatItemStats', {
- itemId: item.id,
- formattedStats: formattedStats,
- statCount: stats.length
- });
-
- return formattedStats;
- }
-
- // Save and load
- save() {
- const debugLogger = window.debugLogger;
-
- // if (debugLogger) debugLogger.startStep('save', {
- // currentCredits: this.credits,
- // currentGems: this.gems,
- // currentPremiumCurrency: this.premiumCurrency,
- // transactionCount: this.transactions.length,
- // ownedCosmeticsCount: this.ownedCosmetics.length
- // });
-
- const saveData = {
- credits: this.credits,
- gems: this.gems,
- premiumCurrency: this.premiumCurrency,
- transactions: this.transactions,
- ownedCosmetics: this.ownedCosmetics,
- // Include shop data with timestamp for proper refresh calculation
- shopData: {
- randomShopItems: this.randomShopItems,
- categoryPurchaseLimits: this.categoryPurchaseLimits,
- lastShopRefresh: this.lastShopRefresh,
- saveTimestamp: Date.now() // When this save was created
- }
- };
-
- // if (debugLogger) debugLogger.endStep('save', {
- // saveDataSize: JSON.stringify(saveData).length,
- // economyState: saveData
- // });
-
- return saveData;
- }
-
- load(data) {
- const debugLogger = window.debugLogger;
- const oldState = {
+ // Get system statistics
+ getStats() {
+ return {
credits: this.credits,
gems: this.gems,
premiumCurrency: this.premiumCurrency,
transactionCount: this.transactions.length,
- ownedCosmeticsCount: this.ownedCosmetics.length
+ ownedCosmeticsCount: this.ownedCosmetics.length,
+ shopItemsCount: this.game.systems.itemSystem && this.game.systems.itemSystem.itemCatalog ?
+ this.game.systems.itemSystem.getStats().totalItems : 0
};
-
- // if (debugLogger) debugLogger.startStep('load', {
- // oldState: oldState,
- // loadData: data
- // });
-
- try {
- this.credits = data.credits || 0;
- this.gems = data.gems || 0;
- this.premiumCurrency = data.premiumCurrency || 0;
- this.transactions = data.transactions || [];
- this.ownedCosmetics = data.ownedCosmetics || [];
-
- // Load shop data and calculate if refresh is needed
- if (data.shopData) {
- console.log('[ECONOMY] Loading shop data from save');
-
- // Restore shop data
- this.randomShopItems = data.shopData.randomShopItems || {};
- this.categoryPurchaseLimits = data.shopData.categoryPurchaseLimits || {};
- this.lastShopRefresh = data.shopData.lastShopRefresh || null;
-
- // Calculate if shop should have refreshed while game was closed
- const saveTimestamp = data.shopData.saveTimestamp || Date.now();
- const currentTime = Date.now();
- const timeSinceSave = currentTime - saveTimestamp;
-
- if (this.lastShopRefresh) {
- const timeSinceLastRefresh = currentTime - this.lastShopRefresh;
- const totalIntervalsPassed = Math.floor(timeSinceLastRefresh / this.SHOP_REFRESH_INTERVAL);
-
- if (totalIntervalsPassed > 0) {
- console.log(`[ECONOMY] Shop missed ${totalIntervalsPassed} refresh(es) while game was closed, refreshing now`);
- this.refreshRandomShop();
- } else {
- console.log('[ECONOMY] Shop items still valid, no refresh needed');
- }
- } else {
- console.log('[ECONOMY] No previous shop refresh, generating new shop');
- this.refreshRandomShop();
- }
- } else {
- console.log('[ECONOMY] No shop data in save, initializing new shop');
- this.refreshRandomShop();
- }
-
- if (debugLogger) debugLogger.endStep('load', {
- success: true,
- oldState: oldState,
- newState: {
- credits: this.credits,
- gems: this.gems,
- premiumCurrency: this.premiumCurrency,
- transactionCount: this.transactions.length,
- ownedCosmeticsCount: this.ownedCosmetics.length
- }
- });
- } catch (error) {
- if (debugLogger) debugLogger.errorEvent('load', error, {
- oldState: oldState,
- error: error.message
- });
- throw error;
- }
- }
-
- // Utility methods for shop
- canAfford(item) {
- if (item.currency === 'credits') {
- return this.credits >= item.price;
- } else if (item.currency === 'gems') {
- return this.gems >= item.price;
- } else if (item.currency === 'premium') {
- return this.premiumCurrency >= item.price;
- }
- return false;
- }
-
- formatPrice(item) {
- const currencySymbols = {
- 'credits': '₵',
- 'gems': '💎',
- 'premium': '⭐'
- };
-
- const symbol = currencySymbols[item.currency] || item.currency;
- return `${symbol}${this.game.formatNumber(item.price)}`;
}
}
+
+// Export for use in other modules
+if (typeof module !== 'undefined' && module.exports) {
+ module.exports = Economy;
+} else {
+ window.Economy = Economy;
+}
diff --git a/Client/js/core/GameEngine.js b/Client/js/core/GameEngine.js
index 6534af9..713040a 100644
--- a/Client/js/core/GameEngine.js
+++ b/Client/js/core/GameEngine.js
@@ -5,66 +5,53 @@
class GameEngine extends EventTarget {
constructor() {
- super(); // Call EventTarget constructor first
-
- console.log('🛑 DEBUG STOP 1: GameEngine constructor starting');
- const debugLogger = window.debugLogger;
- if (debugLogger) debugLogger.log('GameEngine constructor called', {
- autoSaveInterval: 5,
- timestamp: new Date().toISOString()
- });
+ // Must call super() first since we extend EventTarget
+ super();
+ // Basic game state
this.isRunning = false;
- this.lastUpdate = 0;
+ this.isPaused = false;
this.gameTime = 0;
+ this.lastSaveTime = 0;
+ this.autoSaveInterval = 5000; // 5 seconds
+ this.gameLogicInterval = 1000; // 1 second for game updates
- // Auto-save settings
- this.autoSaveInterval = 1; // Default 1 minute
- this.autoSaveTimer = null;
- this.lastAutoSave = 0;
+ // Game systems
+ this.systems = {};
- // Save slot information
+ // Save slot configuration
this.saveSlotInfo = {
- slot: 1, // Default save slot
- useFileSystem: !!window.electronAPI // Use file system if Electron API is available
+ slot: 1,
+ useFileSystem: true
};
- console.log('🛑 DEBUG STOP 2: Save slot info initialized:', this.saveSlotInfo);
-
- // GUI update settings
- this.guiUpdateInterval = 1000; // Update GUI once per second (1000ms)
- this.lastGUIUpdate = 0;
-
- // Game logic settings (independent of frame rate)
- this.gameLogicInterval = 1000; // Update game logic every 1 second
- this.gameLogicTimer = null;
-
// Game state
this.state = {
paused: false,
currentTab: 'dashboard',
- notifications: [],
- loading: true
+ notifications: []
};
- console.log('🛑 DEBUG STOP 3: Game state initialized:', this.state);
-
- // Systems
- this.systems = {};
-
// Event listeners
this.eventListeners = new Map();
- console.log('🛑 DEBUG STOP 4: About to call this.init()');
+ // Initialize immediately
this.init();
- console.log('🛑 DEBUG STOP 5: GameEngine constructor completed');
}
setMultiplayerMode(isMultiplayer, socket = null, serverData = null, currentUser = null) {
const debugLogger = window.debugLogger;
console.log('[GAME ENGINE] Setting multiplayer mode:', isMultiplayer);
- if (debugLogger) debugLogger.logStep('setMultiplayerMode', { isMultiplayer });
+ console.log('[GAME ENGINE] Previous mode was:', this.isMultiplayer);
+ if (debugLogger) debugLogger.logStep('setMultiplayerMode', { isMultiplayer, previousMode: this.isMultiplayer });
+
+ // CRITICAL: Once set to multiplayer, never allow fallback to singleplayer
+ if (this.isMultiplayer && !isMultiplayer) {
+ console.warn('[GAME ENGINE] ATTEMPTED FALLBACK TO SINGLEPLAYER - BLOCKING!');
+ console.log('[GAME ENGINE] Preserving multiplayer mode');
+ return; // Don't allow fallback to singleplayer
+ }
this.isMultiplayer = isMultiplayer;
this.socket = socket;
@@ -106,17 +93,33 @@ class GameEngine extends EventTarget {
if (debugLogger) await debugLogger.startStep('gameEngineInit');
try {
- // Initialize core systems but don't setup new game data
- await this.initializeSystemsForLoad();
+ // In multiplayer mode, use simplified initialization to avoid hanging
+ if (this.isMultiplayer) {
+ console.log('[GAME ENGINE] Using simplified multiplayer initialization');
+ try {
+ await this.initializeMultiplayerSystems();
+ console.log('[GAME ENGINE] Multiplayer initialization complete - skipping event listeners');
+ } catch (multiplayerError) {
+ console.error('[GAME ENGINE] Multiplayer systems initialization failed:', multiplayerError);
+ // Don't fall back to singleplayer - keep multiplayer mode but with minimal systems
+ console.log('[GAME ENGINE] Continuing with minimal multiplayer systems');
+ // Create essential systems only
+ this.systems.player = new Player(this);
+ this.systems.inventory = new Inventory(this);
+ this.systems.economy = new Economy(this);
+ this.systems.itemSystem = new ItemSystem(this);
+ this.systems.idleSystem = new IdleSystem(this);
+ }
+ } else {
+ // Full initialization for singleplayer
+ await this.initializeSystemsForLoad();
+
+ if (debugLogger) await debugLogger.logStep('Systems initialized, setting up event listeners');
+
+ // Set up event listeners (only in singleplayer to avoid conflicts)
+ await this.setupEventListeners();
+ }
- if (debugLogger) await debugLogger.logStep('Systems initialized, setting up event listeners');
-
- // Set up event listeners
- await this.setupEventListeners();
-
- if (debugLogger) await debugLogger.logStep('Event listeners setup complete');
-
- if (logger) await logger.info('Game engine initialization completed');
if (debugLogger) await debugLogger.endStep('gameEngineInit', {
systemsInitialized: Object.keys(this.systems).length,
eventListeners: this.eventListeners.size
@@ -129,84 +132,72 @@ class GameEngine extends EventTarget {
}
}
- async startGame(continueGame = false) {
- const logger = window.logger;
- const debugLogger = window.debugLogger;
-
- console.log('[GAME ENGINE] startGame called with continueGame =', continueGame);
- if (logger) await logger.info('Starting game', { continueGame });
- if (debugLogger) await debugLogger.startStep('startGame', { continueGame });
+ // Simplified multiplayer-only system initialization to avoid hanging
+ async initializeMultiplayerSystems() {
+ console.log('[GAME ENGINE] Initializing multiplayer systems');
try {
- if (continueGame) {
- console.log('[GAME ENGINE] Loading existing save data...');
- if (debugLogger) await debugLogger.logStep('Loading existing save data');
- await this.loadGame();
- console.log('[GAME ENGINE] Save data loaded');
- } else {
- console.log('[GAME ENGINE] Creating new game...');
- if (debugLogger) await debugLogger.logStep('Creating new game');
- await this.newGame();
- console.log('[GAME ENGINE] New game created');
- }
+ // Initialize texture manager first
+ this.systems.textureManager = new TextureManager(this);
- // Start game loop
- // CRITICAL: Ensure UIManager is initialized before starting game
- if (this.systems.ui) {
- console.log('[GAME ENGINE] Final UIManager initialization check...');
- try {
- await this.systems.ui.initialize();
- console.log('[GAME ENGINE] UIManager initialized successfully');
- } catch (error) {
- console.error('[GAME ENGINE] UIManager initialization failed:', error);
- }
- }
+ // Create essential systems immediately
+ console.log('[GAME ENGINE] Creating essential systems');
+ this.systems.player = new Player(this);
+ this.systems.inventory = new Inventory(this);
+ this.systems.economy = new Economy(this);
+ this.systems.ui = new UIManager(this);
+ this.systems.idleSystem = new IdleSystem(this);
+ this.systems.itemSystem = new ItemSystem(this);
- this.start();
- console.log('[GAME ENGINE] Game loop started');
- if (debugLogger) await debugLogger.logStep('Game loop started');
+ console.log('[GAME ENGINE] Essential systems created successfully');
- const loadingStatus = document.getElementById('loadingStatus');
- if (loadingStatus) {
- console.log('[GAME ENGINE] Hiding loading status text');
- if (debugLogger) await debugLogger.logStep('Hiding loading status text');
- loadingStatus.classList.add('hidden');
- }
-
- const gameInterface = document.getElementById('gameInterface');
- if (gameInterface) {
- console.log('[GAME ENGINE] Showing game interface');
- if (debugLogger) await debugLogger.logStep('Showing game interface');
- gameInterface.classList.remove('hidden');
-
- // CRITICAL: Initialize UIManager after showing interface
- console.log('[GAME ENGINE] Initializing UIManager after showing interface...');
- if (this.systems.ui) {
- try {
- await this.systems.ui.initialize();
- console.log('[GAME ENGINE] UIManager initialized successfully (post-interface)');
- } catch (error) {
- console.error('[GAME ENGINE] UIManager initialization failed:', error);
- }
- } else {
- console.log('[GAME ENGINE] UIManager not found when trying to initialize after interface');
- }
- } else {
- console.warn('[GAME ENGINE] gameInterface element not found');
- if (debugLogger) await debugLogger.warn('gameInterface element not found');
- }
-
- if (logger) await logger.info('Game started successfully');
- if (debugLogger) await debugLogger.endStep('startGame', {
- continueGame,
- isRunning: this.isRunning,
- gameTime: this.gameTime
+ // Initialize ItemSystem ONCE and emit event when ready
+ console.log('[GAME ENGINE] Initializing ItemSystem (single initialization)');
+ this.systems.itemSystem.initialize().then(() => {
+ console.log('[GAME ENGINE] ItemSystem fully initialized, emitting ready event');
+ this.emit('itemSystemReady');
+ }).catch(error => {
+ console.error('[GAME ENGINE] ItemSystem initialization failed:', error);
+ // Still emit event so Economy can fallback gracefully
+ this.emit('itemSystemReady');
});
+ // Initialize Economy (without ItemSystem - it will wait for the event)
+ console.log('[GAME ENGINE] Initializing Economy system');
+ this.systems.economy.initialize().catch(error => {
+ console.error('[GAME ENGINE] Economy initialization failed:', error);
+ });
+
+ // Create additional systems asynchronously to avoid blocking
+ setTimeout(() => {
+ console.log('[GAME ENGINE] Creating additional multiplayer systems asynchronously');
+
+ if (typeof SkillSystem !== 'undefined') {
+ this.systems.skillSystem = new SkillSystem(this);
+ }
+ if (typeof DungeonSystem !== 'undefined') {
+ this.systems.dungeonSystem = new DungeonSystem(this);
+ // Initialize server-driven dungeon system
+ this.systems.dungeonSystem.initialize().then(() => {
+ console.log('[GAME ENGINE] DungeonSystem initialized with server data');
+ }).catch(error => {
+ console.error('[GAME ENGINE] Failed to initialize DungeonSystem:', error);
+ });
+ }
+ // REMOVED: QuestSystem should be server-driven only
+ if (typeof CraftingSystem !== 'undefined') {
+ this.systems.crafting = new CraftingSystem(this);
+ }
+ if (typeof BaseSystem !== 'undefined') {
+ this.systems.base = new BaseSystem(this);
+ }
+ console.log('[GAME ENGINE] All multiplayer systems created asynchronously');
+ }, 100); // Create after 100ms delay
+
} catch (error) {
- console.error('[GAME ENGINE] Failed to start game:', error);
- if (logger) await logger.errorEvent(error, 'Game Start');
- if (debugLogger) await debugLogger.errorEvent(error, 'Game Start');
+ console.error('[GAME ENGINE] Error in multiplayer systems initialization:', error);
+ // Don't re-throw - allow game to continue with basic systems
+ console.log('[GAME ENGINE] Continuing with available systems');
}
}
@@ -242,35 +233,20 @@ class GameEngine extends EventTarget {
if (logger) await logger.systemEvent('Economy', 'Created');
if (debugLogger) await debugLogger.logStep('Economy system created');
- if (debugLogger) await debugLogger.logStep('Creating IdleSystem');
- this.systems.idleSystem = new IdleSystem(this);
- if (logger) await logger.systemEvent('IdleSystem', 'Created');
- if (debugLogger) await debugLogger.logStep('IdleSystem created');
-
- if (debugLogger) await debugLogger.logStep('Creating DungeonSystem');
- this.systems.dungeonSystem = new DungeonSystem(this);
- if (logger) await logger.systemEvent('DungeonSystem', 'Created');
- if (debugLogger) await debugLogger.logStep('DungeonSystem created');
-
- if (debugLogger) await debugLogger.logStep('Creating SkillSystem');
- this.systems.skillSystem = new SkillSystem(this);
- if (logger) await logger.systemEvent('SkillSystem', 'Created');
- if (debugLogger) await debugLogger.logStep('SkillSystem created');
-
- if (debugLogger) await debugLogger.logStep('Creating BaseSystem');
- this.systems.baseSystem = new BaseSystem(this);
- if (logger) await logger.systemEvent('BaseSystem', 'Created');
- if (debugLogger) await debugLogger.logStep('BaseSystem created');
-
- if (debugLogger) await debugLogger.logStep('Creating QuestSystem');
- this.systems.questSystem = new QuestSystem(this);
- if (logger) await logger.systemEvent('QuestSystem', 'Created');
- if (debugLogger) await debugLogger.logStep('QuestSystem created');
-
- if (debugLogger) await debugLogger.logStep('Creating ShipSystem');
- this.systems.shipSystem = new ShipSystem(this);
- if (logger) await logger.systemEvent('ShipSystem', 'Created');
- if (debugLogger) await debugLogger.logStep('ShipSystem created');
+ // In multiplayer mode, skip singleplayer systems
+ if (!this.isMultiplayer) {
+ if (debugLogger) await debugLogger.logStep('Creating IdleSystem');
+ this.systems.idleSystem = new IdleSystem(this);
+ if (logger) await logger.systemEvent('IdleSystem', 'Created');
+ if (debugLogger) await debugLogger.logStep('IdleSystem created');
+
+ if (debugLogger) await debugLogger.logStep('Creating ItemSystem');
+ this.systems.itemSystem = new ItemSystem(this);
+ if (logger) await logger.systemEvent('ItemSystem', 'Created');
+ if (debugLogger) await debugLogger.logStep('ItemSystem created');
+ } else {
+ console.log('[GAME ENGINE] Multiplayer mode - skipping singleplayer systems (IdleSystem, ItemSystem)');
+ }
if (debugLogger) await debugLogger.logStep('Creating UIManager');
if (typeof UIManager !== 'undefined') {
@@ -284,23 +260,7 @@ class GameEngine extends EventTarget {
} else {
console.error('[GAME ENGINE] UIManager class not found - this should not happen!');
if (debugLogger) await debugLogger.error('UIManager class not found - this should not happen!');
- // Create minimal UI system to prevent crashes (should never be needed based on logs)
- this.systems.ui = {
- showNotification: (message, type, duration) => {
- console.log(`[UI MINIMAL] ${type}: ${message}`);
- },
- updateUI: () => {
- console.log('[UI MINIMAL] updateUI called');
- },
- switchTab: (tabName) => {
- console.log('[UI MINIMAL] switchTab called');
- }
- };
}
- if (debugLogger) await debugLogger.logStep('Creating CraftingSystem');
- this.systems.crafting = new CraftingSystem(this);
- if (logger) await logger.systemEvent('CraftingSystem', 'Created');
- if (debugLogger) await debugLogger.logStep('CraftingSystem created');
if (debugLogger) await debugLogger.endStep('initializeSystemsForLoad', {
systemsCreated: Object.keys(this.systems).length
@@ -309,388 +269,63 @@ class GameEngine extends EventTarget {
}
}
- async initializeSystems() {
+ async startGame(continueGame = false) {
const logger = window.logger;
const debugLogger = window.debugLogger;
- if (debugLogger) await debugLogger.startStep('initializeSystems');
+ console.log('[GAME ENGINE] startGame called with continueGame =', continueGame);
+ if (logger) await logger.info('Starting game', { continueGame });
+ if (debugLogger) await debugLogger.startStep('startGame', { continueGame });
- if (logger) {
- await logger.timeAsync('Game Systems Initialization', async () => {
- await logger.info('Initializing game systems');
-
- if (debugLogger) await debugLogger.logStep('Initializing TextureManager');
- // Initialize texture manager first
- this.systems.textureManager = new TextureManager(this);
- if (logger) await logger.systemEvent('TextureManager', 'Initialized');
- if (debugLogger) await debugLogger.logStep('TextureManager initialized');
-
- if (debugLogger) await debugLogger.logStep('Initializing Player system');
- // Initialize in dependency order
- this.systems.player = new Player(this);
- if (logger) await logger.systemEvent('Player', 'Initialized');
- if (debugLogger) await debugLogger.logStep('Player system initialized', {
- playerLevel: this.systems.player.stats.level,
- playerExperience: this.systems.player.stats.experience
- });
-
- if (debugLogger) await debugLogger.logStep('Initializing Inventory system');
- this.systems.inventory = new Inventory(this);
- if (logger) await logger.systemEvent('Inventory', 'Initialized');
- if (debugLogger) await debugLogger.logStep('Inventory system initialized', {
- inventorySize: this.systems.inventory.items.length
- });
-
- if (debugLogger) await debugLogger.logStep('Initializing Economy system');
- this.systems.economy = new Economy(this);
- if (logger) await logger.systemEvent('Economy', 'Initialized');
- if (debugLogger) await debugLogger.logStep('Economy system initialized', {
- credits: this.systems.economy.credits,
- gems: this.systems.economy.gems
- });
-
- if (debugLogger) await debugLogger.logStep('Initializing IdleSystem');
- this.systems.idleSystem = new IdleSystem(this);
- if (logger) await logger.systemEvent('IdleSystem', 'Initialized');
- if (debugLogger) await debugLogger.logStep('IdleSystem initialized');
-
- if (debugLogger) await debugLogger.logStep('Initializing DungeonSystem');
- this.systems.dungeonSystem = new DungeonSystem(this);
- if (logger) await logger.systemEvent('DungeonSystem', 'Initialized');
- if (debugLogger) await debugLogger.logStep('DungeonSystem initialized');
-
- if (debugLogger) await debugLogger.logStep('Initializing SkillSystem');
- this.systems.skillSystem = new SkillSystem(this);
- if (logger) await logger.systemEvent('SkillSystem', 'Initialized');
- if (debugLogger) await debugLogger.logStep('SkillSystem initialized');
-
- if (debugLogger) await debugLogger.logStep('Initializing BaseSystem');
- this.systems.baseSystem = new BaseSystem(this);
- if (logger) await logger.systemEvent('BaseSystem', 'Initialized');
- if (debugLogger) await debugLogger.logStep('BaseSystem initialized');
-
- if (debugLogger) await debugLogger.logStep('Initializing QuestSystem');
- this.systems.questSystem = new QuestSystem(this);
- await this.systems.questSystem.initialize();
- if (logger) await logger.systemEvent('QuestSystem', 'Initialized');
- if (debugLogger) await debugLogger.logStep('QuestSystem initialized');
-
- if (debugLogger) await debugLogger.logStep('Initializing ShipSystem');
- this.systems.ship = new ShipSystem(this);
- if (logger) await logger.systemEvent('ShipSystem', 'Initialized');
- if (debugLogger) await debugLogger.logStep('ShipSystem initialized');
-
- if (debugLogger) await debugLogger.logStep('UIManager already initialized in initializeSystemsForLoad');
-
- if (debugLogger) await debugLogger.logStep('Initializing CraftingSystem');
- this.systems.crafting = new CraftingSystem(this);
- if (logger) await logger.systemEvent('CraftingSystem', 'Initialized');
- if (debugLogger) await debugLogger.logStep('CraftingSystem initialized');
-
- if (logger) await logger.info('All game systems initialized');
- if (debugLogger) await debugLogger.logStep('All systems created, running individual initializations');
-
- if (debugLogger) await debugLogger.logStep('Running individual system initializations');
- for (const [name, system] of Object.entries(this.systems)) {
- if (system.initialize) {
- if (debugLogger) await debugLogger.logStep(`Initializing ${name} system`);
- await system.initialize();
- if (debugLogger) await debugLogger.logStep(`${name} system initialization complete`);
- } else {
- if (debugLogger) await debugLogger.logStep(`${name} system has no initialize method`);
- }
- }
-
- if (debugLogger) await debugLogger.endStep('initializeSystems', {
- totalSystems: Object.keys(this.systems).length,
- systemsList: Object.keys(this.systems)
- });
- });
- } else {
- // Fallback without timing
- if (debugLogger) await debugLogger.logStep('Logger not available, using fallback initialization');
-
- // Initialize texture manager first
- this.systems.textureManager = new TextureManager(this);
-
- // Initialize in dependency order
- this.systems.player = new Player(this);
- this.systems.inventory = new Inventory(this);
- this.systems.economy = new Economy(this);
- this.systems.idleSystem = new IdleSystem(this);
- this.systems.dungeonSystem = new DungeonSystem(this);
- this.systems.skillSystem = new SkillSystem(this);
- this.systems.baseSystem = new BaseSystem(this);
- this.systems.questSystem = new QuestSystem(this);
- // UIManager already initialized in initializeSystemsForLoad
- this.systems.crafting = new CraftingSystem(this);
-
- for (const [name, system] of Object.entries(this.systems)) {
- if (system.initialize) {
- await system.initialize();
- }
+ try {
+ if (continueGame) {
+ console.log('[GAME ENGINE] Loading existing save data...');
+ if (debugLogger) await debugLogger.logStep('Loading existing save data');
+ await this.loadGame();
+ console.log('[GAME ENGINE] Save data loaded');
+ } else {
+ console.log('[GAME ENGINE] Creating new game...');
+ if (debugLogger) await debugLogger.logStep('Creating new game');
+ await this.newGame();
+ console.log('[GAME ENGINE] New game created');
}
- if (debugLogger) await debugLogger.endStep('initializeSystems', {
- fallbackMode: true,
- totalSystems: Object.keys(this.systems).length
- });
- }
- }
-
- async setupEventListeners() {
- const debugLogger = window.debugLogger;
-
- if (debugLogger) await debugLogger.startStep('setupEventListeners');
-
- // Window events
- if (debugLogger) await debugLogger.logStep('Setting up window event listeners');
- window.addEventListener('beforeunload', () => {
- if (debugLogger) debugLogger.logStep('beforeunload event triggered - saving game');
- this.save();
- });
-
- window.addEventListener('visibilitychange', () => {
- // if (debugLogger) debugLogger.logStep('visibilitychange event triggered', {
- // hidden: document.hidden,
- // visibilityState: document.visibilityState
- // });
- if (document.hidden) {
- // if (debugLogger) debugLogger.logStep('Document hidden - saving game');
- this.save();
- }
- });
-
- // Keyboard shortcuts
- if (debugLogger) await debugLogger.logStep('Setting up keyboard shortcuts');
- document.addEventListener('keydown', (event) => {
- if (debugLogger) debugLogger.logStep('Key pressed', {
- key: event.key,
- code: event.code,
- ctrlKey: event.ctrlKey,
- altKey: event.altKey,
- shiftKey: event.shiftKey
- });
+ // Start game loop
+ this.start();
+ console.log('[GAME ENGINE] Game loop started');
+ if (debugLogger) await debugLogger.logStep('Game loop started');
- // Ctrl+S for manual save
- if (event.ctrlKey && event.key === 's') {
- event.preventDefault();
- if (debugLogger) debugLogger.logStep('Manual save shortcut triggered (Ctrl+S)');
- this.save();
+ const loadingStatus = document.getElementById('loadingStatus');
+ if (loadingStatus) {
+ console.log('[GAME ENGINE] Hiding loading status text');
+ if (debugLogger) await debugLogger.logStep('Hiding loading status text');
+ loadingStatus.classList.add('hidden');
}
- });
-
- // Game loop events
- if (debugLogger) await debugLogger.logStep('Setting up game loop events');
- this.addEventListener('gameStarted', () => {
- if (debugLogger) debugLogger.logStep('Game started event received');
- });
-
- this.addEventListener('gameStopped', () => {
- if (debugLogger) debugLogger.logStep('Game stopped event received');
- });
-
- this.addEventListener('gameSaved', () => {
- // if (debugLogger) debugLogger.logStep('Game saved event received');
- });
-
- this.addEventListener('gameLoaded', () => {
- if (debugLogger) debugLogger.logStep('Game loaded event received');
- });
-
- // Setup UI event listeners
- if (debugLogger) await debugLogger.logStep('Setting up UI event listeners');
- if (this.systems.ui && typeof this.systems.ui.setupEventListeners === 'function' && !this.uiEventListenersSetup) {
- this.systems.ui.setupEventListeners();
- this.uiEventListenersSetup = true; // Prevent duplicate setup
- if (debugLogger) await debugLogger.logStep('UI event listeners setup complete');
- } else if (this.uiEventListenersSetup) {
- if (debugLogger) await debugLogger.logStep('UI event listeners already setup, skipping');
- }
-
- if (debugLogger) await debugLogger.endStep('setupEventListeners', {
- windowEvents: 2,
- keyboardShortcuts: 2,
- gameLoopEvents: 4
- });
- }
-
- start() {
- const debugLogger = window.debugLogger;
-
- if (this.isRunning) {
- if (debugLogger) debugLogger.log('GameEngine.start() called but game is already running', {
+
+ const gameInterface = document.getElementById('gameInterface');
+ if (gameInterface) {
+ console.log('[GAME ENGINE] Showing game interface');
+ if (debugLogger) await debugLogger.logStep('Showing game interface');
+ gameInterface.classList.remove('hidden');
+ } else {
+ console.warn('[GAME ENGINE] gameInterface element not found');
+ if (debugLogger) await debugLogger.warn('gameInterface element not found');
+ }
+
+ if (logger) await logger.info('Game started successfully');
+ if (debugLogger) await debugLogger.endStep('startGame', {
+ continueGame,
isRunning: this.isRunning,
gameTime: this.gameTime
});
- return;
- }
-
- if (debugLogger) debugLogger.logStep('Starting game engine', {
- gameLogicInterval: this.gameLogicInterval
- });
-
- this.isRunning = true;
- this.lastUpdate = performance.now();
-
- if (debugLogger) debugLogger.logStep('Game engine started, beginning game loop');
-
- this.gameLoop();
-
- // Loading screen is now handled by startGame() method
- // Don't duplicate the hiding logic here
-
- if (debugLogger) debugLogger.logStep('Game loop initiated');
- }
-
- async stop() {
- const debugLogger = window.debugLogger;
-
- if (debugLogger) await debugLogger.startStep('stopGame');
-
- console.log('[GAME ENGINE] Stopping game and saving...');
- if (debugLogger) await debugLogger.logStep('Stopping game engine');
-
- if (!this.isRunning) {
- if (debugLogger) debugLogger.logStep('Game already stopped, ignoring stop request');
- return;
- }
-
- this.isRunning = false;
-
- // Clear game logic timer
- if (this.gameLogicTimer) {
- clearInterval(this.gameLogicTimer);
- this.gameLogicTimer = null;
- }
-
- // Clear auto-save timer
- if (this.autoSaveTimer) {
- clearInterval(this.autoSaveTimer);
- this.autoSaveTimer = null;
- }
-
- console.log('[GAME ENGINE] Game stopped and saved successfully');
-
- if (debugLogger) await debugLogger.endStep('stopGame', {
- gameTime: this.gameTime,
- isRunning: this.isRunning
- });
- }
-
- startAutoSave() {
- const debugLogger = window.debugLogger;
-
- // Load saved interval or use default
- const savedInterval = localStorage.getItem('autoSaveInterval');
- this.autoSaveInterval = savedInterval ? parseInt(savedInterval) : 5;
-
- console.log(`[GAME ENGINE] Starting auto-save with ${this.autoSaveInterval} minute interval`);
- if (debugLogger) debugLogger.logStep('Starting auto-save system', {
- interval: this.autoSaveInterval,
- intervalMs: this.autoSaveInterval * 60 * 1000,
- savedInterval: savedInterval,
- isRunning: this.isRunning
- });
-
- // Clear any existing timer
- this.stopAutoSave();
-
- // Set up new timer
- this.autoSaveTimer = setInterval(async () => {
- if (debugLogger) debugLogger.logStep('Auto-save timer triggered', {
- isRunning: this.isRunning,
- paused: this.state.paused,
- gameTime: this.gameTime,
- lastAutoSave: this.lastAutoSave
- });
- console.log('[GAME ENGINE] Auto-save timer triggered - isRunning:', this.isRunning, 'paused:', this.state.paused);
-
- if (this.isRunning && !this.state.paused) {
- console.log('[GAME ENGINE] Auto-saving game...');
- if (debugLogger) debugLogger.logStep('Auto-saving game');
-
- try {
- await this.save();
- this.showNotification('Game auto-saved', 'info', 2000);
- console.log('[GAME ENGINE] Auto-save completed successfully');
-
- this.lastAutoSave = Date.now();
- if (debugLogger) debugLogger.logStep('Auto-save completed successfully', {
- lastAutoSave: this.lastAutoSave
- });
-
- } catch (error) {
- console.error('[GAME ENGINE] Auto-save failed:', error);
- if (debugLogger) await debugLogger.errorEvent(error, 'Auto-save');
- }
- } else {
- console.log('[GAME ENGINE] Auto-save skipped - game not running or paused');
- if (debugLogger) debugLogger.logStep('Auto-save skipped', {
- reason: this.isRunning ? 'paused' : 'not running',
- isRunning: this.isRunning,
- paused: this.state.paused
- });
- }
- }, this.autoSaveInterval * 60 * 1000); // Convert minutes to milliseconds
-
- this.lastAutoSave = Date.now();
- if (debugLogger) debugLogger.logStep('Auto-save timer started', {
- timerId: this.autoSaveTimer,
- lastAutoSave: this.lastAutoSave
- });
- }
-
- stopAutoSave() {
- const debugLogger = window.debugLogger;
-
- if (this.autoSaveTimer) {
- console.log('[GAME ENGINE] Stopping auto-save timer');
- if (debugLogger) debugLogger.logStep('Stopping auto-save timer', {
- timerId: this.autoSaveTimer,
- wasRunning: true
- });
-
- clearInterval(this.autoSaveTimer);
- this.autoSaveTimer = null;
-
- if (debugLogger) debugLogger.logStep('Auto-save timer stopped');
- } else {
- if (debugLogger) debugLogger.logStep('stopAutoSave called but no timer was active', {
- timerId: this.autoSaveTimer,
- wasRunning: false
- });
+ } catch (error) {
+ console.error('[GAME ENGINE] Failed to start game:', error);
+ if (logger) await logger.errorEvent(error, 'Game Start');
+ if (debugLogger) await debugLogger.errorEvent(error, 'Game Start');
}
}
- updateAutoSaveInterval(newInterval) {
- const debugLogger = window.debugLogger;
-
- console.log(`[GAME ENGINE] Updating auto-save interval to ${newInterval} minutes`);
- if (debugLogger) debugLogger.logStep('Updating auto-save interval', {
- oldInterval: this.autoSaveInterval,
- newInterval: newInterval,
- isRunning: this.isRunning
- });
-
- this.autoSaveInterval = newInterval;
-
- // Save to localStorage
- localStorage.setItem('autoSaveInterval', newInterval.toString());
-
- // Restart auto-save with new interval if game is running
- if (this.isRunning) {
- if (debugLogger) debugLogger.logStep('Restarting auto-save with new interval');
- this.startAutoSave();
- }
-
- if (debugLogger) debugLogger.logStep('Auto-save interval updated successfully', {
- currentInterval: this.autoSaveInterval,
- savedToStorage: true
- });
- }
-
start() {
const debugLogger = window.debugLogger;
@@ -731,8 +366,6 @@ class GameEngine extends EventTarget {
// Use fixed 1-second interval for all updates
const fixedDelta = 1000; // 1 second in milliseconds
- console.log('[GAME ENGINE] Game logic update called - adding', fixedDelta, 'ms to playtime');
-
if (this.state.paused) {
if (debugLogger) debugLogger.logStep('Game logic update called but game is paused', {
gameTime: this.gameTime,
@@ -743,27 +376,9 @@ class GameEngine extends EventTarget {
this.gameTime += fixedDelta;
- console.log('[GAME ENGINE] Total game time now:', this.gameTime, 'ms');
-
- // Emit UI update event instead of direct call
- this.emitUIUpdateEvent('full');
-
- // Log update performance every 300 frames (approximately 5 seconds)
- if (debugLogger && this.gameTime % 15000 === 0) { // Every 15 seconds of game time
- debugLogger.logStep('Game logic update cycle', {
- gameTime: this.gameTime,
- fixedDelta: fixedDelta,
- isRunning: this.isRunning,
- paused: this.state.paused,
- currentTab: this.state.currentTab
- });
- }
-
// Update player play time with fixed delta
if (this.systems.player && this.systems.player.updatePlayTime) {
- console.log('[GAME ENGINE] Before update - player playTime:', this.systems.player.stats.playTime);
this.systems.player.updatePlayTime(fixedDelta);
- console.log('[GAME ENGINE] After update - player playTime:', this.systems.player.stats.playTime);
}
// Update all systems with fixed delta
@@ -780,79 +395,57 @@ class GameEngine extends EventTarget {
// Update UI displays (money, gems, energy) after system updates
if (this.systems && this.systems.ui) {
- console.log('[GAME ENGINE] Updating UI displays');
try {
this.systems.ui.updateUI();
- console.log('[GAME ENGINE] UI update completed');
} catch (error) {
console.error('[GAME ENGINE] Error updating UI:', error);
}
}
+
+ // Emit game updated event
+ this.emit('gameUpdated', { gameTime: this.gameTime });
}
- update(deltaTime) {
- // Legacy method - game logic now handled by updateGameLogic()
- // This method kept for compatibility but doesn't process game systems
- }
-
- shouldUpdateGUI() {
- const currentTime = Date.now();
- if (currentTime - this.lastGUIUpdate >= this.guiUpdateInterval) {
- this.lastGUIUpdate = currentTime;
- return true;
- }
- return false;
- }
-
- updateGUI() {
+ startAutoSave() {
const debugLogger = window.debugLogger;
- console.log('[GAME ENGINE] Updating GUI');
+ // Load saved interval or use default
+ const savedInterval = localStorage.getItem('autoSaveInterval');
+ this.autoSaveInterval = savedInterval ? parseInt(savedInterval) : 5;
- // Update UI displays (money, gems, energy) after system updates
- if (this.systems && this.systems.ui) {
- console.log('[GAME ENGINE] Updating UI displays');
- try {
- this.systems.ui.updateUI();
- console.log('[GAME ENGINE] UI update completed');
- } catch (error) {
- console.error('[GAME ENGINE] Error updating UI:', error);
- }
- }
- }
-
- // Event system
- on(event, callback) {
- if (!this.eventListeners.has(event)) {
- this.eventListeners.set(event, []);
- }
- this.eventListeners.get(event).push(callback);
- }
-
- emit(event, data) {
- if (this.eventListeners.has(event)) {
- this.eventListeners.get(event).forEach(callback => {
+ console.log(`[GAME ENGINE] Starting auto-save with ${this.autoSaveInterval} minute interval`);
+
+ // Clear any existing timer
+ this.stopAutoSave();
+
+ // Set up new timer
+ this.autoSaveTimer = setInterval(async () => {
+ console.log('[GAME ENGINE] Auto-save timer triggered - isRunning:', this.isRunning, 'paused:', this.state.paused);
+
+ if (this.isRunning && !this.state.paused) {
+ console.log('[GAME ENGINE] Auto-saving game...');
+
try {
- callback(data);
+ await this.save();
+ this.showNotification('Game auto-saved', 'info', 2000);
+ console.log('[GAME ENGINE] Auto-save completed successfully');
+
} catch (error) {
- console.error(`[GAME ENGINE] Error in event listener for ${event}:`, error);
+ console.error('[GAME ENGINE] Auto-save failed:', error);
+ if (debugLogger) await debugLogger.errorEvent(error, 'Auto-save');
}
- });
- }
-
- // Also dispatch as DOM event for UIManager
- this.dispatchEvent(new CustomEvent(event, { detail: data }));
+ } else {
+ console.log('[GAME ENGINE] Auto-save skipped - game not running or paused');
+ }
+ }, this.autoSaveInterval * 60 * 1000); // Convert minutes to milliseconds
}
- emitUIUpdateEvent(type, data = {}) {
- const eventData = {
- type: type,
- timestamp: Date.now(),
- ...data
- };
-
- console.log('[GAME ENGINE] Emitting UI update event:', eventData);
- this.emit('uiUpdate', eventData);
+ stopAutoSave() {
+ if (this.autoSaveTimer) {
+ console.log('[GAME ENGINE] Stopping auto-save timer');
+ clearInterval(this.autoSaveTimer);
+ this.autoSaveTimer = null;
+ }
}
// Notification system
@@ -885,613 +478,27 @@ class GameEngine extends EventTarget {
this.state.notifications = this.state.notifications.filter(notification => notification.id !== id);
}
- processNotifications() {
- const now = Date.now();
- this.state.notifications = this.state.notifications.filter(
- notification => now - notification.timestamp < notification.duration
- );
+ // Event system
+ on(event, callback) {
+ if (!this.eventListeners.has(event)) {
+ this.eventListeners.set(event, []);
+ }
+ this.eventListeners.get(event).push(callback);
}
- // Save/Load system
- async save() {
- const logger = window.logger;
- const debugLogger = window.debugLogger;
-
- console.log('[GAME ENGINE] Save method called');
- if (logger) await logger.info('Saving game');
- // if (debugLogger) await debugLogger.startStep('saveGame');
-
- try {
- // if (debugLogger) await debugLogger.logStep('Collecting save data from systems');
- console.log('[GAME ENGINE] Collecting save data from systems...');
-
- const saveData = {
- player: this.systems.player.save(),
- inventory: this.systems.inventory.save(),
- economy: this.systems.economy.save(),
- idleSystem: this.systems.idleSystem.save(),
- dungeonSystem: this.systems.dungeonSystem.save(),
- skillSystem: this.systems.skillSystem.save(),
- baseSystem: this.systems.baseSystem.save(),
- questSystem: this.systems.questSystem.save(),
- gameTime: this.gameTime,
- lastSave: Date.now()
- };
-
- // if (debugLogger) await debugLogger.logStep('Save data collected', {
- // playerLevel: saveData.player?.stats?.level,
- // gameTime: this.gameTime,
- // saveDataSize: JSON.stringify(saveData).length,
- // });
-
- // console.log('[GAME ENGINE] Save data collected, player level:', saveData.player?.stats?.level);
- // console.log('[GAME ENGINE] Save slot info:', this.saveSlotInfo);
-
- // Check if we're in local mode and should use localStorage
- const isLocalMode = window.liveMainMenu && window.liveMainMenu.isLocalMode;
-
- if (isLocalMode) {
- console.log('[GAME ENGINE] Using localStorage for local mode save');
- if (debugLogger) await debugLogger.logStep('Saving via localStorage for local mode');
-
+ emit(event, data) {
+ if (this.eventListeners.has(event)) {
+ this.eventListeners.get(event).forEach(callback => {
try {
- const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`;
- const saveString = JSON.stringify(saveData);
-
- // Save to localStorage
- localStorage.setItem(saveKey, saveString);
-
- console.log('[GAME ENGINE] Game saved successfully to localStorage');
- console.log('[GAME ENGINE] Save key:', saveKey);
- console.log('[GAME ENGINE] Save data size:', saveString.length, 'characters');
- console.log('[GAME ENGINE] Current localStorage keys:', Object.keys(localStorage));
-
- // Verify save exists
- const verifySave = localStorage.getItem(saveKey);
- console.log('[GAME ENGINE] Save verification:', !!verifySave ? 'SUCCESS' : 'FAILED');
-
- if (debugLogger) await debugLogger.logStep('Game saved successfully to localStorage', {
- saveKey,
- saveDataSize: saveString.length
- });
-
- // Emit save event
- this.emit('gameSaved', { slot: this.saveSlotInfo.slot, saveData });
- // if (debugLogger) debugLogger.logStep('Game saved event emitted');
-
+ callback(data);
} catch (error) {
- console.error('[GAME ENGINE] Failed to save to localStorage:', error);
- if (debugLogger) await debugLogger.errorEvent(error, 'LocalStorage Save');
- throw error;
+ console.error(`[GAME ENGINE] Error in event listener for ${event}:`, error);
}
- } else {
- // Use file system if available, otherwise localStorage (original logic)
- if (this.saveSlotInfo && this.saveSlotInfo.useFileSystem) {
- if (window.electronAPI) {
- // console.log('[GAME ENGINE] Using Electron API to save game');
- // if (debugLogger) await debugLogger.logStep('Saving via Electron API', {
- // slot: this.saveSlotInfo.slot,
- // useFileSystem: true
- // });
-
- try {
- // Save through Electron API
- const result = await window.electronAPI.saveGame(this.saveSlotInfo.slot, saveData);
- if (result.success) {
- // console.log('[GAME ENGINE] Game saved successfully via Electron API');
- if (debugLogger) await debugLogger.logStep('Game saved successfully via Electron API', {
- slot: this.saveSlotInfo.slot,
- result: result
- });
-
- // Emit save event
- this.emit('gameSaved', { slot: this.saveSlotInfo.slot, saveData });
- // if (debugLogger) debugLogger.logStep('Game saved event emitted');
-
- } else {
- console.error('[GAME ENGINE] Failed to save via Electron API:', result.error);
- if (debugLogger) await debugLogger.errorEvent(new Error(result.error), 'Electron API Save');
-
- // Fallback to localStorage
- if (debugLogger) await debugLogger.logStep('Falling back to localStorage');
- const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`;
- localStorage.setItem(saveKey, JSON.stringify(saveData));
- console.log('[GAME ENGINE] Fallback: Game saved to localStorage with key:', saveKey);
- if (debugLogger) await debugLogger.logStep('Game saved to localStorage fallback', {
- saveKey,
- dataSize: JSON.stringify(saveData).length
- });
- }
- } catch (error) {
- console.error('[GAME ENGINE] Electron API save error:', error);
- if (debugLogger) await debugLogger.errorEvent(error, 'Electron API Save');
-
- // Fallback to localStorage
- if (debugLogger) await debugLogger.logStep('Falling back to localStorage due to error');
- const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`;
- localStorage.setItem(saveKey, JSON.stringify(saveData));
- console.log('[GAME ENGINE] Error fallback: Game saved to localStorage with key:', saveKey);
- }
- } else {
- console.warn('[GAME ENGINE] Electron API not available, using localStorage');
- if (debugLogger) await debugLogger.warn('Electron API not available, using localStorage');
-
- const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`;
- localStorage.setItem(saveKey, JSON.stringify(saveData));
- console.log('[GAME ENGINE] Game saved to localStorage with key:', saveKey);
- if (debugLogger) await debugLogger.logStep('Game saved to localStorage', {
- saveKey,
- dataSize: JSON.stringify(saveData).length
- });
- }
- } else {
- // LocalStorage fallback
- if (debugLogger) await debugLogger.logStep('Using localStorage save');
- const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`;
- localStorage.setItem(saveKey, JSON.stringify(saveData));
- console.log('[GAME ENGINE] Game saved to localStorage with key:', saveKey);
- if (debugLogger) await debugLogger.logStep('Game saved to localStorage', {
- saveKey,
- dataSize: JSON.stringify(saveData).length
- });
- }
- }
-
- if (logger) await logger.info('Game saved successfully');
- // if (debugLogger) await debugLogger.endStep('saveGame', {
- // gameTime: this.gameTime,
- // saveSlot: this.saveSlotInfo?.slot,
- // success: true
- // });
-
- } catch (error) {
- console.error('[GAME ENGINE] Save failed:', error);
- if (logger) await logger.errorEvent(error, 'Game Save');
- if (debugLogger) await debugLogger.errorEvent(error, 'Game Save');
- throw error;
+ });
}
- }
-
- async loadGame() {
- const logger = window.logger;
- const debugLogger = window.debugLogger;
- console.log('[GAME ENGINE] Load method called');
- if (logger) await logger.info('Loading game');
- if (debugLogger) await debugLogger.startStep('loadGame');
-
- try {
- if (debugLogger) await debugLogger.logStep('Loading save data', {
- saveSlot: this.saveSlotInfo?.slot,
- useFileSystem: this.saveSlotInfo?.useFileSystem
- });
-
- let saveData;
-
- // Use file system if available, otherwise localStorage
- if (this.saveSlotInfo && this.saveSlotInfo.useFileSystem && window.electronAPI) {
- console.log('[GAME ENGINE] Loading via Electron API');
- if (debugLogger) await debugLogger.logStep('Loading via Electron API');
-
- const result = await window.electronAPI.loadGame(this.saveSlotInfo.slot);
- if (result.success) {
- saveData = result.data;
- console.log('[GAME ENGINE] Game loaded successfully via Electron API');
- if (debugLogger) await debugLogger.logStep('Game loaded via Electron API', {
- slot: this.saveSlotInfo.slot,
- dataSize: JSON.stringify(saveData).length
- });
- } else {
- console.error('[GAME ENGINE] Failed to load via Electron API:', result.error);
- if (debugLogger) await debugLogger.errorEvent(new Error(result.error), 'Electron API Load');
- throw new Error(result.error);
- }
- } else {
- // LocalStorage fallback
- console.log('[GAME ENGINE] Loading from localStorage');
- if (debugLogger) await debugLogger.logStep('Loading from localStorage');
-
- const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`;
- const saveString = localStorage.getItem(saveKey);
-
- if (saveString) {
- try {
- saveData = JSON.parse(saveString);
- console.log('[GAME ENGINE] Game loaded from localStorage');
- if (debugLogger) await debugLogger.logStep('Game loaded from localStorage', {
- saveKey,
- dataSize: saveString.length
- });
- } catch (parseError) {
- console.error('[GAME ENGINE] Failed to parse save data:', parseError);
- if (debugLogger) await debugLogger.errorEvent(parseError, 'Parse Save Data');
- throw new Error('Corrupted save data');
- }
- } else {
- console.warn('[GAME ENGINE] No save data found in localStorage');
- if (debugLogger) await debugLogger.warn('No save data found in localStorage', { saveKey });
- throw new Error('No save data found');
- }
- }
-
- if (debugLogger) await debugLogger.logStep('Applying save data to systems');
-
- // Apply save data to systems
- if (saveData.player && this.systems.player) {
- console.log('[GAME ENGINE] Loading player data...');
- this.systems.player.load(saveData.player);
- if (debugLogger) await debugLogger.logStep('Player data loaded', {
- level: saveData.player?.stats?.level,
- experience: saveData.player?.stats?.experience
- });
- }
-
- if (saveData.inventory && this.systems.inventory) {
- console.log('[GAME ENGINE] Loading inventory data...');
- this.systems.inventory.load(saveData.inventory);
- if (debugLogger) await debugLogger.logStep('Inventory data loaded', {
- itemCount: saveData.inventory.items?.length || 0
- });
- }
-
- if (saveData.economy && this.systems.economy) {
- console.log('[GAME ENGINE] Loading economy data...');
- this.systems.economy.load(saveData.economy);
- if (debugLogger) await debugLogger.logStep('Economy data loaded', {
- credits: saveData.economy.credits,
- gems: saveData.economy.gems
- });
- }
-
- if (saveData.questSystem && this.systems.questSystem) {
- console.log('[GAME ENGINE] Loading quest data...');
- try {
- this.systems.questSystem.load(saveData.questSystem);
- if (debugLogger) await debugLogger.logStep('Quest data loaded', {
- mainQuestsCount: saveData.questSystem.mainQuests?.length || 0,
- dailyQuestsCount: saveData.questSystem.dailyQuests?.length || 0
- });
- console.log('[GAME ENGINE] Quest data loaded successfully');
- } catch (questError) {
- console.error('[GAME ENGINE] Failed to load quest data:', questError);
- if (debugLogger) await debugLogger.errorEvent(questError, 'Quest data load failed');
- // Continue with other systems instead of throwing
- }
- }
-
- if (saveData.baseSystem && this.systems.baseSystem) {
- console.log('[GAME ENGINE] Loading base data...');
- try {
- this.systems.baseSystem.load(saveData.baseSystem);
- if (debugLogger) await debugLogger.logStep('Base data loaded', {
- baseLevel: saveData.baseSystem.base?.level || 1,
- roomsCount: saveData.baseSystem.base?.rooms?.length || 0
- });
- console.log('[GAME ENGINE] Base data loaded successfully');
- } catch (baseError) {
- console.error('[GAME ENGINE] Failed to load base data:', baseError);
- if (debugLogger) await debugLogger.errorEvent(baseError, 'Base data load failed');
- }
- }
-
- if (saveData.skillSystem && this.systems.skillSystem) {
- console.log('[GAME ENGINE] Loading skill data...');
- try {
- this.systems.skillSystem.load(saveData.skillSystem);
- if (debugLogger) await debugLogger.logStep('Skill data loaded');
- console.log('[GAME ENGINE] Skill data loaded successfully');
- } catch (skillError) {
- console.error('[GAME ENGINE] Failed to load skill data:', skillError);
- if (debugLogger) await debugLogger.errorEvent(skillError, 'Skill data load failed');
- }
- }
-
- // CRITICAL: Initialize UIManager after loading save data
- console.log('[GAME ENGINE] About to initialize UIManager - checking systems...');
- console.log('[GAME ENGINE] Available systems:', Object.keys(this.systems));
- console.log('[GAME ENGINE] UIManager exists:', !!this.systems.ui);
-
- if (this.systems.ui) {
- console.log('[GAME ENGINE] Initializing UIManager after save load...');
- if (debugLogger) await debugLogger.logStep('Initializing UIManager after save load');
- try {
- await this.systems.ui.initialize();
- console.log('[GAME ENGINE] UIManager initialized successfully');
- if (debugLogger) await debugLogger.logStep('UIManager initialized successfully');
- } catch (uiError) {
- console.error('[GAME ENGINE] UIManager initialization failed:', uiError);
- if (debugLogger) await debugLogger.errorEvent(uiError, 'UIManager initialization failed');
- }
- } else {
- console.log('[GAME ENGINE] UIManager not found in systems!');
- }
-
- console.log('[GAME ENGINE] UIManager initialization section completed');
-
- // Initialize DungeonSystem after UIManager is ready
- if (this.systems.dungeonSystem) {
- console.log('[GAME ENGINE] Initializing DungeonSystem...');
- if (debugLogger) await debugLogger.logStep('Initializing DungeonSystem after save load');
- try {
- await this.systems.dungeonSystem.initialize();
- console.log('[GAME ENGINE] DungeonSystem initialized successfully');
- if (debugLogger) await debugLogger.logStep('DungeonSystem initialized successfully');
- } catch (dungeonError) {
- console.error('[GAME ENGINE] DungeonSystem initialization failed:', dungeonError);
- if (debugLogger) await debugLogger.errorEvent(dungeonError, 'DungeonSystem initialization failed');
- }
- } else {
- console.log('[GAME ENGINE] DungeonSystem not found in systems!');
- }
-
- // Restore game time
- if (saveData.gameTime !== undefined) {
- this.gameTime = saveData.gameTime;
- console.log('[GAME ENGINE] Game time restored:', this.gameTime);
- }
-
- console.log('[GAME ENGINE] loadGame method completed successfully');
-
- } catch (error) {
- console.error('[GAME ENGINE] Failed to load game:', error);
- if (logger) await logger.errorEvent(error, 'Game Load');
- if (debugLogger) await debugLogger.errorEvent(error, 'loadGame');
-
- if (debugLogger) await debugLogger.endStep('loadGame', {
- success: false,
- error: error.message
- });
-
- throw error;
- }
- }
-
- async load() {
- const logger = window.logger;
- const debugLogger = window.debugLogger;
-
- console.log('[GAME ENGINE] Load method called');
- if (logger) await logger.info('Loading game');
- if (debugLogger) await debugLogger.startStep('loadGame');
-
- try {
- if (debugLogger) await debugLogger.logStep('Loading save data', {
- saveSlot: this.saveSlotInfo?.slot,
- useFileSystem: this.saveSlotInfo?.useFileSystem
- });
-
- let saveData;
-
- // Use file system if available, otherwise localStorage
- if (this.saveSlotInfo && this.saveSlotInfo.useFileSystem && window.electronAPI) {
- console.log('[GAME ENGINE] Loading via Electron API');
- if (debugLogger) await debugLogger.logStep('Loading via Electron API');
-
- const result = await window.electronAPI.loadGame(this.saveSlotInfo.slot);
- if (result.success) {
- saveData = result.data;
- console.log('[GAME ENGINE] Game loaded successfully via Electron API');
- if (debugLogger) await debugLogger.logStep('Game loaded via Electron API', {
- slot: this.saveSlotInfo.slot,
- dataSize: JSON.stringify(saveData).length
- });
- } else {
- console.error('[GAME ENGINE] Failed to load via Electron API:', result.error);
- if (debugLogger) await debugLogger.errorEvent(new Error(result.error), 'Electron API Load');
- throw new Error(result.error);
- }
- } else {
- // LocalStorage fallback
- console.log('[GAME ENGINE] Loading from localStorage');
- if (debugLogger) await debugLogger.logStep('Loading from localStorage');
-
- const saveKey = `gso_save_slot_${this.saveSlotInfo.slot}`;
- const saveString = localStorage.getItem(saveKey);
-
- if (saveString) {
- try {
- saveData = JSON.parse(saveString);
- console.log('[GAME ENGINE] Game loaded from localStorage');
- if (debugLogger) await debugLogger.logStep('Game loaded from localStorage', {
- saveKey,
- dataSize: saveString.length
- });
- } catch (parseError) {
- console.error('[GAME ENGINE] Failed to parse save data:', parseError);
- if (debugLogger) await debugLogger.errorEvent(parseError, 'Parse Save Data');
- throw new Error('Corrupted save data');
- }
- } else {
- console.warn('[GAME ENGINE] No save data found in localStorage');
- if (debugLogger) await debugLogger.warn('No save data found in localStorage', { saveKey });
- throw new Error('No save data found');
- }
- }
-
- // if (debugLogger) await debugLogger.logStep('Applying save data to systems');
-
- // Apply save data to systems
- if (saveData.player && this.systems.player) {
- console.log('[GAME ENGINE] Loading player data...');
- this.systems.player.load(saveData.player);
- if (debugLogger) await debugLogger.logStep('Player data loaded', {
- level: saveData.player?.stats?.level,
- experience: saveData.player?.stats?.experience
- });
- }
-
- if (saveData.inventory && this.systems.inventory) {
- console.log('[GAME ENGINE] Loading inventory data...');
- this.systems.inventory.load(saveData.inventory);
- if (debugLogger) await debugLogger.logStep('Inventory data loaded', {
- itemCount: saveData.inventory.items?.length || 0
- });
- }
-
- if (saveData.economy && this.systems.economy) {
- console.log('[GAME ENGINE] Loading economy data...');
- this.systems.economy.load(saveData.economy);
- if (debugLogger) await debugLogger.logStep('Economy data loaded', {
- credits: saveData.economy.credits,
- gems: saveData.economy.gems
- });
- }
-
- if (saveData.gameTime) {
- this.gameTime = saveData.gameTime;
- if (debugLogger) await debugLogger.logStep('Game time restored', {
- gameTime: this.gameTime
- });
- }
-
- // Emit load event
- this.emit('gameLoaded', { saveData });
- if (debugLogger) debugLogger.logStep('Game loaded event emitted');
-
- if (logger) await logger.info('Game loaded successfully');
- if (debugLogger) await debugLogger.endStep('loadGame', {
- gameTime: this.gameTime,
- saveSlot: this.saveSlotInfo?.slot,
- success: true
- });
-
- } catch (error) {
- console.error('[GAME ENGINE] Load failed:', error);
- if (logger) await logger.errorEvent(error, 'Game Load');
- if (debugLogger) await debugLogger.errorEvent(error, 'Game Load');
- throw error;
- }
- }
-
- async newGame() {
- const logger = window.logger;
- const debugLogger = window.debugLogger;
-
- console.log('[GAME ENGINE] Starting new game initialization');
- if (logger) await logger.info('Starting new game');
- if (debugLogger) await debugLogger.startStep('newGame');
-
- try {
- // For new games, we need to properly initialize systems with default data
- if (debugLogger) await debugLogger.logStep('Initializing systems for new game');
-
- // Initialize inventory with starting items
- if (this.systems.inventory) {
- console.log('[GAME ENGINE] Initializing inventory with starting items');
- if (debugLogger) await debugLogger.logStep('Initializing inventory with starting items');
- await this.systems.inventory.initialize();
- }
-
- // Initialize DungeonSystem for new game
- if (this.systems.dungeonSystem) {
- console.log('[GAME ENGINE] Initializing DungeonSystem for new game...');
- if (debugLogger) await debugLogger.logStep('Initializing DungeonSystem for new game');
- try {
- await this.systems.dungeonSystem.initialize();
- console.log('[GAME ENGINE] DungeonSystem initialized successfully for new game');
- if (debugLogger) await debugLogger.logStep('DungeonSystem initialized successfully for new game');
- } catch (dungeonError) {
- console.error('[GAME ENGINE] DungeonSystem initialization failed for new game:', dungeonError);
- if (debugLogger) await debugLogger.errorEvent(dungeonError, 'DungeonSystem initialization failed for new game');
- }
- } else {
- console.log('[GAME ENGINE] DungeonSystem not found in systems!');
- }
-
- if (this.systems.player) {
- console.log('[GAME ENGINE] Resetting player to initial state');
- if (debugLogger) await debugLogger.logStep('Resetting player to initial state');
- this.systems.player.resetToLevel1();
- console.log('[GAME ENGINE] Player reset completed');
- if (debugLogger) await debugLogger.logStep('Player reset completed', {
- level: this.systems.player.stats.level,
- experience: this.systems.player.stats.experience,
- playTime: this.systems.player.stats.playTime
- });
-
- console.log('[GAME ENGINE] Setting up new player');
- if (debugLogger) await debugLogger.logStep('Setting up new player');
- this.systems.player.setupNewPlayer();
- console.log('[GAME ENGINE] Player setup completed');
- if (debugLogger) await debugLogger.logStep('Player setup completed', {
- level: this.systems.player.stats.level,
- experience: this.systems.player.stats.experience
- });
- } else {
- console.error('[GAME ENGINE] Player system not available');
- if (debugLogger) await debugLogger.error('Player system not available');
- }
-
- if (debugLogger) await debugLogger.logStep('Resetting economy system');
- console.log('[GAME ENGINE] Step 2: Resetting economy system');
-
- if (this.systems.economy) {
- console.log('[GAME ENGINE] Calling economy.reset()');
- if (debugLogger) await debugLogger.logStep('Calling economy.reset()');
- this.systems.economy.reset();
- console.log('[GAME ENGINE] Economy reset completed');
- if (debugLogger) await debugLogger.logStep('Economy reset completed', {
- credits: this.systems.economy.credits,
- gems: this.systems.economy.gems
- });
- } else {
- console.error('[GAME ENGINE] Economy system not available');
- if (debugLogger) await debugLogger.error('Economy system not available');
- }
-
- // Skip inventory reset - initialize() already handles proper setup
- if (debugLogger) await debugLogger.logStep('Skipping inventory reset - already initialized');
- console.log('[GAME ENGINE] Skipping inventory reset - already initialized with starting items');
-
- if (this.systems.inventory) {
- if (debugLogger) await debugLogger.logStep('Inventory already initialized', {
- itemCount: this.systems.inventory.items.length
- });
- } else {
- console.error('[GAME ENGINE] Inventory system not available');
- if (debugLogger) await debugLogger.error('Inventory system not available');
- }
-
- if (debugLogger) await debugLogger.logStep('Resetting quest system');
- console.log('[GAME ENGINE] Step 4: Resetting quest system');
-
- if (this.systems.questSystem) {
- console.log('[GAME ENGINE] Calling questSystem.reset()');
- if (debugLogger) await debugLogger.logStep('Calling questSystem.reset()');
- this.systems.questSystem.reset();
- console.log('[GAME ENGINE] Quest system reset completed');
- if (debugLogger) await debugLogger.logStep('Quest system reset completed');
-
- // Activate the tutorial quest for new games
- console.log('[GAME ENGINE] Activating tutorial quest for new game');
- if (debugLogger) await debugLogger.logStep('Activating tutorial quest');
- this.systems.questSystem.startQuest('tutorial_complete');
- console.log('[GAME ENGINE] Tutorial quest activated');
- if (debugLogger) await debugLogger.logStep('Tutorial quest activated');
- } else {
- console.error('[GAME ENGINE] Quest system not available');
- if (debugLogger) await debugLogger.error('Quest system not available');
- }
-
- if (debugLogger) await debugLogger.logStep('Resetting game time');
- console.log('[GAME ENGINE] Step 5: Resetting game time');
- this.gameTime = 0;
- console.log('[GAME ENGINE] Game time reset to 0');
- if (debugLogger) await debugLogger.logStep('Game time reset', { gameTime: this.gameTime });
-
- if (logger) await logger.info('New game initialized successfully');
- if (debugLogger) await debugLogger.endStep('newGame', {
- gameTime: this.gameTime,
- playerLevel: this.systems.player?.stats.level,
- success: true
- });
-
- } catch (error) {
- console.error('[GAME ENGINE] Failed to initialize new game:', error);
- if (logger) await logger.errorEvent(error, 'New Game Initialization');
- if (debugLogger) await debugLogger.errorEvent(error, 'New Game Initialization');
- throw error;
- }
+ // Also dispatch as DOM event for UIManager
+ this.dispatchEvent(new CustomEvent(event, { detail: data }));
}
// Utility methods
@@ -1516,14 +523,6 @@ class GameEngine extends EventTarget {
}
}
- toggleDebugConsole() {
- const debugLogger = window.debugLogger;
- // if (debugLogger) debugLogger.logStep('Toggle debug console requested');
-
- // Implementation would go here
- // console.log('[GAME ENGINE] Debug console toggle requested');
- }
-
getPerformanceStats() {
const debugLogger = window.debugLogger;
@@ -1543,10 +542,163 @@ class GameEngine extends EventTarget {
};
}
- // if (debugLogger) debugLogger.logStep('Performance stats requested', stats);
-
return stats;
}
+
+ // Load server player data (transforms server format to client format)
+ async loadServerPlayerData(playerData) {
+ console.log('[GAME ENGINE] Loading server player data with format transformation');
+ console.log('[GAME ENGINE] Original server data structure:', playerData);
+
+ // Transform server data format to client format
+ const transformedData = {
+ ...playerData,
+
+ // Transform quests from server format to client format
+ quests: playerData.quests ? {
+ mainQuests: playerData.quests.main || [],
+ dailyQuests: playerData.quests.daily || [],
+ weeklyQuests: playerData.quests.weekly || [],
+ tutorialQuests: playerData.quests.tutorial || [],
+ activeQuests: playerData.quests.active || [],
+ completedQuests: playerData.quests.completed || []
+ } : {
+ mainQuests: [],
+ dailyQuests: [],
+ weeklyQuests: [],
+ tutorialQuests: [],
+ activeQuests: [],
+ completedQuests: []
+ }
+ };
+
+ // DEBUG: Log quest data transformation
+ console.log('[GAME ENGINE] Quest data transformation:', {
+ serverQuests: playerData.quests,
+ transformedQuests: transformedData.quests,
+ mainQuestsCount: transformedData.quests.mainQuests.length,
+ dailyQuestsCount: transformedData.quests.dailyQuests.length,
+ weeklyQuestsCount: transformedData.quests.weeklyQuests.length,
+ tutorialQuestsCount: transformedData.quests.tutorialQuests.length
+ });
+
+ // Use crafting data from server or initialize empty
+ transformedData.crafting = playerData.crafting || {
+ skill: 1,
+ experience: 0,
+ knownRecipes: [],
+ completedDungeons: [],
+ currentInstance: null,
+ dungeonProgress: {}
+ };
+
+ return transformedData;
+ }
+
+ async loadPlayerData(playerData) {
+ console.log('[GAME ENGINE] Loading server player data');
+ console.log('[GAME ENGINE] Full playerData structure:', playerData);
+ console.log('[GAME ENGINE] PlayerData keys:', Object.keys(playerData));
+
+ try {
+ // Apply basic player stats
+ if (playerData.stats && this.systems && this.systems.player) {
+ console.log('[GAME ENGINE] Found player stats and player system, applying...');
+ console.log('[GAME ENGINE] Server playerData.stats:', playerData.stats);
+ console.log('[GAME ENGINE] Server playerData keys:', Object.keys(playerData));
+
+ // Check for playTime in different possible locations
+ const possiblePlayTimeFields = [
+ playerData.stats?.playTime,
+ playerData.playTime,
+ playerData.totalPlayTime,
+ playerData.stats?.totalPlayTime
+ ];
+
+ console.log('[GAME ENGINE] Possible playTime fields found:', possiblePlayTimeFields);
+
+ // Preserve existing playTime if server doesn't provide it
+ const existingPlayTime = this.systems.player.stats.playTime || 0;
+ console.log('[GAME ENGINE] Preserving existing playTime:', existingPlayTime);
+
+ this.systems.player.load(playerData.stats);
+ console.log('[GAME ENGINE] Applied player stats:', playerData.stats);
+
+ // Restore playTime if it was lost
+ if (!this.systems.player.stats.playTime || this.systems.player.stats.playTime === 0) {
+ this.systems.player.stats.playTime = existingPlayTime;
+ console.log('[GAME ENGINE] Restored playTime to:', existingPlayTime);
+ }
+
+ console.log('[GAME ENGINE] Final playTime after load:', this.systems.player.stats.playTime);
+
+ // Apply credits from server data to economy system
+ if (playerData.stats.credits !== undefined && this.systems.economy) {
+ this.systems.economy.credits = playerData.stats.credits;
+ console.log('[GAME ENGINE] Applied credits from server:', playerData.stats.credits);
+ }
+
+ // Apply gems from server data to economy system
+ if (playerData.stats.gems !== undefined && this.systems.economy) {
+ this.systems.economy.gems = playerData.stats.gems;
+ console.log('[GAME ENGINE] Applied gems from server:', playerData.stats.gems);
+ }
+
+ // Request fresh economy data from server to ensure sync
+ if (this.systems.economy && this.systems.economy.requestEconomyData) {
+ setTimeout(() => {
+ this.systems.economy.requestEconomyData();
+ }, 1000); // Delay to ensure socket is ready
+ }
+
+ // Apply energy from server data to player attributes
+ if (playerData.stats.currentEnergy !== undefined && this.systems.player.attributes) {
+ this.systems.player.attributes.currentEnergy = playerData.stats.currentEnergy;
+ console.log('[GAME ENGINE] Applied current energy from server:', playerData.stats.currentEnergy);
+ }
+
+ if (playerData.stats.maxEnergy !== undefined && this.systems.player.attributes) {
+ this.systems.player.attributes.maxEnergy = playerData.stats.maxEnergy;
+ console.log('[GAME ENGINE] Applied max energy from server:', playerData.stats.maxEnergy);
+ }
+
+ console.log('[GAME ENGINE] Final player stats after application:', this.systems.player.stats);
+ } else {
+ console.log('[GAME ENGINE] Missing player stats or player system');
+ console.log('[GAME ENGINE] - playerData.stats:', !!playerData.stats);
+ console.log('[GAME ENGINE] - this.systems:', !!this.systems);
+ console.log('[GAME ENGINE] - this.systems.player:', !!this.systems?.player);
+ }
+
+ // Apply inventory
+ if (playerData.inventory && this.systems && this.systems.inventory) {
+ this.systems.inventory.load(playerData.inventory);
+ console.log('[GAME ENGINE] Applied inventory');
+ }
+
+ // REMOVED: QuestSystem should be server-driven only
+ // Quest data will be handled by server-side systems only
+
+ // Show notification
+ if (this.showNotification) {
+ this.showNotification(`Welcome back! Level ${playerData.stats?.level || 1}`, 'success', 3000);
+ }
+
+ console.log('[GAME ENGINE] Server player data loaded successfully');
+
+ // Trigger UI update to refresh all tabs with new data
+ if (this.systems && this.systems.ui) {
+ this.systems.ui.updateUI();
+ console.log('[GAME ENGINE] Triggered UI update after server data load');
+ }
+
+ } catch (error) {
+ console.error('[GAME ENGINE] Error loading server player data:', error);
+ if (this.showNotification) {
+ this.showNotification('Failed to load server data!', 'error', 3000);
+ }
+ }
+ }
}
// Global game instance
diff --git a/Client/js/core/Inventory.js b/Client/js/core/Inventory.js
index 7804cfa..9a4dfe3 100644
--- a/Client/js/core/Inventory.js
+++ b/Client/js/core/Inventory.js
@@ -95,72 +95,24 @@ class Inventory {
maxSlots: this.maxSlots
});
- const startingItems = [
- {
- id: 'starter_blaster_common',
- name: 'Common Blaster',
- type: 'weapon',
- rarity: 'common',
- quantity: 1,
- stats: { attack: 5, criticalChance: 0.02 },
- description: 'A reliable basic blaster for new pilots',
- equipable: true,
- slot: 'weapon'
- },
- {
- id: 'basic_armor_common',
- name: 'Basic Armor',
- type: 'armor',
- rarity: 'common',
- quantity: 1,
- stats: { defense: 3 },
- description: 'Light armor providing basic protection',
- equipable: true,
- slot: 'armor'
- }
- ];
-
- if (debugLogger) debugLogger.logStep('Adding starting items', {
- startingItemCount: startingItems.length,
- startingItems: startingItems.map(item => ({
- id: item.id,
- name: item.name,
- type: item.type,
- quantity: item.quantity
- }))
- });
-
- startingItems.forEach(item => {
- console.log(`[DEBUG] Adding starting item: ${item.name}`);
- const result = this.addItem(item);
- console.log(`[DEBUG] Starting item add result: ${result}, inventory size: ${this.items.length}`);
- });
-
- // Equip starter items
- console.log('[INVENTORY] Equipping starter items');
- if (debugLogger) debugLogger.logStep('Equipping starter items');
-
- // Equip starter blaster
- const blasterItem = this.items.find(item => item.id === 'starter_blaster');
- if (blasterItem) {
- console.log('[INVENTORY] Equipping starter blaster');
- this.equipItem(blasterItem.id);
+ // In multiplayer mode, starting items should come from server
+ if (window.smartSaveManager?.isMultiplayer) {
+ console.log('[INVENTORY] Multiplayer mode - starting items will be provided by server');
+ if (debugLogger) debugLogger.logStep('Skipping starting items in multiplayer mode');
+ if (debugLogger) debugLogger.endStep('Inventory.addStartingItems', {
+ finalItemCount: this.items.length,
+ itemsAdded: 0
+ });
+ return;
}
- // Equip basic armor
- const armorItem = this.items.find(item => item.id === 'basic_armor');
- if (armorItem) {
- console.log('[INVENTORY] Equipping basic armor');
- this.equipItem(armorItem.id);
- }
-
- // Auto-stack starting items
- if (debugLogger) debugLogger.logStep('Auto-stacking starting items');
- this.autoStackItems();
+ // Singleplayer mode - no hardcoded starting items available
+ console.log('[INVENTORY] Singleplayer mode - no hardcoded starting items available');
+ if (debugLogger) debugLogger.logStep('No starting items available in singleplayer mode');
if (debugLogger) debugLogger.endStep('Inventory.addStartingItems', {
finalItemCount: this.items.length,
- itemsAdded: startingItems.length
+ itemsAdded: 0
});
}
diff --git a/Client/js/core/Logger.js b/Client/js/core/Logger.js
index 26b2221..e715594 100644
--- a/Client/js/core/Logger.js
+++ b/Client/js/core/Logger.js
@@ -213,12 +213,10 @@ class Logger {
async info(message, data = null) {
await this.log('info', message, data);
- console.info(`[INFO] ${message}`, data || '');
}
async debug(message, data = null) {
await this.log('debug', message, data);
- console.debug(`[DEBUG] ${message}`, data || '');
}
async gameEvent(eventType, details) {
diff --git a/Client/js/core/Player.js b/Client/js/core/Player.js
index c244c42..17056e4 100644
--- a/Client/js/core/Player.js
+++ b/Client/js/core/Player.js
@@ -537,9 +537,9 @@ class Player {
upgrades: []
};
- console.log('=== DEBUG: Character Reset ===');
- console.log('Player health reset to:', this.attributes.health, '/', this.attributes.maxHealth);
- console.log('Ship health reset to:', this.ship.health, '/', this.ship.maxHealth);
+ // console.log('=== DEBUG: Character Reset ===');
+ // console.log('Player health reset to:', this.attributes.health, '/', this.attributes.maxHealth);
+ // console.log('Ship health reset to:', this.ship.health, '/', this.ship.maxHealth);
// Reset skills
this.skills = {};
@@ -692,16 +692,43 @@ class Player {
}
updatePlayTime(deltaTime) {
+ // DISABLED: Reduce console spam for quest debugging
+ /*
console.log('[PLAYER] updatePlayTime called with deltaTime:', deltaTime, 'ms');
+ console.log('[PLAYER] Game state check:', {
+ hasGame: !!this.game,
+ isRunning: this.game?.isRunning,
+ isPaused: this.game?.state?.paused,
+ isHidden: document.hidden
+ });
+ */
+
+ // Only update playtime when game is actively running and not paused
+ if (!this.game || !this.game.isRunning || this.game.state.paused) {
+ // console.log('[PLAYER] Skipping playtime update - game not running or paused');
+ return;
+ }
+
+ // Also check if tab is visible (don't count time when tab is in background)
+ if (document.hidden) {
+ // console.log('[PLAYER] Skipping playtime update - tab hidden');
+ return;
+ }
+
+ // DISABLED: Reduce console spam for quest debugging
+ /*
console.log('[PLAYER] Before update - playTime:', this.stats.playTime, 'ms');
+ */
// Use real computer time delta
this.stats.playTime += deltaTime;
+ // DISABLED: Reduce console spam for quest debugging
+ /*
console.log('[PLAYER] After update - playTime:', this.stats.playTime, 'ms');
console.log('[PLAYER] PlayTime in seconds:', this.stats.playTime / 1000, 'seconds');
console.log('[PLAYER] PlayTime in minutes:', this.stats.playTime / 60000, 'minutes');
- console.log('[PLAYER] PlayTime in hours:', this.stats.playTime / 3600000, 'hours');
+ */
}
// UI updates
@@ -717,18 +744,25 @@ class Player {
// Update player info
const playerNameElement = document.getElementById('playerName');
+ const playerTitleElement = document.getElementById('playerTitle');
const playerLevelElement = document.getElementById('playerLevel');
if (playerNameElement) {
- playerNameElement.textContent = `${this.info.name} - ${this.info.title}`;
+ playerNameElement.textContent = this.info.name;
+ }
+
+ if (playerTitleElement) {
+ playerTitleElement.textContent = ` - ${this.info.title}`;
}
if (playerLevelElement) {
playerLevelElement.textContent = `Lv. ${this.stats.level}`;
}
- // Update health and energy
- if (this.game && this.game.systems && this.game.systems.ui) {
+ // Update health and energy only if in multiplayer mode or game is actively running
+ const shouldUpdateUI = window.smartSaveManager?.isMultiplayer || this.game?.isRunning;
+
+ if (shouldUpdateUI && this.game && this.game.systems && this.game.systems.ui) {
this.game.systems.ui.updateResourceDisplay();
}
@@ -765,6 +799,7 @@ class Player {
if (debugLogger) debugLogger.logStep('Player UI update completed', {
elementsUpdated: {
playerName: !!playerNameElement,
+ playerTitle: !!playerTitleElement,
playerLevel: !!playerLevelElement,
totalKills: !!totalKillsElement,
dungeonsCleared: !!dungeonsClearedElement,
@@ -817,9 +852,29 @@ class Player {
try {
if (data.stats) {
console.log('[PLAYER] Loading stats:', data.stats);
+ console.log('[PLAYER] Current playTime before load:', this.stats.playTime);
+ console.log('[PLAYER] Server playTime:', data.stats.playTime);
+
const oldStats = { ...this.stats };
- this.stats = { ...this.stats, ...data.stats };
+
+ // Preserve playTime if server doesn't provide it or provides 0
+ const existingPlayTime = this.stats.playTime || 0;
+ const serverPlayTime = data.stats.playTime || 0;
+
+ // Use server playTime if it's greater than existing, otherwise preserve existing
+ const preservedPlayTime = serverPlayTime > existingPlayTime ? serverPlayTime : existingPlayTime;
+
+ console.log('[PLAYER] Preserving playTime:', preservedPlayTime, '(existing:', existingPlayTime, ', server:', serverPlayTime, ')');
+
+ // Merge stats but preserve playTime
+ this.stats = {
+ ...this.stats,
+ ...data.stats,
+ playTime: preservedPlayTime // Force preserve playTime
+ };
+
console.log('[PLAYER] Level after stats load:', this.stats.level);
+ console.log('[PLAYER] PlayTime after stats load:', this.stats.playTime);
if (debugLogger) debugLogger.logStep('Player stats loaded', {
oldLevel: oldStats.level,
diff --git a/Client/js/main.js b/Client/js/main.js
index ec83f21..e9275fa 100644
--- a/Client/js/main.js
+++ b/Client/js/main.js
@@ -54,8 +54,9 @@ document.addEventListener('DOMContentLoaded', async () => {
window.debugLogger.startStep('domLoad');
window.debugLogger.logStep('DOM loaded, starting initialization');
- // Auto-start local server for singleplayer mode
- console.log('[MAIN] Checking local server status...');
+ // Auto-start local server for singleplayer mode (DISABLED to prevent auto-start after multiplayer disconnect)
+ console.log('[MAIN] Skipping local server auto-start to prevent conflicts with multiplayer mode');
+ /*
if (window.localServerManager) {
try {
const serverResult = await window.localServerManager.autoStartIfSingleplayer();
@@ -78,6 +79,7 @@ document.addEventListener('DOMContentLoaded', async () => {
} else {
console.warn('[MAIN] LocalServerManager not available');
}
+ */
// Title bar is already initialized, just log it
console.log('[MAIN] DOM loaded - title bar should already be working');
@@ -368,7 +370,7 @@ if (window.performance && window.performance.memory) {
setInterval(() => {
if (window.game && window.game.isRunning) {
const stats = window.game.getPerformanceStats();
- if (stats.memory && stats.memory.used / stats.memory.total > 0.9) {
+ if (stats.memory && stats.memory.used / stats.memory.limit > 0.8) {
console.warn('High memory usage detected:', stats.memory);
}
}
diff --git a/Client/js/systems/BaseSystem.js b/Client/js/systems/BaseSystem.js
index 391e9f3..b5acea3 100644
--- a/Client/js/systems/BaseSystem.js
+++ b/Client/js/systems/BaseSystem.js
@@ -1309,7 +1309,11 @@ class BaseSystem {
});
}
- player.updateUI();
+ // Only update player UI if in multiplayer mode or game is actively running
+ if (this.game.shouldUpdateGUI()) {
+ player.updateUI();
+ }
+
this.updateShipGallery();
// Also update ShipSystem display
@@ -1592,10 +1596,12 @@ class BaseSystem {
// Apply benefits
this.applyStarbaseBenefits(newStarbase);
- // Update UI
- economy.updateUI();
- this.updateStarbaseList();
- this.updateStarbasePurchaseList();
+ // Only update UI if in multiplayer mode or game is actively running
+ if (this.game.shouldUpdateGUI()) {
+ economy.updateUI();
+ this.updateStarbaseList();
+ this.updateStarbasePurchaseList();
+ }
this.game.showNotification(`Purchased ${starbaseTemplate.name}!`, 'success', 4000);
}
@@ -1650,11 +1656,18 @@ class BaseSystem {
}
startMiningProduction(starbase) {
- // Set up passive credit generation
- if (!this.miningInterval) {
+ // Only start mining if in multiplayer mode or game is actively running
+ const shouldStartMining = window.smartSaveManager?.isMultiplayer || this.game?.isRunning;
+
+ if (shouldStartMining && !this.miningInterval) {
+ // Set up passive credit generation
this.miningInterval = setInterval(() => {
this.game.systems.economy.addCredits(500, 'mining_outpost');
}, 3600000); // 1 hour
+
+ console.log('[BASE SYSTEM] Mining production started');
+ } else if (!shouldStartMining) {
+ console.log('[BASE SYSTEM] Skipping mining production - not in multiplayer mode');
}
}
@@ -2057,10 +2070,12 @@ showRoomBuildMenu(x, y) {
console.log('[DEBUG] Player ship updated:', player.ship);
console.log('[DEBUG] Player attributes updated:', player.attributes);
- // Update UI displays
- player.updateUI();
- if (this.game.systems.ship && this.game.systems.ship.updateCurrentShipDisplay) {
- this.game.systems.ship.updateCurrentShipDisplay();
+ // Only update UI displays if in multiplayer mode or game is actively running
+ if (this.game.shouldUpdateGUI()) {
+ player.updateUI();
+ if (this.game.systems.ship && this.game.systems.ship.updateCurrentShipDisplay) {
+ this.game.systems.ship.updateCurrentShipDisplay();
+ }
}
}
}
diff --git a/Client/js/systems/CraftingSystem.js b/Client/js/systems/CraftingSystem.js
index 0e4843a..a21618e 100644
--- a/Client/js/systems/CraftingSystem.js
+++ b/Client/js/systems/CraftingSystem.js
@@ -641,7 +641,13 @@ class CraftingSystem extends BaseSystem {
switchCategory(category) {
this.currentCategory = category;
this.selectedRecipe = null;
- this.updateUI();
+
+ // Update UI only if in multiplayer mode or game is actively running
+ const shouldUpdateUI = window.smartSaveManager?.isMultiplayer || this.game?.isRunning;
+
+ if (shouldUpdateUI) {
+ this.updateUI();
+ }
}
}
diff --git a/Client/js/systems/DungeonSystem.js b/Client/js/systems/DungeonSystem.js
index 2bce1dc..6f4bddd 100644
--- a/Client/js/systems/DungeonSystem.js
+++ b/Client/js/systems/DungeonSystem.js
@@ -1,1977 +1,462 @@
/**
- * Galaxy Strike Online - Dungeon System
- * Manages procedural dungeon generation and exploration
+ * Galaxy Strike Online - Client Dungeon System
+ * Server-driven dungeon management client
*/
class DungeonSystem {
constructor(gameEngine) {
this.game = gameEngine;
- // Dungeon configuration
- this.dungeonTypes = {
- tutorial: {
- name: 'Tutorial Dungeon',
- description: 'Learn the basics of dungeon exploration in this guided tutorial',
- difficulty: 'tutorial',
- enemyTypes: ['training_drone', 'practice_target'],
- rewardMultiplier: 0.5,
- oneTimeOnly: true,
- healthType: 'player', // Ground mission
- energyCost: 0
- },
- alien_ruins: {
- name: 'Alien Ruins',
- description: 'Ancient alien structures filled with mysterious technology',
- difficulty: 'medium',
- enemyTypes: ['alien_guardian', 'ancient_drone', 'crystal_golem'],
- rewardMultiplier: 1.2,
- healthType: 'player', // Ground mission
- energyCost: 20
- },
- pirate_lair: {
- name: 'Pirate Lair',
- description: 'Dangerous pirate hideouts with valuable loot',
- difficulty: 'easy',
- enemyTypes: ['space_pirate', 'pirate_captain', 'defense_turret'],
- rewardMultiplier: 1.0,
- healthType: 'ship', // Space mission
- energyCost: 15
- },
- corrupted_vault: {
- name: 'Corrupted AI Vault',
- description: ' malfunctioning AI facilities with corrupted security',
- difficulty: 'hard',
- enemyTypes: ['security_drone', 'corrupted_ai', 'virus_program'],
- rewardMultiplier: 1.5,
- healthType: 'ship', // Space mission
- energyCost: 25
- },
- asteroid_mine: {
- name: 'Asteroid Mine',
- description: 'Abandoned mining facilities in asteroid fields',
- difficulty: 'easy',
- enemyTypes: ['mining_drone', 'rock_creature', 'explosive_asteroid'],
- rewardMultiplier: 0.8,
- healthType: 'ship', // Space mission
- energyCost: 10
- },
- nebula_anomaly: {
- name: 'Nebula Anomaly',
- description: 'Strange energy anomalies in deep space',
- difficulty: 'extreme',
- enemyTypes: ['energy_being', 'phase_shifter', 'quantum_entity'],
- rewardMultiplier: 2.0,
- healthType: 'ship', // Space mission
- energyCost: 30
- },
-
- // NEW DUNGEONS - Space Theme
- space_station: {
- name: 'Abandoned Space Station',
- description: 'A derelict space station floating in the void',
- difficulty: 'medium',
- enemyTypes: ['maintenance_drone', 'security_android', 'station_ai'],
- rewardMultiplier: 1.3,
- healthType: 'player',
- energyCost: 22
- },
- comet_core: {
- name: 'Comet Core',
- description: 'The frozen heart of a passing comet',
- difficulty: 'hard',
- enemyTypes: ['ice_elemental', 'frost_wyrm', 'crystal_guardian'],
- rewardMultiplier: 1.6,
- healthType: 'ship',
- energyCost: 28
- },
- black_hole_perimeter: {
- name: 'Black Hole Perimeter',
- description: 'Dangerous space near a black hole event horizon',
- difficulty: 'extreme',
- enemyTypes: ['gravity_wraith', 'void_stalker', 'singularity_spawn'],
- rewardMultiplier: 2.5,
- healthType: 'ship',
- energyCost: 35
- },
- star_forge: {
- name: 'Star Forge',
- description: 'Ancient facility that harnesses stellar energy',
- difficulty: 'hard',
- enemyTypes: ['plasma_elemental', 'solar_guardian', 'fusion_core'],
- rewardMultiplier: 1.8,
- healthType: 'ship',
- energyCost: 30
- },
- debris_field: {
- name: 'Ship Debris Field',
- description: 'Graveyard of destroyed spacecraft',
- difficulty: 'easy',
- enemyTypes: ['scrap_golem', 'hull_breacher', 'salage_drone'],
- rewardMultiplier: 0.9,
- healthType: 'ship',
- energyCost: 12
- },
-
- // NEW DUNGEONS - Planet Theme
- jungle_temple: {
- name: 'Jungle Temple',
- description: 'Overgrown temple hidden in dense alien jungle',
- difficulty: 'medium',
- enemyTypes: ['plant_beast', 'tribal_warrior', 'jungle_spirit'],
- rewardMultiplier: 1.4,
- healthType: 'player',
- energyCost: 25
- },
- desert_pyramid: {
- name: 'Desert Pyramid',
- description: 'Ancient pyramid buried under endless sand dunes',
- difficulty: 'hard',
- enemyTypes: ['sand_worm', 'mummy_guardian', 'heat_elemental'],
- rewardMultiplier: 1.7,
- healthType: 'player',
- energyCost: 28
- },
- volcanic_caverns: {
- name: 'Volcanic Caverns',
- description: 'Molten caverns deep within an active volcano',
- difficulty: 'hard',
- enemyTypes: ['lava_elemental', 'fire_demon', 'magma_beast'],
- rewardMultiplier: 1.6,
- healthType: 'player',
- energyCost: 26
- },
- arctic_research: {
- name: 'Arctic Research Base',
- description: 'Frozen research facility with failed experiments',
- difficulty: 'medium',
- enemyTypes: ['cryo_mutant', 'frost_android', 'ice_wraith'],
- rewardMultiplier: 1.5,
- healthType: 'player',
- energyCost: 24
- },
- swamp_lair: {
- name: 'Swamp Lair',
- description: 'Murky swamp inhabited by strange creatures',
- difficulty: 'easy',
- enemyTypes: ['swamp_beast', 'toxic_spitter', 'mud_golem'],
- rewardMultiplier: 1.1,
- healthType: 'player',
- energyCost: 18
- },
-
- // NEW DUNGEONS - Technology Theme
- cyber_realm: {
- name: 'Cyber Realm',
- description: 'Virtual reality space corrupted by malware',
- difficulty: 'hard',
- enemyTypes: ['glitch_wraith', 'firewall_guardian', 'data_vampire'],
- rewardMultiplier: 1.9,
- healthType: 'player',
- energyCost: 32
- },
- robot_factory: {
- name: 'Robot Factory',
- description: 'Automated factory producing hostile machines',
- difficulty: 'medium',
- enemyTypes: ['assembly_drone', 'welder_bot', 'factory_overseer'],
- rewardMultiplier: 1.4,
- healthType: 'player',
- energyCost: 23
- },
- quantum_lab: {
- name: 'Quantum Laboratory',
- description: 'Research facility experimenting with quantum physics',
- difficulty: 'extreme',
- enemyTypes: ['quantum_phantom', 'particle_accelerator', 'reality_bender'],
- rewardMultiplier: 2.3,
- healthType: 'player',
- energyCost: 38
- },
- server_farm: {
- name: 'Server Farm',
- description: 'Massive data center with rogue security programs',
- difficulty: 'medium',
- enemyTypes: ['sentinel_program', 'data_corruptor', 'system_guardian'],
- rewardMultiplier: 1.3,
- healthType: 'player',
- energyCost: 21
- },
-
- // NEW DUNGEONS - Biome/Elemental Theme
- crystal_caves: {
- name: 'Crystal Caves',
- description: 'Caves filled with energy-infused crystals',
- difficulty: 'medium',
- enemyTypes: ['crystal_golem', 'shard_elemental', 'resonance_beast'],
- rewardMultiplier: 1.4,
- healthType: 'player',
- energyCost: 22
- },
- toxic_wastes: {
- name: 'Toxic Wastes',
- description: 'Polluted wasteland filled with mutated creatures',
- difficulty: 'hard',
- enemyTypes: ['mutant_horror', 'toxic_slime', 'radiation_beast'],
- rewardMultiplier: 1.7,
- healthType: 'player',
- energyCost: 27
- },
- shadow_realm: {
- name: 'Shadow Realm',
- description: 'Dark dimension inhabited by shadow creatures',
- difficulty: 'extreme',
- enemyTypes: ['shadow_demon', 'nightmare_stalker', 'void_walker'],
- rewardMultiplier: 2.2,
- healthType: 'player',
- energyCost: 36
- },
- time_anomaly: {
- name: 'Time Anomaly',
- description: 'Area where time flows unpredictably',
- difficulty: 'extreme',
- enemyTypes: ['temporal_paradox', 'future_soldier', 'past_guardian'],
- rewardMultiplier: 2.4,
- healthType: 'player',
- energyCost: 40
- },
-
- // NEW DUNGEONS - Military/War Theme
- war_zone: {
- name: 'Active War Zone',
- description: 'Battlefield with ongoing combat operations',
- difficulty: 'hard',
- enemyTypes: ['enemy_soldier', 'combat_drone', 'field_commander'],
- rewardMultiplier: 1.8,
- healthType: 'player',
- energyCost: 29
- },
- military_base: {
- name: 'Abandoned Military Base',
- description: 'Former military installation with automated defenses',
- difficulty: 'medium',
- enemyTypes: ['turret_system', 'combat_android', 'base_commander'],
- rewardMultiplier: 1.5,
- healthType: 'player',
- energyCost: 24
- },
- weapons_testing: {
- name: 'Weapons Testing Facility',
- description: 'Secret facility testing advanced weaponry',
- difficulty: 'hard',
- enemyTypes: ['weapon_drone', 'test_subject', 'chief_scientist'],
- rewardMultiplier: 1.9,
- healthType: 'player',
- energyCost: 31
- },
-
- // NEW DUNGEONS - Special/Unique Theme
- dream_scape: {
- name: 'Dream Scape',
- description: 'Surreal landscape shaped by collective dreams',
- difficulty: 'medium',
- enemyTypes: ['nightmare_creature', 'dream_guardian', 'subconscious_demon'],
- rewardMultiplier: 1.6,
- healthType: 'player',
- energyCost: 26
- },
- memory_palace: {
- name: 'Memory Palace',
- description: 'Mental realm storing forgotten memories',
- difficulty: 'hard',
- enemyTypes: ['memory_fragment', 'forgetfulness_demon', 'nostalgia_spirit'],
- rewardMultiplier: 1.7,
- healthType: 'player',
- energyCost: 28
- },
- dimension_rift: {
- name: 'Dimension Rift',
- description: 'Tear between dimensions with interdimensional invaders',
- difficulty: 'extreme',
- enemyTypes: ['rift_demon', 'dimensional_hunter', 'reality_tear'],
- rewardMultiplier: 2.6,
- healthType: 'player',
- energyCost: 42
- }
- };
-
- // Current dungeon state
+ // Current dungeon state (runtime only)
this.currentDungeon = null;
this.currentRoom = null;
this.dungeonProgress = 0;
this.isExploring = false;
- // Dungeon templates
- this.roomTypes = {
- entrance: { name: 'Entrance', enemies: 0, rewards: false },
- corridor: { name: 'Corridor', enemies: 1, rewards: false },
- chamber: { name: 'Chamber', enemies: 2, rewards: true },
- treasure: { name: 'Treasure Room', enemies: 0, rewards: true },
- boss: { name: 'Boss Room', enemies: 1, rewards: true, isBoss: true },
- exit: { name: 'Exit', enemies: 0, rewards: false }
- };
+ // Server data loaded from server
+ this.serverDungeons = [];
+ this.roomTypes = {};
+ this.enemyTemplates = {};
- // Enemy templates
- this.enemyTemplates = {
- // Original enemies
- training_drone: {
- name: 'Training Drone',
- health: 10,
- attack: 5,
- defense: 2,
- experience: 5,
- credits: 3
- },
- practice_target: {
- name: 'Practice Target',
- health: 5,
- attack: 0,
- defense: 0,
- experience: 2,
- credits: 1
- },
- alien_guardian: {
- name: 'Alien Guardian',
- health: 50,
- attack: 8,
- defense: 5,
- experience: 25,
- credits: 15
- },
- ancient_drone: {
- name: 'Ancient Drone',
- health: 30,
- attack: 12,
- defense: 2,
- experience: 20,
- credits: 10
- },
- crystal_golem: {
- name: 'Crystal Golem',
- health: 80,
- attack: 6,
- defense: 10,
- experience: 35,
- credits: 25
- },
- space_pirate: {
- name: 'Space Pirate',
- health: 25,
- attack: 10,
- defense: 3,
- experience: 15,
- credits: 12
- },
- pirate_captain: {
- name: 'Pirate Captain',
- health: 40,
- attack: 15,
- defense: 6,
- experience: 30,
- credits: 20
- },
- defense_turret: {
- name: 'Defense Turret',
- health: 20,
- attack: 18,
- defense: 8,
- experience: 18,
- credits: 8
- },
- security_drone: {
- name: 'Security Drone',
- health: 35,
- attack: 14,
- defense: 4,
- experience: 22,
- credits: 15
- },
- corrupted_ai: {
- name: 'Corrupted AI',
- health: 60,
- attack: 20,
- defense: 2,
- experience: 40,
- credits: 30
- },
- virus_program: {
- name: 'Virus Program',
- health: 15,
- attack: 25,
- defense: 1,
- experience: 20,
- credits: 12
- },
- mining_drone: {
- name: 'Mining Drone',
- health: 20,
- attack: 8,
- defense: 3,
- experience: 12,
- credits: 8
- },
- rock_creature: {
- name: 'Rock Creature',
- health: 45,
- attack: 6,
- defense: 12,
- experience: 25,
- credits: 15
- },
- explosive_asteroid: {
- name: 'Explosive Asteroid',
- health: 10,
- attack: 30,
- defense: 0,
- experience: 15,
- credits: 5
- },
- energy_being: {
- name: 'Energy Being',
- health: 55,
- attack: 22,
- defense: 3,
- experience: 45,
- credits: 35
- },
- phase_shifter: {
- name: 'Phase Shifter',
- health: 30,
- attack: 28,
- defense: 1,
- experience: 35,
- credits: 25
- },
- quantum_entity: {
- name: 'Quantum Entity',
- health: 70,
- attack: 35,
- defense: 5,
- experience: 60,
- credits: 50
- },
+ console.log('[DUNGEON SYSTEM] Client DungeonSystem initialized - server-driven mode');
+
+ // Set up socket event listeners
+ this.setupSocketListeners();
+ }
+
+ /**
+ * Set up Socket.IO event listeners for dungeon data
+ */
+ setupSocketListeners() {
+ if (!this.game.socket) {
+ console.warn('[DUNGEON SYSTEM] No socket available for event listeners');
+ return;
+ }
+
+ // Listen for dungeon data response
+ this.game.socket.on('dungeons_data', (data) => {
+ console.log('[DUNGEON SYSTEM] Received dungeons data:', data);
+ this.serverDungeons = data.dungeons || data;
+ console.log('[DUNGEON SYSTEM] Loaded grouped dungeons from server:', Object.keys(this.serverDungeons));
+ // Update UI when data is loaded
+ this.generateDungeonList();
+ });
+
+ // Listen for room types response
+ this.game.socket.on('room_types_data', (data) => {
+ console.log('[DUNGEON SYSTEM] Received room types data:', data);
+ this.roomTypes = data;
+ console.log('[DUNGEON SYSTEM] Loaded room types from server');
+ });
+
+ // Listen for enemy templates response
+ this.game.socket.on('enemy_templates_data', (data) => {
+ console.log('[DUNGEON SYSTEM] Received enemy templates data:', data);
+ this.enemyTemplates = data;
+ console.log(`[DUNGEON SYSTEM] Loaded ${Object.keys(this.enemyTemplates).length} enemy templates from server`);
+ // Update UI when enemy data is loaded
+ this.generateDungeonList();
+ });
+
+ // Listen for dungeon start response
+ this.game.socket.on('dungeon_started', (data) => {
+ console.log('[DUNGEON SYSTEM] Dungeon started:', data);
+ this.currentDungeon = data.instance;
+ this.isExploring = true;
+ this.dungeonProgress = 0;
+ });
+
+ // Listen for encounter response
+ this.game.socket.on('encounter_data', (data) => {
+ console.log('[DUNGEON SYSTEM] Encounter received:', data);
+ this.currentRoom = data.encounter;
+ this.dungeonProgress++;
+ });
+
+ // Listen for dungeon completion response
+ this.game.socket.on('dungeon_completed', (data) => {
+ console.log('[DUNGEON SYSTEM] Dungeon completed:', data);
+ // Reset dungeon state
+ this.currentDungeon = null;
+ this.currentRoom = null;
+ this.isExploring = false;
+ this.dungeonProgress = 0;
+ });
+
+ // Listen for dungeon status response
+ this.game.socket.on('dungeon_status', (data) => {
+ console.log('[DUNGEON SYSTEM] Dungeon status received:', data);
+ });
+
+ console.log('[DUNGEON SYSTEM] Socket event listeners set up');
+ }
+
+ /**
+ * Load dungeon data from server using Socket.IO packets
+ */
+ async loadServerData() {
+ try {
+ console.log('[DUNGEON SYSTEM] Loading dungeon data from server via packets...');
- // NEW ENEMIES - Space Theme
- maintenance_drone: {
- name: 'Maintenance Drone',
- health: 28,
- attack: 11,
- defense: 4,
- experience: 18,
- credits: 12
- },
- security_android: {
- name: 'Security Android',
- health: 42,
- attack: 16,
- defense: 7,
- experience: 28,
- credits: 20
- },
- station_ai: {
- name: 'Station AI',
- health: 65,
- attack: 24,
- defense: 3,
- experience: 45,
- credits: 32
- },
- ice_elemental: {
- name: 'Ice Elemental',
- health: 38,
- attack: 18,
- defense: 8,
- experience: 32,
- credits: 24
- },
- frost_wyrm: {
- name: 'Frost Wyrm',
- health: 72,
- attack: 26,
- defense: 6,
- experience: 52,
- credits: 38
- },
- gravity_wraith: {
- name: 'Gravity Wraith',
- health: 85,
- attack: 32,
- defense: 4,
- experience: 68,
- credits: 48
- },
- void_stalker: {
- name: 'Void Stalker',
- health: 78,
- attack: 38,
- defense: 5,
- experience: 72,
- credits: 52
- },
- singularity_spawn: {
- name: 'Singularity Spawn',
- health: 95,
- attack: 42,
- defense: 8,
- experience: 85,
- credits: 65
- },
- plasma_elemental: {
- name: 'Plasma Elemental',
- health: 58,
- attack: 28,
- defense: 4,
- experience: 48,
- credits: 35
- },
- solar_guardian: {
- name: 'Solar Guardian',
- health: 82,
- attack: 34,
- defense: 7,
- experience: 65,
- credits: 48
- },
- fusion_core: {
- name: 'Fusion Core',
- health: 68,
- attack: 30,
- defense: 12,
- experience: 58,
- credits: 42
- },
- scrap_golem: {
- name: 'Scrap Golem',
- health: 35,
- attack: 14,
- defense: 9,
- experience: 22,
- credits: 16
- },
- hull_breacher: {
- name: 'Hull Breacher',
- health: 32,
- attack: 20,
- defense: 3,
- experience: 26,
- credits: 18
- },
- salage_drone: {
- name: 'Salvage Drone',
- health: 22,
- attack: 12,
- defense: 5,
- experience: 16,
- credits: 11
- },
-
- // NEW ENEMIES - Planet Theme
- plant_beast: {
- name: 'Plant Beast',
- health: 48,
- attack: 15,
- defense: 8,
- experience: 35,
- credits: 26
- },
- tribal_warrior: {
- name: 'Tribal Warrior',
- health: 38,
- attack: 18,
- defense: 6,
- experience: 28,
- credits: 20
- },
- jungle_spirit: {
- name: 'Jungle Spirit',
- health: 55,
- attack: 22,
- defense: 4,
- experience: 42,
- credits: 30
- },
- sand_worm: {
- name: 'Sand Worm',
- health: 75,
- attack: 28,
- defense: 9,
- experience: 58,
- credits: 42
- },
- mummy_guardian: {
- name: 'Mummy Guardian',
- health: 62,
- attack: 24,
- defense: 7,
- experience: 48,
- credits: 35
- },
- heat_elemental: {
- name: 'Heat Elemental',
- health: 52,
- attack: 26,
- defense: 3,
- experience: 45,
- credits: 32
- },
- lava_elemental: {
- name: 'Lava Elemental',
- health: 68,
- attack: 30,
- defense: 5,
- experience: 55,
- credits: 40
- },
- fire_demon: {
- name: 'Fire Demon',
- health: 72,
- attack: 32,
- defense: 6,
- experience: 62,
- credits: 45
- },
- magma_beast: {
- name: 'Magma Beast',
- health: 85,
- attack: 28,
- defense: 12,
- experience: 68,
- credits: 50
- },
- cryo_mutant: {
- name: 'Cryo Mutant',
- health: 45,
- attack: 20,
- defense: 7,
- experience: 38,
- credits: 28
- },
- frost_android: {
- name: 'Frost Android',
- health: 52,
- attack: 22,
- defense: 8,
- experience: 42,
- credits: 30
- },
- ice_wraith: {
- name: 'Ice Wraith',
- health: 58,
- attack: 25,
- defense: 4,
- experience: 48,
- credits: 35
- },
- swamp_beast: {
- name: 'Swamp Beast',
- health: 35,
- attack: 16,
- defense: 9,
- experience: 24,
- credits: 18
- },
- toxic_spitter: {
- name: 'Toxic Spitter',
- health: 28,
- attack: 19,
- defense: 3,
- experience: 22,
- credits: 16
- },
- mud_golem: {
- name: 'Mud Golem',
- health: 42,
- attack: 12,
- defense: 11,
- experience: 26,
- credits: 19
- },
-
- // NEW ENEMIES - Technology Theme
- glitch_wraith: {
- name: 'Glitch Wraith',
- health: 48,
- attack: 26,
- defense: 2,
- experience: 45,
- credits: 32
- },
- firewall_guardian: {
- name: 'Firewall Guardian',
- health: 65,
- attack: 22,
- defense: 8,
- experience: 52,
- credits: 38
- },
- data_vampire: {
- name: 'Data Vampire',
- health: 38,
- attack: 30,
- defense: 3,
- experience: 35,
- credits: 26
- },
- assembly_drone: {
- name: 'Assembly Drone',
- health: 32,
- attack: 15,
- defense: 6,
- experience: 24,
- credits: 17
- },
- welder_bot: {
- name: 'Welder Bot',
- health: 28,
- attack: 20,
- defense: 4,
- experience: 22,
- credits: 15
- },
- factory_overseer: {
- name: 'Factory Overseer',
- health: 58,
- attack: 24,
- defense: 7,
- experience: 46,
- credits: 33
- },
- quantum_phantom: {
- name: 'Quantum Phantom',
- health: 78,
- attack: 35,
- defense: 4,
- experience: 68,
- credits: 50
- },
- particle_accelerator: {
- name: 'Particle Accelerator',
- health: 92,
- attack: 40,
- defense: 6,
- experience: 85,
- credits: 62
- },
- reality_bender: {
- name: 'Reality Bender',
- health: 88,
- attack: 45,
- defense: 3,
- experience: 92,
- credits: 68
- },
- sentinel_program: {
- name: 'Sentinel Program',
- health: 42,
- attack: 21,
- defense: 8,
- experience: 32,
- credits: 24
- },
- data_corruptor: {
- name: 'Data Corruptor',
- health: 35,
- attack: 25,
- defense: 3,
- experience: 28,
- credits: 20
- },
- system_guardian: {
- name: 'System Guardian',
- health: 55,
- attack: 23,
- defense: 9,
- experience: 42,
- credits: 30
- },
-
- // NEW ENEMIES - Biome/Elemental Theme
- shard_elemental: {
- name: 'Shard Elemental',
- health: 45,
- attack: 19,
- defense: 10,
- experience: 38,
- credits: 28
- },
- resonance_beast: {
- name: 'Resonance Beast',
- health: 52,
- attack: 22,
- defense: 6,
- experience: 42,
- credits: 30
- },
- mutant_horror: {
- name: 'Mutant Horror',
- health: 68,
- attack: 28,
- defense: 5,
- experience: 58,
- credits: 42
- },
- toxic_slime: {
- name: 'Toxic Slime',
- health: 42,
- attack: 18,
- defense: 8,
- experience: 32,
- credits: 24
- },
- radiation_beast: {
- name: 'Radiation Beast',
- health: 75,
- attack: 30,
- defense: 4,
- experience: 65,
- credits: 48
- },
- shadow_demon: {
- name: 'Shadow Demon',
- health: 72,
- attack: 34,
- defense: 3,
- experience: 68,
- credits: 50
- },
- nightmare_stalker: {
- name: 'Nightmare Stalker',
- health: 85,
- attack: 38,
- defense: 5,
- experience: 78,
- credits: 58
- },
- void_walker: {
- name: 'Void Walker',
- health: 92,
- attack: 42,
- defense: 7,
- experience: 88,
- credits: 65
- },
- temporal_paradox: {
- name: 'Temporal Paradox',
- health: 78,
- attack: 40,
- defense: 4,
- experience: 75,
- credits: 55
- },
- future_soldier: {
- name: 'Future Soldier',
- health: 65,
- attack: 32,
- defense: 9,
- experience: 58,
- credits: 42
- },
- past_guardian: {
- name: 'Past Guardian',
- health: 70,
- attack: 28,
- defense: 12,
- experience: 62,
- credits: 45
- },
-
- // NEW ENEMIES - Military/War Theme
- enemy_soldier: {
- name: 'Enemy Soldier',
- health: 45,
- attack: 20,
- defense: 7,
- experience: 35,
- credits: 26
- },
- combat_drone: {
- name: 'Combat Drone',
- health: 38,
- attack: 22,
- defense: 5,
- experience: 30,
- credits: 22
- },
- field_commander: {
- name: 'Field Commander',
- health: 62,
- attack: 28,
- defense: 9,
- experience: 52,
- credits: 38
- },
- turret_system: {
- name: 'Turret System',
- health: 48,
- attack: 26,
- defense: 10,
- experience: 38,
- credits: 28
- },
- combat_android: {
- name: 'Combat Android',
- health: 55,
- attack: 24,
- defense: 8,
- experience: 45,
- credits: 33
- },
- base_commander: {
- name: 'Base Commander',
- health: 72,
- attack: 30,
- defense: 11,
- experience: 62,
- credits: 45
- },
- weapon_drone: {
- name: 'Weapon Drone',
- health: 42,
- attack: 28,
- defense: 4,
- experience: 38,
- credits: 28
- },
- test_subject: {
- name: 'Test Subject',
- health: 58,
- attack: 25,
- defense: 6,
- experience: 48,
- credits: 35
- },
- chief_scientist: {
- name: 'Chief Scientist',
- health: 35,
- attack: 32,
- defense: 3,
- experience: 42,
- credits: 30
- },
-
- // NEW ENEMIES - Special/Unique Theme
- nightmare_creature: {
- name: 'Nightmare Creature',
- health: 62,
- attack: 28,
- defense: 5,
- experience: 55,
- credits: 40
- },
- dream_guardian: {
- name: 'Dream Guardian',
- health: 68,
- attack: 30,
- defense: 8,
- experience: 58,
- credits: 42
- },
- subconscious_demon: {
- name: 'Subconscious Demon',
- health: 75,
- attack: 34,
- defense: 4,
- experience: 68,
- credits: 50
- },
- memory_fragment: {
- name: 'Memory Fragment',
- health: 48,
- attack: 26,
- defense: 6,
- experience: 45,
- credits: 33
- },
- forgetfulness_demon: {
- name: 'Forgetfulness Demon',
- health: 55,
- attack: 30,
- defense: 3,
- experience: 48,
- credits: 35
- },
- nostalgia_spirit: {
- name: 'Nostalgia Spirit',
- health: 52,
- attack: 24,
- defense: 9,
- experience: 42,
- credits: 30
- },
- rift_demon: {
- name: 'Rift Demon',
- health: 88,
- attack: 44,
- defense: 5,
- experience: 92,
- credits: 68
- },
- dimensional_hunter: {
- name: 'Dimensional Hunter',
- health: 95,
- attack: 48,
- defense: 8,
- experience: 105,
- credits: 78
- },
- reality_tear: {
- name: 'Reality Tear',
- health: 102,
- attack: 52,
- defense: 3,
- experience: 115,
- credits: 85
+ if (!this.game.socket) {
+ console.error('[DUNGEON SYSTEM] No socket connection available');
+ return;
}
- };
+
+ // Request dungeons from server
+ this.game.socket.emit('get_dungeons');
+
+ // Request room types from server
+ this.game.socket.emit('get_room_types');
+
+ // Request enemy templates from server
+ this.game.socket.emit('get_enemy_templates');
+
+ console.log('[DUNGEON SYSTEM] Server data requests sent via packets');
+
+ } catch (error) {
+ console.error('[DUNGEON SYSTEM] Error loading server data:', error);
+ }
+ }
+
+ /**
+ * Get all available dungeons
+ */
+ getAllDungeons() {
+ return this.serverDungeons;
+ }
+
+ /**
+ * Get dungeons by difficulty
+ */
+ getDungeonsByDifficulty(difficulty) {
+ return this.serverDungeons.filter(dungeon => dungeon.difficulty === difficulty);
+ }
+
+ /**
+ * Get specific dungeon by ID
+ */
+ getDungeon(dungeonId) {
+ return this.serverDungeons.find(dungeon => dungeon.id === dungeonId);
+ }
+
+ /**
+ * Get room type by ID
+ */
+ getRoomType(roomTypeId) {
+ return this.roomTypes[roomTypeId];
+ }
+
+ /**
+ * Get enemy template by ID
+ */
+ getEnemyTemplate(enemyId) {
+ return this.enemyTemplates[enemyId];
+ }
+
+ /**
+ * Start exploring a dungeon using Socket.IO packets
+ */
+ async startDungeon(dungeonId) {
+ try {
+ console.log(`[DUNGEON SYSTEM] Starting dungeon: ${dungeonId}`);
+
+ if (!this.game.socket) {
+ console.error('[DUNGEON SYSTEM] No socket connection available');
+ return null;
+ }
+
+ // Send packet to start dungeon
+ this.game.socket.emit('start_dungeon', {
+ dungeonId: dungeonId,
+ userId: this.game.player?.id || 'anonymous'
+ });
+
+ console.log('[DUNGEON SYSTEM] Dungeon start packet sent');
+ return true;
+
+ } catch (error) {
+ console.error('[DUNGEON SYSTEM] Error starting dungeon:', error);
+ return null;
+ }
+ }
+
+ /**
+ * Process current room encounter using Socket.IO packets
+ */
+ async processEncounter() {
+ if (!this.currentDungeon || !this.isExploring) {
+ console.warn('[DUNGEON SYSTEM] No active dungeon to process');
+ return null;
+ }
- // Statistics
- this.stats = {
- dungeonsAttempted: 0,
- dungeonsCompleted: 0,
- totalEnemiesDefeated: 0,
- bestTime: Infinity,
- totalLootEarned: 0
- };
+ try {
+ console.log(`[DUNGEON SYSTEM] Processing encounter for dungeon: ${this.currentDungeon.id}`);
+
+ if (!this.game.socket) {
+ console.error('[DUNGEON SYSTEM] No socket connection available');
+ return null;
+ }
+
+ // Send packet to process encounter
+ this.game.socket.emit('process_encounter', {
+ instanceId: this.currentDungeon.id,
+ userId: this.game.player?.id || 'anonymous'
+ });
+
+ console.log('[DUNGEON SYSTEM] Encounter process packet sent');
+ return true;
+
+ } catch (error) {
+ console.error('[DUNGEON SYSTEM] Error processing encounter:', error);
+ return null;
+ }
}
- async initialize() {
- this.generateDungeonList();
+ /**
+ * Complete current dungeon using Socket.IO packets
+ */
+ async completeDungeon() {
+ if (!this.currentDungeon || !this.isExploring) {
+ console.warn('[DUNGEON SYSTEM] No active dungeon to complete');
+ return null;
+ }
+
+ try {
+ console.log(`[DUNGEON SYSTEM] Completing dungeon: ${this.currentDungeon.id}`);
+
+ if (!this.game.socket) {
+ console.error('[DUNGEON SYSTEM] No socket connection available');
+ return null;
+ }
+
+ // Send packet to complete dungeon
+ this.game.socket.emit('complete_dungeon', {
+ instanceId: this.currentDungeon.id,
+ userId: this.game.player?.id || 'anonymous'
+ });
+
+ console.log('[DUNGEON SYSTEM] Dungeon completion packet sent');
+ return true;
+
+ } catch (error) {
+ console.error('[DUNGEON SYSTEM] Error completing dungeon:', error);
+ return null;
+ }
}
+ /**
+ * Get player's current dungeon status using Socket.IO packets
+ */
+ async getDungeonStatus() {
+ try {
+ if (!this.game.socket) {
+ console.error('[DUNGEON SYSTEM] No socket connection available');
+ return null;
+ }
+
+ // Send packet to get dungeon status
+ this.game.socket.emit('get_dungeon_status', {
+ userId: this.game.player?.id || 'anonymous'
+ });
+
+ console.log('[DUNGEON SYSTEM] Dungeon status request packet sent');
+ return true;
+
+ } catch (error) {
+ console.error('[DUNGEON SYSTEM] Error getting dungeon status:', error);
+ }
+
+ return null;
+ }
+
+ /**
+ * Generate dungeon list UI using server data
+ */
generateDungeonList() {
+ console.log('[DUNGEON SYSTEM] Generating dungeon list UI with server data...');
const dungeonListElement = document.getElementById('dungeonList');
if (!dungeonListElement) {
- console.error('Dungeon list element not found!');
+ console.error('[DUNGEON SYSTEM] Dungeon list element not found');
return;
}
- // Clear existing list
+ // Clear existing content
dungeonListElement.innerHTML = '';
- const questSystem = this.game.systems.questSystem;
- const firstStepsQuest = questSystem ? questSystem.findQuest('tutorial_complete') : null;
- const showTutorialDungeon = firstStepsQuest && firstStepsQuest.status === 'active';
+ if (!this.serverDungeons || Object.keys(this.serverDungeons).length === 0) {
+ dungeonListElement.innerHTML = '
Loading dungeons from server...
';
+ return;
+ }
- Object.entries(this.dungeonTypes).forEach(([key, dungeon]) => {
- // Skip tutorial dungeon unless First Steps quest is active
- if (key === 'tutorial' && !showTutorialDungeon) {
- return;
- }
+ // Generate HTML for each difficulty category
+ let html = '';
+
+ Object.entries(this.serverDungeons).forEach(([difficulty, dungeons]) => {
+ if (!dungeons || dungeons.length === 0) return;
- const dungeonElement = document.createElement('div');
- dungeonElement.className = 'dungeon-item';
- dungeonElement.dataset.dungeonType = key;
+ const difficultyClass = difficulty === 'tutorial' ? 'tutorial' : difficulty;
+ const difficultyTitle = difficulty === 'tutorial' ? 'Tutorial Dungeons' :
+ difficulty.charAt(0).toUpperCase() + difficulty.slice(1) + ' Dungeons';
+ const difficultyIcon = this.getDifficultyIcon(difficulty);
- // Check if tutorial dungeon is completed
- const isCompleted = key === 'tutorial' && this.game.systems.player.stats.tutorialDungeonCompleted;
- const statusClass = isCompleted ? 'completed' : '';
- const statusText = isCompleted ? 'COMPLETED' : '';
-
- dungeonElement.innerHTML = `
-
${dungeon.name}
-
${dungeon.difficulty.toUpperCase()}
- ${statusText ? `
${statusText}
` : ''}
-
${dungeon.description}
-
Rewards: ${dungeon.rewardMultiplier}x
-
Energy Cost: ${dungeon.energyCost || this.getEnergyCost(key)}
+ // Add difficulty header
+ html += `
+
`;
- dungeonElement.addEventListener('click', () => {
- if (isCompleted) {
- this.game.showNotification('Tutorial dungeon has already been completed!', 'warning', 3000);
- } else {
- this.selectDungeon(key);
- }
- });
-
- dungeonListElement.appendChild(dungeonElement);
- });
-
- }
-
- selectDungeon(type) {
-
- // Check energy cost
- const energyCost = this.getEnergyCost(type);
- const player = this.game.systems.player;
-
-
- if (!player.useEnergy(energyCost)) {
- this.game.showNotification(`Not enough energy! Need ${energyCost} energy`, 'error', 3000);
- return;
- }
-
-
- // Ensure we're on the Dungeons tab so the user can see the dungeon
- if (this.game.ui && this.game.ui.currentTab !== 'dungeons') {
- this.game.ui.switchTab('dungeons');
- }
-
- // Remove previous selection
- document.querySelectorAll('.dungeon-item').forEach(item => {
- item.classList.remove('selected');
- });
-
- // Add selection to clicked dungeon
- const selectedElement = document.querySelector(`[data-dungeon-type="${type}"]`);
- if (selectedElement) {
- selectedElement.classList.add('selected');
- }
-
-
- // Generate dungeon
- this.generateDungeon(type);
- this.displayDungeon();
-
-
- this.game.showNotification(`Entered dungeon! -${energyCost} energy`, 'info', 3000);
- }
-
- getEnergyCost(type) {
- const costs = {
- tutorial: 0,
- alien_ruins: 20,
- pirate_lair: 15,
- corrupted_vault: 25,
- asteroid_mine: 10,
- nebula_anomaly: 30
- };
- return costs[type] || 15;
- }
-
- calculateDungeonRewards(type, difficulty) {
- const dungeonTemplate = this.dungeonTypes[type];
- const baseRewards = {
- credits: 50,
- experience: 25,
- items: []
- };
-
- // Apply difficulty multiplier
- const difficultyMultipliers = {
- tutorial: 0.5,
- easy: 1.0,
- medium: 1.5,
- hard: 2.0
- };
-
- const multiplier = difficultyMultipliers[difficulty] || 1.0;
- const rewardMultiplier = dungeonTemplate.rewardMultiplier || 1.0;
-
- baseRewards.credits = Math.floor(baseRewards.credits * multiplier * rewardMultiplier);
- baseRewards.experience = Math.floor(baseRewards.experience * multiplier * rewardMultiplier);
-
- return baseRewards;
- }
-
- generateDungeon(type) {
-
- const dungeonTemplate = this.dungeonTypes[type];
-
- // Check if tutorial dungeon has already been completed
- if (type === 'tutorial' && this.game.systems.player.stats.tutorialDungeonCompleted) {
- this.game.showNotification('Tutorial dungeon has already been completed!', 'warning', 3000);
- return;
- }
-
-
- this.currentDungeon = {
- type: type,
- name: dungeonTemplate.name,
- description: dungeonTemplate.description,
- difficulty: dungeonTemplate.difficulty,
- healthType: dungeonTemplate.healthType,
- enemyTypes: dungeonTemplate.enemyTypes,
- rewardMultiplier: dungeonTemplate.rewardMultiplier,
- rooms: [],
- currentRoomIndex: 0,
- startTime: Date.now(),
- completed: false
- };
-
-
- // Generate rooms - ensure minimum of 3 rooms (entrance, at least 1 middle, boss)
- const roomCount = Math.max(3, this.game.getRandomInt(5, 8));
- this.currentDungeon.rooms = this.generateRoomLayout(roomCount, dungeonTemplate);
-
- // Set current room
- this.currentRoom = this.currentDungeon.rooms[0];
- this.dungeonProgress = 0;
-
-
- // Show dungeon view and hide list
- this.showDungeonView();
-
- }
-
- generateRoomLayout(roomCount, dungeonTemplate) {
- const rooms = [];
-
-
- // Always start with entrance
- rooms.push(this.createRoom('entrance', dungeonTemplate));
-
- // Generate middle rooms (subtract 2 for entrance and boss room)
- const middleRoomCount = roomCount - 2;
-
- for (let i = 0; i < middleRoomCount; i++) {
- const roomType = this.getRandomRoomType();
- const room = this.createRoom(roomType, dungeonTemplate);
- rooms.push(room);
- }
-
- // Always end with boss room (exit is handled by completing the boss room)
- rooms.push(this.createRoom('boss', dungeonTemplate));
-
-
- // Verify room accessibility
- for (let i = 0; i < rooms.length; i++) {
- }
-
- return rooms;
- }
-
- getRandomRoomType() {
- const weights = {
- corridor: 40,
- chamber: 30,
- treasure: 0, // Temporarily disabled
- entrance: 5,
- boss: 5,
- exit: 0
- };
-
- const totalWeight = Object.values(weights).reduce((sum, weight) => sum + weight, 0);
- let random = Math.random() * totalWeight;
-
- for (const [type, weight] of Object.entries(weights)) {
- random -= weight;
- if (random <= 0) {
- return type;
- }
- }
-
- return 'corridor';
- }
-
- createRoom(roomType, dungeonTemplate) {
- const template = this.roomTypes[roomType];
- const room = {
- type: roomType,
- name: template.name,
- enemies: [],
- rewards: null,
- explored: false,
- completed: false
- };
-
- // Generate enemies
- if (template.enemies > 0) {
- for (let i = 0; i < template.enemies; i++) {
- const enemyType = dungeonTemplate.enemyTypes[
- Math.floor(Math.random() * dungeonTemplate.enemyTypes.length)
- ];
- room.enemies.push(this.createEnemy(enemyType, template.isBoss));
- }
- }
-
- // Generate rewards
- if (template.rewards) {
- room.rewards = this.generateRoomRewards(dungeonTemplate.difficulty, template.isBoss);
- }
-
- return room;
- }
-
- createEnemy(enemyType, isBoss = false) {
- const template = this.enemyTemplates[enemyType];
- const bossMultiplier = isBoss ? 2.5 : 1.0;
-
- return {
- id: Date.now() + Math.random().toString(36).substr(2, 9),
- type: enemyType,
- name: isBoss ? `Boss ${template.name}` : template.name,
- health: Math.floor(template.health * bossMultiplier),
- maxHealth: Math.floor(template.health * bossMultiplier),
- attack: Math.floor(template.attack * bossMultiplier),
- defense: Math.floor(template.defense * bossMultiplier),
- experience: Math.floor(template.experience * bossMultiplier),
- credits: Math.floor(template.credits * bossMultiplier),
- isBoss: isBoss
- };
- }
-
- generateRoomRewards(difficulty, isBoss = false) {
- const baseRewards = this.game.systems.economy.generateRewards(difficulty, 'dungeon');
- const multiplier = isBoss ? 2.0 : 1.0;
-
- const rewards = {
- credits: Math.floor(baseRewards.credits * multiplier),
- experience: Math.floor(baseRewards.experience * multiplier),
- materials: []
- };
-
- // Add crafting materials based on chance and difficulty
- const materialChance = isBoss ? 0.9 : 0.4;
- if (Math.random() < materialChance) {
- const materialCount = isBoss ? this.game.getRandomInt(3, 6) : this.game.getRandomInt(1, 3);
-
- // Define material pools with weights for better balance
- const materialPools = {
- tutorial: [
- { id: 'iron_ore', weight: 40 },
- { id: 'copper_wire', weight: 30 },
- { id: 'herbs', weight: 20 },
- { id: 'bandages', weight: 10 }
- ],
- easy: [
- { id: 'iron_ore', weight: 30 },
- { id: 'copper_wire', weight: 25 },
- { id: 'energy_crystal', weight: 15 },
- { id: 'leather', weight: 15 },
- { id: 'herbs', weight: 10 },
- { id: 'bandages', weight: 5 }
- ],
- medium: [
- { id: 'iron_ore', weight: 25 },
- { id: 'steel_plate', weight: 20 },
- { id: 'energy_crystal', weight: 20 },
- { id: 'copper_wire', weight: 15 },
- { id: 'rare_metal', weight: 5 },
- { id: 'bandages', weight: 15 }
- ],
- hard: [
- { id: 'steel_plate', weight: 30 },
- { id: 'energy_crystal', weight: 25 },
- { id: 'rare_metal', weight: 15 },
- { id: 'battery', weight: 20 },
- { id: 'bandages', weight: 10 }
- ],
- extreme: [
- { id: 'rare_metal', weight: 30 },
- { id: 'energy_crystal', weight: 25 },
- { id: 'battery', weight: 25 },
- { id: 'advanced_components', weight: 20 }
- ]
- };
-
- const pool = materialPools[difficulty] || materialPools.easy;
-
- // Helper function to get weighted random material
- function getWeightedRandomMaterial(materialPool) {
- const totalWeight = materialPool.reduce((sum, mat) => sum + mat.weight, 0);
- let random = Math.random() * totalWeight;
+ dungeons.forEach(dungeon => {
+ const canEnter = this.canEnterDungeon(dungeon);
+ const statusClass = canEnter ? 'available' : 'locked';
+ const energyCost = dungeon.energyCost || 0;
+ const healthType = dungeon.healthType || 'player';
+ const healthIcon = healthType === 'ship' ? '🚀' : '👤';
- for (const material of materialPool) {
- random -= material.weight;
- if (random <= 0) {
- return material.id;
- }
- }
- return materialPool[0].id; // Fallback
- }
-
- for (let i = 0; i < materialCount; i++) {
- const material = getWeightedRandomMaterial(pool);
- const quantity = this.game.getRandomInt(1, isBoss ? 3 : 2);
-
- rewards.materials.push({
- id: material,
- quantity: quantity
- });
- }
- }
-
- return rewards;
- }
-
- displayDungeon() {
-
- const dungeonViewElement = document.getElementById('dungeonView');
-
- if (!dungeonViewElement) {
- const allElements = document.querySelectorAll('[id*="dungeon"]');
- return;
- }
-
- if (!this.currentDungeon) {
- return;
- }
-
- const room = this.currentRoom;
- if (room) {
- // Room exists, continue processing
- }
-
- const progress = Math.floor((this.currentDungeon.currentRoomIndex / this.currentDungeon.rooms.length) * 100);
-
-
- let content = `
-
-